using System.Collections.ObjectModel;
using ClaudeDo.Data;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace ClaudeDo.Ui.ViewModels.Modals.Settings;

public sealed partial class FilesSettingsTabViewModel : ViewModelBase
{
    private readonly IWorkerClient _worker;

    public OperationStatus RestoreOp { get; } = new();
    public OperationStatus ClaudeBinOp { get; } = new();

    // worker.config.json's claude_bin: a command name or a full path to the CLI. Saved on its own
    // (not with the modal's Save button) because the worker owns the file, not app_settings.
    [ObservableProperty] private string _claudeBin = "";
    [ObservableProperty] private string _claudeBinResolvedHint = "";

    [ObservableProperty] private string _statusMessage = "";
    [ObservableProperty] private bool _hasCustomizedPrompts;
    [ObservableProperty] private string? _viewedKindName;
    [ObservableProperty] private string _viewedContent = "";

    public string SystemPromptPath { get; } = PromptFiles.PathFor(PromptKind.System);
    public string PlanningPromptPath { get; } = PromptFiles.PathFor(PromptKind.Planning);
    public string PlanningInitialPromptPath { get; } = PromptFiles.PathFor(PromptKind.PlanningInitial);
    public string RetryPromptPath { get; } = PromptFiles.PathFor(PromptKind.Retry);
    public string DailyPrepPromptPath { get; } = PromptFiles.PathFor(PromptKind.DailyPrep);
    public string WeeklyReportPromptPath { get; } = PromptFiles.PathFor(PromptKind.WeeklyReport);

    public ObservableCollection<CustomizedPromptRowViewModel> CustomizedPrompts { get; } = new();

    public FilesSettingsTabViewModel(IWorkerClient worker)
    {
        _worker = worker;
        RefreshCustomizedPrompts();
        RestoreOp.PropertyChanged += (_, e) =>
        {
            if (e.PropertyName == nameof(OperationStatus.IsRunning)) RestoreDefaultAgentsCommand.NotifyCanExecuteChanged();
        };
    }

    public async Task LoadClaudeBinAsync()
    {
        var dto = await _worker.GetClaudeBinAsync();
        if (dto is null) return;
        ClaudeBin = dto.Value;
        ApplyResolvedHint(dto);
    }

    [RelayCommand]
    private async Task SaveClaudeBin()
    {
        StatusMessage = "";
        using var op = ClaudeBinOp.Begin(Loc.T("ops.claudeBin.saving"));
        var dto = await _worker.SetClaudeBinAsync(ClaudeBin);
        if (dto is null)
        {
            StatusMessage = Loc.T("vm.filesTab.workerOffline");
            return;
        }
        ClaudeBin = dto.Value;
        ApplyResolvedHint(dto);
    }

    private void ApplyResolvedHint(ClaudeBinDto dto) =>
        ClaudeBinResolvedHint = dto.ResolvedPath is null
            ? Loc.T("settings.files.claudeBinUnresolved")
            : Loc.T("settings.files.claudeBinResolved", dto.ResolvedPath);

    private bool CanRestoreDefaultAgents() => !RestoreOp.IsRunning;

    [RelayCommand(CanExecute = nameof(CanRestoreDefaultAgents))]
    private async Task RestoreDefaultAgents()
    {
        StatusMessage = "";
        using var op = RestoreOp.Begin(Loc.T("ops.skills.restoringDefaults"));
        try
        {
            var r = await _worker.RestoreDefaultAgentsAsync();
            if (r is null) StatusMessage = Loc.T("vm.filesTab.workerOffline");
            else if (r.Copied == 0 && r.Skipped == 0) StatusMessage = Loc.T("vm.filesTab.noneBundled");
            else if (r.Copied == 0) StatusMessage = Loc.T("vm.filesTab.allPresent");
            else StatusMessage = Loc.T("vm.filesTab.restored", r.Copied);
            await _worker.RefreshAgentsAsync();
        }
        catch (Exception ex) { StatusMessage = Loc.T("vm.filesTab.restoreFailed", ex.Message); }
    }

    [RelayCommand]
    private void ViewPrompt(string? kindName)
    {
        if (!Enum.TryParse<PromptKind>(kindName, ignoreCase: true, out var kind)) return;
        ViewedKindName = kindName;
        ViewedContent = PromptFiles.ReadOrDefault(kind);
    }

    private void RefreshCustomizedPrompts()
    {
        CustomizedPrompts.Clear();
        foreach (var kind in Enum.GetValues<PromptKind>())
        {
            if (PromptFiles.Classify(kind) != PromptFileState.Edited) continue;
            CustomizedPrompts.Add(new CustomizedPromptRowViewModel(kind, PromptFiles.DiffAgainstDefault(kind), OnResetPromptToDefault));
        }
        HasCustomizedPrompts = CustomizedPrompts.Count > 0;
    }

    private void OnResetPromptToDefault(PromptKind kind)
    {
        PromptFiles.ResetToDefault(kind);
        RefreshCustomizedPrompts();
        StatusMessage = Loc.T("vm.filesTab.resetToDefault");
    }
}

public sealed partial class CustomizedPromptRowViewModel : ObservableObject
{
    private readonly Action<PromptKind> _onReset;

    public PromptKind Kind { get; }
    public string KindName => Kind.ToString();
    public string DiffPreview { get; }

    public CustomizedPromptRowViewModel(PromptKind kind, string diffPreview, Action<PromptKind> onReset)
    {
        Kind = kind;
        DiffPreview = diffPreview;
        _onReset = onReset;
    }

    [RelayCommand]
    private void Reset() => _onReset(Kind);
}
