namespace ClawdDotNet.Core.Staging;

/// <summary>
/// Entscheidet je (Tool, Aktion), ob ein Aufruf läuft, gestaged oder abgelehnt wird.
///
/// Auflösung vom Speziellen zum Allgemeinen: <c>Tool.Aktion</c> → <c>Tool</c> → Standard.
/// Rein und ohne Zustand, damit die Entscheidung testbar bleibt.
/// </summary>
public sealed class StagingPolicy
{
    private readonly Dictionary<string, StagingDecision> _rules;
    private readonly StagingDecision _default;

    public StagingPolicy(
        IReadOnlyDictionary<string, StagingDecision>? rules = null,
        StagingDecision defaultDecision = StagingDecision.Auto)
    {
        _rules = new Dictionary<string, StagingDecision>(StringComparer.OrdinalIgnoreCase);
        foreach (var (key, value) in rules ?? DefaultRules)
            _rules[key] = value;
        _default = defaultDecision;
    }

    public StagingDecision Decide(string tool, string? action)
    {
        if (!string.IsNullOrWhiteSpace(action)
            && _rules.TryGetValue($"{tool}.{action}", out var specific))
            return specific;

        if (_rules.TryGetValue(tool, out var byTool))
            return byTool;

        return _default;
    }

    /// <summary>
    /// Eingebaute Standardregeln — genau die irreversiblen Aktionen aus der Roadmap, nach
    /// Sichtung der Tools. Lesende Aktionen bleiben bewusst außen vor (Staging soll
    /// schützen, nicht lähmen). Überschreibbar per Konfiguration.
    /// </summary>
    public static IReadOnlyDictionary<string, StagingDecision> DefaultRules { get; } =
        new Dictionary<string, StagingDecision>(StringComparer.OrdinalIgnoreCase)
        {
            ["Mail.send"] = StagingDecision.Approve,
            ["Telegram.send_message"] = StagingDecision.Approve,
            ["Database.insert"] = StagingDecision.Approve,
            ["Database.upsert"] = StagingDecision.Approve,
            ["FileRW.delete"] = StagingDecision.Approve,
            ["FTP.upload"] = StagingDecision.Approve,
            ["FTP.delete"] = StagingDecision.Approve,

            // Rocket.Chat ist der Arbeitsraum, nicht ein Kanal nach draußen: Müsste jede
            // Antwort freigegeben werden, wäre er unbenutzbar. Der Schutz sitzt dort an
            // der Raum-Allowlist des Tools, deshalb bleibt send_message bewusst `auto`.
            // Eine Datei ist etwas anderes — sie verlässt den Workspace als Ganzes.
            ["RocketChat.send_file"] = StagingDecision.Approve
        };
}
