using System.ComponentModel;
using ClawdDotNet.Core.Config;

namespace ClawdDotNet.Models;

/// <summary>Bietet die drei gültigen Prompt-Caching-Werte als Auswahlliste an.</summary>
public sealed class PromptCachingConverter : StringConverter
{
    public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true;
    public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => true;

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context)
        => new(new[] { "auto", "on", "off" });
}

[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class AgentSettingsViewModel
{
    private readonly AgentConfig _config;

    public AgentSettingsViewModel(AgentConfig config)
    {
        _config = config;
    }

    // ──────────────── Identity ────────────────

    [Category("1 - Identity")]
    [DisplayName("Agent-ID")]
    [Description("Eindeutige ID des Agenten. Wird intern und in Logs verwendet.")]
    [ReadOnly(true)]
    public string AgentId => _config.AgentId;

    [Category("1 - Identity")]
    [DisplayName("Anzeigename")]
    [Description("Freundlicher Name des Agenten (z.B. 'Marktanalyst').")]
    public string DisplayName
    {
        get => _config.DisplayName;
        set => _config.DisplayName = value;
    }

    [Category("1 - Identity")]
    [DisplayName("Modell")]
    [Description("LLM-Modell das dieser Agent verwendet. Dropdown zeigt verfügbare Modelle von OpenRouter.")]
    [TypeConverter(typeof(ModelTypeConverter))]
    public string Model
    {
        get => _config.Model;
        set => _config.Model = value;
    }

    [Category("1 - Identity")]
    [DisplayName("Identity")]
    [Description("Aus Identity.md geladen – definiert WER der Agent ist. Bearbeitung über den Toolbar-Button 'Identity bearbeiten'.")]
    [ReadOnly(true)]
    public string IdentityStatus =>
        string.IsNullOrWhiteSpace(_config.Identity) ? "(nicht definiert)" : $"✔ {_config.Identity.Split('\n').Length} Zeilen";

    [Category("1 - Identity")]
    [DisplayName("Soul")]
    [Description("Aus Soul.md geladen – definiert WIE der Agent denkt. Bearbeitung über den Toolbar-Button 'Soul bearbeiten'.")]
    [ReadOnly(true)]
    public string SoulStatus =>
        string.IsNullOrWhiteSpace(_config.Soul) ? "(nicht definiert)" : $"✔ {_config.Soul.Split('\n').Length} Zeilen";

    // ──────────────── Loop-Schutz ────────────────

    [Category("2 - Loop-Schutz")]
    [DisplayName("Max. Schritte pro Run")]
    [Description("Maximale Anzahl LLM-Aufrufe pro Run. Verhindert Endlosschleifen bei fehlerhaften Tool-Calls.")]
    public int MaxSteps
    {
        get => _config.LoopGuard.MaxSteps;
        set => _config.LoopGuard.MaxSteps = Math.Max(1, value);
    }

    [Category("2 - Loop-Schutz")]
    [DisplayName("Kostenbudget pro Run (Tokens)")]
    [Description("Summe ALLER abgerechneten Tokens eines Runs über alle Schritte. " +
                 "Da jeder Schritt den kompletten Kontext erneut sendet, wächst diese Summe " +
                 "überproportional — der Wert liegt deutlich über der Kontextgröße. " +
                 "Für die Kontextgröße ist 'Max. Kontext-Tokens' zuständig.")]
    public int MaxCumulativeTokens
    {
        get => _config.LoopGuard.MaxCumulativeTokens;
        set => _config.LoopGuard.MaxCumulativeTokens = Math.Max(10_000, value);
    }

    [Category("2 - Loop-Schutz")]
    [DisplayName("Timeout (Sekunden)")]
    [Description("Maximale Laufzeit pro Run in Sekunden. Danach wird der Run abgebrochen.")]
    public int TimeoutSeconds
    {
        get => _config.LoopGuard.TimeoutSeconds;
        set => _config.LoopGuard.TimeoutSeconds = Math.Max(10, value);
    }

    // ──────────────── Kontext-Management ────────────────

    [Category("3 - Kontext-Management")]
    [DisplayName("Max. Kontext-Tokens")]
    [Description("Maximales Token-Budget für den gesamten Konversationskontext. Bei Überschreitung wird automatisch kompaktiert.")]
    public int MaxContextTokens
    {
        get => _config.LoopGuard.MaxContextTokens;
        set => _config.LoopGuard.MaxContextTokens = Math.Max(10_000, value);
    }

    [Category("3 - Kontext-Management")]
    [DisplayName("Kompaktierungs-Schwelle (%)")]
    [Description("Ab welchem Prozentsatz der Max. Kontext-Tokens wird kompaktiert. 80 = bei 80% Auslastung. Stufe 1: Tool-Results kürzen. Stufe 2: LLM-Zusammenfassung.")]
    public int CompactionThresholdPercent
    {
        get => (int)(_config.LoopGuard.CompactionThreshold * 100);
        set => _config.LoopGuard.CompactionThreshold = Math.Clamp(value, 50, 95) / 100.0;
    }

    [Category("3 - Kontext-Management")]
    [DisplayName("Modell für Zusammenfassungen")]
    [Description("Modell, mit dem beim Kompaktieren zusammengefasst wird. Leer = Modell des Agenten. " +
                 "Zusammenfassen ist anspruchslos — ein günstiges Modell spart hier deutlich, " +
                 "da bis zu 30.000 Zeichen verarbeitet werden.")]
    public string SummaryModel
    {
        get => _config.LoopGuard.SummaryModel;
        set => _config.LoopGuard.SummaryModel = value ?? "";
    }

    [Category("3 - Kontext-Management")]
    [DisplayName("Max. Zeichen pro Tool-Ergebnis")]
    [Description("Längere Tool-Ergebnisse werden gekürzt, bevor sie in den Kontext gelangen. " +
                 "Ohne Grenze kann ein einzelner Abruf den Kontext sprengen — 512 KB entsprechen " +
                 "etwa 130.000 Tokens in einer einzigen Antwort.")]
    public int MaxToolResultChars
    {
        get => _config.MaxToolResultChars;
        set => _config.MaxToolResultChars = Math.Max(1_000, value);
    }

    // ──────────────── Kosten ────────────────

    [Category("5 - Kosten")]
    [DisplayName("Prompt-Caching")]
    [Description("auto = für Modelle aktivieren, die es unterstützen; on = erzwingen; off = aus. " +
                 "Spart erheblich, weil jeder Schritt eines Runs den kompletten Prompt erneut sendet — " +
                 "System-Prompt und Tool-Definitionen also dutzendfach.")]
    [TypeConverter(typeof(PromptCachingConverter))]
    public string PromptCaching
    {
        get => _config.PromptCaching;
        set => _config.PromptCaching = string.IsNullOrWhiteSpace(value) ? "auto" : value;
    }

    [Category("5 - Kosten")]
    [DisplayName("Tagesbudget (USD)")]
    [Description("Hartes Tageslimit für diesen Agenten in US-Dollar. 0 = kein Limit. " +
                 "Bei Überschreitung wird kein Lauf mehr gestartet. " +
                 "Greift nur, wenn für das Modell Preise vorliegen — sonst zusätzlich " +
                 "das Token-Tageslimit setzen.")]
    public decimal DailyBudgetUsd
    {
        get => _config.Budget.DailyCostUsd;
        set => _config.Budget.DailyCostUsd = Math.Max(0, value);
    }

    [Category("5 - Kosten")]
    [DisplayName("Token-Tageslimit")]
    [Description("Hartes Tageslimit für diesen Agenten in Tokens. 0 = kein Limit. " +
                 "Wirkt auch dann, wenn für das Modell keine Preise vorliegen.")]
    public long DailyTokenLimit
    {
        get => _config.Budget.DailyTokens;
        set => _config.Budget.DailyTokens = Math.Max(0, value);
    }

    // ──────────────── Tools (Read-Only) ────────────────

    [Category("4 - Tools")]
    [DisplayName("Zugewiesene Tools")]
    [Description("Liste der Tool-Namen, die diesem Agent zugewiesen sind. Zuweisung über die Tabelle unten.")]
    [ReadOnly(true)]
    public string AssignedTools =>
        _config.Tools.Count == 0
            ? "(keine)"
            : string.Join(", ", _config.Tools.Keys);

    [Category("4 - Tools")]
    [DisplayName("Anzahl")]
    [ReadOnly(true)]
    public int ToolCount => _config.Tools.Count;

    // ──────────────── Intern ────────────────

    [Browsable(false)]
    public AgentConfig UnderlyingConfig => _config;

    public override string ToString() =>
        string.IsNullOrWhiteSpace(_config.DisplayName) ? _config.AgentId : _config.DisplayName;
}
