using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text.Json;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;

namespace ClaudeDo.Ui.ViewModels.Agent;

public enum AgentConfigScope { List, Task }

/// <summary>
/// One agent-config editor (Model / MaxTurns / SystemPrompt / AgentFile with inherited
/// badges + reset) shared by the List Settings modal and the per-task gear flyout.
/// Scope picks the inheritance depth (List: list→global; Task: task→list→global) and the
/// persistence (List: explicit <see cref="SaveAsync"/>; Task: debounced auto-save).
/// </summary>
public sealed partial class AgentConfigEditorViewModel : ViewModelBase, IDisposable
{
    private readonly IWorkerClient _worker;
    private readonly AgentConfigScope _scope;
    private readonly EventHandler _langChangedHandler;

    /// scope==List ⇒ the list id; scope==Task ⇒ the task id. Null ⇒ no save target.
    internal string? TargetId { get; set; }

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(IsEnabled))]
    private bool _isRunning;

    // Task scope gates the editor while the run is live; List scope is always enabled.
    public bool IsEnabled => !IsRunning;

    [ObservableProperty] private string? _model;
    [ObservableProperty] private decimal? _maxTurns;
    [ObservableProperty] private string _systemPrompt = "";
    [ObservableProperty] private AgentInfo? _selectedAgent;
    // Null = inherit (list → global). Restricted to PermissionModeRegistry.Modes by the ComboBox.
    [ObservableProperty] private string? _permissionMode;

    [ObservableProperty] private string _modelBadge = "";
    [ObservableProperty] private string _modelInheritedHint = "";
    [ObservableProperty] private string _turnsBadge = "";
    [ObservableProperty] private string _turnsInheritedHint = "";
    [ObservableProperty] private string _turnsCeilingHint = "";
    [ObservableProperty] private string _agentBadge = "";
    [ObservableProperty] private string _permissionBadge = "";
    [ObservableProperty] private string _permissionInheritedHint = "";
    [ObservableProperty] private string _effectiveSystemPromptHint = "";

    private string _globalModel = ModelRegistry.DefaultAlias;
    // The global max-turns default is per-model (Settings -> General), so it moves with whichever
    // model actually ends up in effect here.
    private IReadOnlyList<ModelPreset> _presets = ModelPresets.Defaults;
    private int _maxTurnsCeiling = 80;
    private string EffectiveModel => Model ?? _listModel ?? _globalModel;
    private int GlobalMaxTurns => ModelPresets.For(_presets, EffectiveModel).MaxTurns;
    private string _globalPermissionMode = PermissionModeRegistry.DefaultMode;
    private string? _listModel;          // Task scope only
    private int? _listMaxTurns;          // Task scope only
    private string? _listAgentName;      // Task scope only
    private string? _listPermissionMode; // Task scope only

    private bool _suppressSave;
    private CancellationTokenSource? _saveCts;

    public int EffectiveMaxTurns =>
        MaxTurns is decimal t ? (int)t : (_listMaxTurns ?? GlobalMaxTurns);

    public ObservableCollection<string> ModelOptions { get; } = new(ModelRegistry.Aliases);
    public IReadOnlyList<string> PermissionModeOptions { get; } = PermissionModeRegistry.Modes;
    public ObservableCollection<AgentInfo> Agents { get; } = new();
    public ObservableCollection<SelectableSkillViewModel> SessionSkills { get; } = new();

    public AgentConfigEditorViewModel(IWorkerClient worker, AgentConfigScope scope)
    {
        _worker = worker;
        _scope = scope;
        _langChangedHandler = (_, _) => RecomputeBadges();
        // Only the long-lived Task editor needs live re-badging; the List editor is a
        // short-lived modal recreated with the current language on each open.
        if (scope == AgentConfigScope.Task)
            Loc.LanguageChanged += _langChangedHandler;

        SessionSkills.CollectionChanged += OnSessionSkillsCollectionChanged;
    }

    private void OnSessionSkillsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems is not null)
            foreach (SelectableSkillViewModel item in e.NewItems)
                item.PropertyChanged += OnSkillItemPropertyChanged;
        if (e.OldItems is not null)
            foreach (SelectableSkillViewModel item in e.OldItems)
                item.PropertyChanged -= OnSkillItemPropertyChanged;
    }

    private void OnSkillItemPropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(SelectableSkillViewModel.IsSelected)) QueueSave();
    }

    public void Dispose()
    {
        Loc.LanguageChanged -= _langChangedHandler;
        SessionSkills.CollectionChanged -= OnSessionSkillsCollectionChanged;
        foreach (var s in SessionSkills) s.PropertyChanged -= OnSkillItemPropertyChanged;
    }

    partial void OnModelChanged(string? value)
    {
        RecomputeModelBadge();
        // A different model means a different global turn default.
        RecomputeTurnsBadge();
        OnPropertyChanged(nameof(EffectiveMaxTurns));
        QueueSave();
    }

    partial void OnMaxTurnsChanged(decimal? value)
    {
        RecomputeTurnsBadge();
        OnPropertyChanged(nameof(EffectiveMaxTurns));
        QueueSave();
    }

    partial void OnSystemPromptChanged(string value) => QueueSave();
    partial void OnSelectedAgentChanged(AgentInfo? value) { RecomputeAgentBadge(); QueueSave(); }
    partial void OnPermissionModeChanged(string? value) { RecomputePermissionBadge(); QueueSave(); }

    private void RecomputeBadges()
    {
        RecomputeModelBadge();
        RecomputeTurnsBadge();
        RecomputeAgentBadge();
        RecomputePermissionBadge();
    }

    private void RecomputeModelBadge()
    {
        var own = string.IsNullOrWhiteSpace(Model) ? null : Model;
        var (value, source) = _scope == AgentConfigScope.Task
            ? InheritanceResolver.Resolve(own, _listModel, _globalModel)
            : InheritanceResolver.ResolveList(own, _globalModel);
        ModelInheritedHint = value;
        ModelBadge = BadgeFor(source, own is not null);
    }

    private void RecomputeTurnsBadge()
    {
        var own = MaxTurns?.ToString();
        var (value, source) = _scope == AgentConfigScope.Task
            ? InheritanceResolver.Resolve(own, _listMaxTurns?.ToString(), GlobalMaxTurns.ToString())
            : InheritanceResolver.ResolveList(own, GlobalMaxTurns.ToString());
        TurnsInheritedHint = value;
        TurnsBadge = BadgeFor(source, MaxTurns is not null);
        TurnsCeilingHint = MaxTurns is decimal t && (int)t > _maxTurnsCeiling
            ? Loc.T("settings.turnsCeilingHint", _maxTurnsCeiling)
            : "";
    }

    private void RecomputeAgentBadge()
    {
        var agentSet = SelectedAgent is not null && !string.IsNullOrWhiteSpace(SelectedAgent.Path);
        var own = agentSet ? SelectedAgent!.Path : null;
        var (_, source) = _scope == AgentConfigScope.Task
            ? InheritanceResolver.Resolve(own, _listAgentName, null)
            : InheritanceResolver.ResolveList(own, null);
        AgentBadge = BadgeFor(source, agentSet);
    }

    private void RecomputePermissionBadge()
    {
        var own = string.IsNullOrWhiteSpace(PermissionMode) ? null : PermissionMode;
        var (value, source) = _scope == AgentConfigScope.Task
            ? InheritanceResolver.Resolve(own, _listPermissionMode, _globalPermissionMode)
            : InheritanceResolver.ResolveList(own, _globalPermissionMode);
        PermissionInheritedHint = value;
        PermissionBadge = BadgeFor(source, own is not null);
    }

    private static string BadgeFor(InheritSource source, bool isSet) => isSet
        ? Loc.T("settings.inherit.overrideBadge")
        : source == InheritSource.List
            ? Loc.T("settings.inherit.inheritedFromList")
            : Loc.T("settings.inherit.inheritedFromGlobal");

    private void QueueSave()
    {
        // List scope persists on the modal Save button; only Task auto-saves.
        if (_suppressSave || _scope != AgentConfigScope.Task || TargetId is null) return;
        _saveCts?.Cancel();
        _saveCts = new CancellationTokenSource();
        _ = DebouncedSaveAsync(_saveCts.Token);
    }

    private async System.Threading.Tasks.Task DebouncedSaveAsync(CancellationToken ct)
    {
        try
        {
            await System.Threading.Tasks.Task.Delay(300, ct);
            if (TargetId is null) return;
            await SaveAsync();
        }
        catch (OperationCanceledException) { }
        catch { }
    }

    // verifyCommand and ticketProjectId are List-only fields owned by ListSettingsModalViewModel
    // (not this editor, which is also reused for Task scope); the caller passes them through so the
    // single UpdateListConfig call carries the full desired row instead of clobbering it.
    public async System.Threading.Tasks.Task SaveAsync(
        string? verifyCommand = null, int? ticketProjectId = null)
    {
        if (TargetId is null) return;
        var model = string.IsNullOrWhiteSpace(Model) ? null : Model;
        var sp = string.IsNullOrWhiteSpace(SystemPrompt) ? null : SystemPrompt;
        var ap = SelectedAgent is null || string.IsNullOrWhiteSpace(SelectedAgent.Path) ? null : SelectedAgent.Path;
        var turns = MaxTurns is decimal d ? (int?)d : null;
        var skills = SelectedSessionSkillNames();
        var permission = string.IsNullOrWhiteSpace(PermissionMode) ? null : PermissionMode;

        if (_scope == AgentConfigScope.Task)
            await _worker.UpdateTaskAgentSettingsAsync(new UpdateTaskAgentSettingsDto(TargetId, model, sp, ap, turns, skills, permission));
        else
            await _worker.UpdateListConfigAsync(new UpdateListConfigDto(TargetId, model, sp, ap, turns, skills, verifyCommand, permission, ticketProjectId));
    }

    private List<string>? SelectedSessionSkillNames()
    {
        var names = SessionSkills.Where(s => s.IsSelected).Select(s => s.Name).ToList();
        return names.Count == 0 ? null : names;
    }

    public async System.Threading.Tasks.Task LoadForListAsync(string listId, CancellationToken ct = default)
    {
        _suppressSave = true;
        try
        {
            TargetId = listId;
            await ReloadAgentsAsync("(none)");
            await LoadGlobalDefaultsAsync();

            var cfg = await _worker.GetListConfigAsync(listId);
            ApplyConfig(cfg?.Model, cfg?.MaxTurns, cfg?.SystemPrompt, cfg?.AgentPath, cfg?.PermissionMode);
            await ReloadSessionSkillsAsync(cfg?.SessionSkills);

            _listModel = null; _listMaxTurns = null; _listAgentName = null; _listPermissionMode = null;
            EffectiveSystemPromptHint = "";
            RecomputeBadges();
            OnPropertyChanged(nameof(EffectiveMaxTurns));
        }
        finally { _suppressSave = false; }
    }

    public async System.Threading.Tasks.Task LoadForTaskAsync(TaskEntity entity, CancellationToken ct = default)
    {
        _suppressSave = true;
        try
        {
            TargetId = entity.Id;
            await ReloadAgentsAsync("(inherited)");
            ApplyConfig(entity.Model, entity.MaxTurns, entity.SystemPrompt, entity.AgentPath, entity.PermissionMode);
            await ReloadSessionSkillsAsync(ParseSessionSkills(entity.SessionSkills));

            var listCfg = await _worker.GetListConfigAsync(entity.ListId);
            await LoadGlobalDefaultsAsync();
            _listModel = listCfg?.Model;
            _listMaxTurns = listCfg?.MaxTurns;
            _listPermissionMode = listCfg?.PermissionMode;
            _listAgentName = string.IsNullOrWhiteSpace(listCfg?.AgentPath)
                ? null : System.IO.Path.GetFileName(listCfg!.AgentPath!);
            EffectiveSystemPromptHint = string.IsNullOrWhiteSpace(listCfg?.SystemPrompt)
                ? "" : listCfg!.SystemPrompt!;

            RecomputeBadges();
            OnPropertyChanged(nameof(EffectiveMaxTurns));
        }
        finally { _suppressSave = false; }
    }

    public void Clear()
    {
        _suppressSave = true;
        try
        {
            Model = null;
            MaxTurns = null;
            SystemPrompt = "";
            SelectedAgent = null;
            PermissionMode = null;
            foreach (var s in SessionSkills) s.IsSelected = false;
        }
        finally { _suppressSave = false; }
        EffectiveSystemPromptHint = "";
        TargetId = null;
    }

    private static List<string>? ParseSessionSkills(string? json)
    {
        if (string.IsNullOrWhiteSpace(json)) return null;
        try { return JsonSerializer.Deserialize<List<string>>(json); }
        catch (JsonException) { return null; }
    }

    private async System.Threading.Tasks.Task ReloadSessionSkillsAsync(IReadOnlyCollection<string>? selected)
    {
        var installed = await _worker.GetSessionSkillsAsync();
        var selectedSet = selected is null ? new HashSet<string>() : new HashSet<string>(selected);
        foreach (var s in SessionSkills) s.PropertyChanged -= OnSkillItemPropertyChanged;
        SessionSkills.Clear();
        foreach (var s in installed)
            SessionSkills.Add(new SelectableSkillViewModel(s.Name, s.Description, selectedSet.Contains(s.Name)));
    }

    private async System.Threading.Tasks.Task ReloadAgentsAsync(string placeholderName)
    {
        Agents.Clear();
        Agents.Add(new AgentInfo(placeholderName, "", ""));
        foreach (var a in await _worker.GetAgentsAsync()) Agents.Add(a);
    }

    private async System.Threading.Tasks.Task LoadGlobalDefaultsAsync()
    {
        var app = await _worker.GetAppSettingsAsync();
        _globalModel = app?.DefaultModel ?? ModelRegistry.DefaultAlias;
        _presets = app?.ModelPresets is { Count: > 0 } rows
            ? rows.Select(r => new ModelPreset(r.Model, r.Effort, r.MaxTurns)).ToList()
            : ModelPresets.Defaults;
        _maxTurnsCeiling = app?.MaxTurnsCeiling ?? 80;
        _globalPermissionMode = app?.DefaultPermissionMode ?? PermissionModeRegistry.DefaultMode;
    }

    private void ApplyConfig(string? model, int? maxTurns, string? systemPrompt, string? agentPath,
                             string? permissionMode)
    {
        Model = string.IsNullOrWhiteSpace(model) ? null : model!;
        MaxTurns = maxTurns is int mt ? mt : (decimal?)null;
        SystemPrompt = systemPrompt ?? "";
        PermissionMode = string.IsNullOrWhiteSpace(permissionMode) ? null : permissionMode!;
        SelectedAgent = string.IsNullOrWhiteSpace(agentPath)
            ? Agents[0]
            : (Agents.FirstOrDefault(a => a.Path == agentPath) ?? Agents[0]);
    }

    [RelayCommand] private void ResetModel() => Model = null;
    [RelayCommand] private void ResetTurns() => MaxTurns = null;
    [RelayCommand] private void ResetAgent() => SelectedAgent = Agents.Count > 0 ? Agents[0] : null;
    [RelayCommand] private void ResetPermissionMode() => PermissionMode = null;

    [RelayCommand]
    private void ResetAll()
    {
        Model = null;
        MaxTurns = null;
        SystemPrompt = "";
        SelectedAgent = Agents.Count > 0 ? Agents[0] : null;
        PermissionMode = null;
        foreach (var s in SessionSkills) s.IsSelected = false;
    }
}
