namespace ClawdDotNet.Core.Staging;

/// <summary>Was mit einem Tool-Aufruf geschehen soll — die Policy-Entscheidung.</summary>
public enum StagingDecision
{
    /// <summary>Ausführen wie bisher.</summary>
    Auto,

    /// <summary>Stagen und auf menschliche Freigabe warten.</summary>
    Approve,

    /// <summary>Gar nicht erst vorschlagen — ablehnen.</summary>
    Deny
}

/// <summary>Lebenszyklus eines eingefrorenen Aufrufs.</summary>
public enum StagingStatus
{
    /// <summary>Vorgeschlagen, wartet auf Entscheidung.</summary>
    Pending,

    /// <summary>Freigegeben und beansprucht (wird ausgeführt).</summary>
    Approved,

    /// <summary>Freigegeben und ausgeführt.</summary>
    Executed,

    /// <summary>Freigegeben, aber die Ausführung schlug fehl.</summary>
    Failed,

    /// <summary>Abgelehnt.</summary>
    Rejected
}

/// <summary>
/// Ein eingefrorener, konkreter Tool-Aufruf, der auf eine Freigabe wartet. „Eingefroren"
/// heißt: Tool, Aktion und die <b>exakten</b> Argumente zum Zeitpunkt des Vorschlags.
/// Ausgeführt wird genau das (Plan-Freeze) — nie eine nachträglich veränderte Fassung.
/// </summary>
public sealed record StagedCall
{
    public long Id { get; init; }

    /// <summary>Lauf, aus dem der Vorschlag stammt — verbindet ihn mit dem Audit-Log.</summary>
    public string RunId { get; init; } = "";

    public string AgentId { get; init; } = "";
    public string InstanceId { get; init; } = "";

    public string Tool { get; init; } = "";
    public string? Action { get; init; }

    /// <summary>Die eingefrorenen Argumente (roher JSON, exakt wie vom Modell geschickt).</summary>
    public string ArgumentsJson { get; init; } = "";

    /// <summary>Kurze, menschenlesbare Zusammenfassung des Vorschlags.</summary>
    public string Proposal { get; init; } = "";

    public StagingStatus Status { get; init; } = StagingStatus.Pending;

    public DateTime CreatedAt { get; init; }
    public DateTime? DecidedAt { get; init; }
    public string? DecidedBy { get; init; }

    /// <summary>Nach der Ausführung: kurzer Verweis auf das Ergebnis.</summary>
    public string? ResultRef { get; init; }

    /// <summary>Bei Ablehnung: der Grund.</summary>
    public string? RejectionReason { get; init; }
}

public interface IStagingRepository
{
    /// <summary>Legt einen Vorschlag an und gibt seine Id zurück.</summary>
    Task<long> AppendAsync(StagedCall call, CancellationToken ct);

    Task<StagedCall?> GetAsync(long id, CancellationToken ct);

    /// <summary>Die offenen Vorschläge (Pending), älteste zuerst — für die Review-Ansicht.</summary>
    Task<IReadOnlyList<StagedCall>> ListPendingAsync(CancellationToken ct);

    /// <summary>
    /// Atomarer, bedingter Statuswechsel: nur wirksam, wenn der Vorschlag noch im Status
    /// <paramref name="from"/> steht. Verhindert, dass zwei Reviewer denselben Vorschlag
    /// doppelt entscheiden. Gibt zurück, ob der Wechsel gelang.
    /// </summary>
    Task<bool> TryTransitionAsync(
        long id, StagingStatus from, StagingStatus to,
        string? decidedBy, string? rejectionReason, DateTime now, CancellationToken ct);

    /// <summary>Setzt Endstatus und Ergebnis-Verweis nach der Ausführung (zweite Phase).</summary>
    Task FinalizeAsync(long id, StagingStatus status, string? resultRef, DateTime now, CancellationToken ct);

    Task<int> CountPendingAsync(CancellationToken ct);
}
