namespace ClawdDotNet.Core.Audit;

/// <summary>Ausgang eines Tool-Aufrufs, von der Engine festgestellt.</summary>
public enum AuditStatus
{
    /// <summary>Tool lief und lieferte ein Ergebnis.</summary>
    Ok,

    /// <summary>Tool meldete einen Fehler oder warf eine Ausnahme.</summary>
    Error,

    /// <summary>Das <see cref="Security.PermissionGate"/> hat den Aufruf abgelehnt.</summary>
    Denied,

    /// <summary>Das angeforderte Tool ist dem Agenten nicht zugewiesen/unbekannt.</summary>
    NotFound,

    /// <summary>Der Aufruf wurde zur Freigabe vorgelegt (A2), nicht ausgeführt.</summary>
    Staged
}

/// <summary>
/// Ein Eintrag im Audit-Log: ein einzelner Tool-Aufruf, wie die Engine ihn gesehen hat.
///
/// Die Herkunft wird von der <b>Engine gestempelt</b>, nie vom Agenten behauptet:
/// <see cref="AgentId"/>, <see cref="Model"/> und <see cref="Source"/> stammen aus dem
/// Wissen der Engine über den Lauf, nicht aus dem Tool-Ergebnis. Einträge sind
/// unveränderlich — eine Korrektur ist ein neuer Eintrag, kein Überschreiben.
/// </summary>
public sealed record AuditEntry
{
    public long Id { get; init; }

    /// <summary>Korrelations-Id des Laufs — bündelt alle Aufrufe eines Laufs.</summary>
    public string RunId { get; init; } = "";

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

    /// <summary>Worker-Typ: das Modell/die Engine. Getrennt von der Session (Source).</summary>
    public string Model { get; init; } = "";

    /// <summary>Verantwortliche Session/Kanal (webview, telegram, task …). <c>unknown</c>,
    /// wenn nicht bekannt — geraten wird nichts.</summary>
    public string Source { get; init; } = AuditSource.Unknown;

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

    /// <summary>Übergebene Argumente (gekappt). Rohdaten, wie das Modell sie schickte.</summary>
    public string Arguments { get; init; } = "";

    public AuditStatus Status { get; init; }

    /// <summary>Kurze Notiz zum Ausgang (Fehlermeldung, knapper Hinweis).</summary>
    public string Summary { get; init; } = "";

    public long DurationMs { get; init; }

    public DateTime OccurredAt { get; init; }
}

/// <summary>
/// Abschluss-Beleg eines Laufs (Receipt): das Ergebnis mit Schritten, Tokens und Kosten.
/// Verknüpft <c>RunUsage</c> mit einem Task und macht so C7 („Kosten pro Ergebnis")
/// weitgehend zum Abfallprodukt.
/// </summary>
public sealed record RunReceipt
{
    public long Id { get; init; }
    public string RunId { get; init; } = "";
    public string AgentId { get; init; } = "";
    public string Model { get; init; } = "";
    public string Source { get; init; } = AuditSource.Unknown;

    /// <summary>Verknüpfter Task, falls der Lauf aus dem Taskboard kam — sonst <c>null</c>.</summary>
    public string? TaskId { get; init; }

    /// <summary>Endzustand (aus <c>AgentRunStatus</c>).</summary>
    public string Status { get; init; } = "";

    public int StepCount { get; init; }
    public int PromptTokens { get; init; }
    public int CompletionTokens { get; init; }
    public int CachedTokens { get; init; }

    public decimal CostUsd { get; init; }
    public bool CostIsKnown { get; init; }

    public long DurationMs { get; init; }

    /// <summary>Kurzer Verweis auf das Ergebnis (gekappte Schlussnachricht).</summary>
    public string ResultRef { get; init; } = "";

    public DateTime OccurredAt { get; init; }
}

/// <summary>Bekannte Session-/Kanal-Bezeichner für die Herkunft. Deckt sich mit
/// <c>ChatSource</c>; <see cref="Unknown"/> steht für „nicht bekannt", nicht für geraten.</summary>
public static class AuditSource
{
    public const string Unknown = "unknown";

    /// <summary>Ein direkter, quellenloser Lauf (z. B. RunAsync ohne Kanal).</summary>
    public const string Direct = "direct";

    /// <summary>Ausführung eines freigegebenen, eingefrorenen Aufrufs (A2).</summary>
    public const string Approval = "approval";

    public static string Normalize(string? source)
        => string.IsNullOrWhiteSpace(source) ? Unknown : source.Trim();
}
