using System.Collections.ObjectModel;
using System.Diagnostics;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input.Platform;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Ui.ViewModels.Modals;

public enum BatchMergeOutcome { None, Merging, Merged, Conflict, Blocked, Failed }

public sealed partial class WorktreeOverviewRowViewModel : ViewModelBase
{
    [ObservableProperty] private string _taskId = "";
    [ObservableProperty] private string _taskTitle = "";
    [ObservableProperty][NotifyPropertyChangedFor(nameof(IsRunning))] private TaskStatus _taskStatus;
    [ObservableProperty] private string _listId = "";
    [ObservableProperty] private string _listName = "";
    [ObservableProperty] private string _path = "";
    [ObservableProperty] private string _branchName = "";
    [ObservableProperty] private string _baseCommit = "";
    [ObservableProperty][NotifyPropertyChangedFor(nameof(IsActive))] private WorktreeState _state;
    [ObservableProperty] private string? _diffStat;
    [ObservableProperty][NotifyPropertyChangedFor(nameof(AgeText))] private DateTime _createdAt;
    [ObservableProperty] private bool _pathExistsOnDisk;
    [ObservableProperty] private bool _isSelected;
    [ObservableProperty] private bool _isChecked;
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(IsConflict))]
    [NotifyPropertyChangedFor(nameof(HasOutcome))]
    private BatchMergeOutcome _mergeOutcome;

    public bool IsConflict => MergeOutcome == BatchMergeOutcome.Conflict;
    public bool HasOutcome => MergeOutcome != BatchMergeOutcome.None;

    public string AgeText => FormatAge(DateTime.UtcNow - CreatedAt);
    public bool IsActive => State == WorktreeState.Active;
    public bool IsRunning => TaskStatus == TaskStatus.Running;

    private static string FormatAge(TimeSpan ts)
    {
        if (ts.TotalDays >= 1) return $"{(int)ts.TotalDays}d ago";
        if (ts.TotalHours >= 1) return $"{(int)ts.TotalHours}h ago";
        if (ts.TotalMinutes >= 1) return $"{(int)ts.TotalMinutes}m ago";
        return "just now";
    }
}

public sealed partial class WorktreesGroupViewModel : ViewModelBase
{
    public required string ListId { get; init; }
    public required string ListName { get; init; }
    public ObservableCollection<WorktreeOverviewRowViewModel> Rows { get; } = new();
}

public sealed partial class WorktreesOverviewModalViewModel : ViewModelBase
{
    private readonly IWorkerClient _worker;
    private readonly Func<DiffViewerViewModel> _diffVmFactory;
    private readonly IMergeCoordinator _merge;

    [ObservableProperty] private string? _listIdFilter;
    [ObservableProperty] private string _title = "Worktrees";
    [ObservableProperty] private bool _isGlobal;
    [ObservableProperty] private bool _isBusy;
    [ObservableProperty] private string? _statusMessage;
    [ObservableProperty] private WorktreeOverviewRowViewModel? _selectedRow;
    [ObservableProperty][NotifyCanExecuteChangedFor(nameof(MergeAllCommand))] private string? _selectedTarget;
    [ObservableProperty][NotifyCanExecuteChangedFor(nameof(MergeAllCommand))] private int _selectedCount;
    [ObservableProperty][NotifyCanExecuteChangedFor(nameof(MergeAllCommand))] private bool _isMerging;
    [ObservableProperty] private string? _batchProgress;

    public ObservableCollection<WorktreeOverviewRowViewModel> Rows { get; } = new();
    public ObservableCollection<WorktreesGroupViewModel> Groups { get; } = new();
    public ObservableCollection<string> MergeTargets { get; } = new();
    public ObservableCollection<WorktreeOverviewRowViewModel> ConflictRows { get; } = new();

    public Action? CloseAction { get; set; }
    public Action<DiffViewerViewModel>? ShowDiffAction { get; set; }
    public Action<string, string>? JumpToTaskAction { get; set; }
    public Func<string, Task<bool>>? ConfirmAction { get; set; }
    public Func<MergeModalViewModel>? ResolveMergeVm { get; set; }
    public Func<MergeModalViewModel, Task>? ShowMergeAction { get; set; }

    public WorktreesOverviewModalViewModel(IWorkerClient worker, Func<DiffViewerViewModel> diffVmFactory, IMergeCoordinator merge)
    {
        _worker = worker;
        _diffVmFactory = diffVmFactory;
        _merge = merge;
    }

    public void SelectRow(WorktreeOverviewRowViewModel row)
    {
        if (SelectedRow is not null) SelectedRow.IsSelected = false;
        SelectedRow = row;
        row.IsSelected = true;
    }

    public void Configure(string? listId, string? listName)
    {
        ListIdFilter = listId;
        IsGlobal = listId is null;
        Title = listId is null
            ? Loc.T("vm.worktreesOverview.titleAll")
            : Loc.T("vm.worktreesOverview.titleList", listName ?? Loc.T("vm.worktreesOverview.listFallback"));
    }

    public async Task LoadAsync(CancellationToken ct = default)
    {
        IsBusy = true;
        try
        {
            var dtos = await _worker.GetWorktreesOverviewAsync(ListIdFilter);
            var ordered = dtos
                .OrderBy(d => d.State == WorktreeState.Active ? 0 : 1)
                .ThenByDescending(d => d.CreatedAt)
                .Select(Map)
                .ToList();

            Rows.Clear();
            Groups.Clear();
            ConflictRows.Clear();
            SelectedCount = 0;
            BatchProgress = null;
            if (IsGlobal)
            {
                foreach (var grp in ordered.GroupBy(r => (r.ListId, r.ListName))
                                            .OrderBy(g => g.Key.ListName, StringComparer.OrdinalIgnoreCase))
                {
                    var group = new WorktreesGroupViewModel { ListId = grp.Key.ListId, ListName = grp.Key.ListName };
                    foreach (var row in grp) { HookRow(row); group.Rows.Add(row); }
                    Groups.Add(group);
                }
            }
            else
            {
                foreach (var row in ordered) { HookRow(row); Rows.Add(row); }
            }
            await LoadMergeTargetsAsync();
        }
        finally
        {
            IsBusy = false;
        }
    }

    [RelayCommand]
    private Task Refresh()
    {
        StatusMessage = null;
        return LoadAsync();
    }

    [RelayCommand]
    private async Task CleanupFinished()
    {
        IsBusy = true;
        try
        {
            var result = await _worker.CleanupFinishedWorktreesAsync(ListIdFilter);
            StatusMessage = result is null ? Loc.T("vm.worktreesOverview.cleanupFailed") : Loc.T("vm.worktreesOverview.removed", result.Removed);
            await LoadAsync();
        }
        finally { IsBusy = false; }
    }

    [RelayCommand]
    private void Close() => CloseAction?.Invoke();

    [RelayCommand]
    private void ShowDiff(WorktreeOverviewRowViewModel? row)
    {
        if (row is null) return;
        var diffVm = _diffVmFactory();
        diffVm.ConfigureWorktree(row.Path, row.BaseCommit);
        ShowDiffAction?.Invoke(diffVm);
    }

    [RelayCommand]
    private void OpenInExplorer(WorktreeOverviewRowViewModel? row)
    {
        if (row is null || !row.PathExistsOnDisk) return;
        try { Process.Start(new ProcessStartInfo { FileName = row.Path, UseShellExecute = true }); }
        catch { }
    }

    [RelayCommand]
    private async Task Merge(WorktreeOverviewRowViewModel? row)
    {
        if (row is null || row.State != WorktreeState.Active) return;
        if (ResolveMergeVm is null || ShowMergeAction is null) return;
        var mergeVm = ResolveMergeVm();
        await mergeVm.InitializeAsync(row.TaskId, row.TaskTitle);
        await ShowMergeAction(mergeVm);
        await LoadAsync();
    }

    [RelayCommand]
    private void JumpToTask(WorktreeOverviewRowViewModel? row)
    {
        if (row is null) return;
        JumpToTaskAction?.Invoke(row.ListId, row.TaskId);
        CloseAction?.Invoke();
    }

    [RelayCommand]
    private async Task Discard(WorktreeOverviewRowViewModel? row)
    {
        if (row is null || row.State != WorktreeState.Active) return;
        var (ok, err) = await _worker.SetWorktreeStateAsync(row.TaskId, WorktreeState.Discarded);
        if (ok) row.State = WorktreeState.Discarded;
        else StatusMessage = err ?? Loc.T("vm.worktreesOverview.discardFailed");
    }

    [RelayCommand]
    private async Task Keep(WorktreeOverviewRowViewModel? row)
    {
        if (row is null || row.State != WorktreeState.Active) return;
        var (ok, err) = await _worker.SetWorktreeStateAsync(row.TaskId, WorktreeState.Kept);
        if (ok) row.State = WorktreeState.Kept;
        else StatusMessage = err ?? Loc.T("vm.worktreesOverview.keepFailed");
    }

    [RelayCommand]
    private async Task ForceRemove(WorktreeOverviewRowViewModel? row)
    {
        if (row is null) return;
        if (row.IsRunning) { StatusMessage = Loc.T("vm.worktreesOverview.cannotForceRunning"); return; }
        if (ConfirmAction is not null && !await ConfirmAction($"Force remove worktree for '{row.TaskTitle}'? This deletes the directory and branch.")) return;

        var result = await _worker.ForceRemoveWorktreeAsync(row.TaskId);
        if (result is null || !result.Removed)
        {
            StatusMessage = result?.Reason ?? Loc.T("vm.worktreesOverview.forceRemoveFailed");
            return;
        }
        if (IsGlobal)
        {
            foreach (var grp in Groups)
            {
                var idx = grp.Rows.IndexOf(row);
                if (idx >= 0) { grp.Rows.RemoveAt(idx); break; }
            }
        }
        else
        {
            Rows.Remove(row);
        }
    }

    [RelayCommand]
    private Task CopyBranch(WorktreeOverviewRowViewModel? row) => CopyToClipboardAsync(row?.BranchName);

    [RelayCommand]
    private Task CopyPath(WorktreeOverviewRowViewModel? row) => CopyToClipboardAsync(row?.Path);

    private static async Task CopyToClipboardAsync(string? text)
    {
        if (string.IsNullOrEmpty(text)) return;
        if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop &&
            desktop.MainWindow?.Clipboard is { } clipboard)
        {
            try { await clipboard.SetTextAsync(text); } catch { }
        }
    }

    private static WorktreeOverviewRowViewModel Map(WorktreeOverviewDto d) => new()
    {
        TaskId = d.TaskId, TaskTitle = d.TaskTitle, TaskStatus = d.TaskStatus,
        ListId = d.ListId, ListName = d.ListName,
        Path = d.Path, BranchName = d.BranchName, BaseCommit = d.BaseCommit, State = d.State,
        DiffStat = d.DiffStat, CreatedAt = d.CreatedAt, PathExistsOnDisk = d.PathExistsOnDisk,
    };

    public IEnumerable<WorktreeOverviewRowViewModel> AllRows =>
        IsGlobal ? Groups.SelectMany(g => g.Rows) : Rows;

    private void HookRow(WorktreeOverviewRowViewModel row)
    {
        row.PropertyChanged += (_, e) =>
        {
            if (e.PropertyName is nameof(WorktreeOverviewRowViewModel.IsChecked)
                               or nameof(WorktreeOverviewRowViewModel.State))
                RecomputeSelected();
        };
    }

    private void RecomputeSelected() =>
        SelectedCount = AllRows.Count(r => r.IsChecked && r.IsActive);

    // Test seam: adds a row to the flat list with selection tracking wired up.
    internal void AddRowForTest(WorktreeOverviewRowViewModel row)
    {
        HookRow(row);
        Rows.Add(row);
    }

    private async Task LoadMergeTargetsAsync()
    {
        var anchor = AllRows.FirstOrDefault(r => r.IsActive);
        if (anchor is null) { MergeTargets.Clear(); SelectedTarget = null; return; }
        try
        {
            var targets = await _worker.GetMergeTargetsAsync(anchor.TaskId);
            MergeTargets.Clear();
            if (targets is null) { SelectedTarget = null; return; }
            foreach (var b in targets.LocalBranches) MergeTargets.Add(b);
            SelectedTarget = MergeTargets.Contains(targets.DefaultBranch)
                ? targets.DefaultBranch
                : MergeTargets.FirstOrDefault();
        }
        catch { MergeTargets.Clear(); SelectedTarget = null; }
    }

    private bool CanMergeAll() => !IsMerging && SelectedCount > 0 && !string.IsNullOrWhiteSpace(SelectedTarget);

    [RelayCommand(CanExecute = nameof(CanMergeAll))]
    private Task MergeAll() => MergeSelectedAsync(_worker.MergeTaskAsync);

    [RelayCommand]
    private void ResolveConflict(WorktreeOverviewRowViewModel? row)
    {
        if (row is null) return;
        _ = _merge.ResolveConflictAsync(row.TaskId, SelectedTarget ?? "");
    }

    [RelayCommand]
    private void ToggleSelectAll()
    {
        var actives = AllRows.Where(r => r.IsActive).ToList();
        var allChecked = actives.Count > 0 && actives.All(r => r.IsChecked);
        foreach (var r in actives) r.IsChecked = !allChecked;
    }

    public async Task MergeSelectedAsync(
        Func<string, string, bool, string, Task<MergeResultDto>> mergeFn,
        CancellationToken ct = default)
    {
        var target = SelectedTarget;
        if (string.IsNullOrWhiteSpace(target)) return;

        var selected = AllRows.Where(r => r.IsChecked && r.IsActive).ToList();
        if (selected.Count == 0) return;

        IsMerging = true;
        ConflictRows.Clear();
        var done = 0;
        try
        {
            foreach (var row in selected)
            {
                ct.ThrowIfCancellationRequested();
                row.MergeOutcome = BatchMergeOutcome.Merging;
                BatchProgress = Loc.T("vm.worktreesOverview.batchProgress", ++done, selected.Count);

                MergeResultDto result;
                try
                {
                    result = await mergeFn(row.TaskId, target!, false,
                        Loc.T("vm.merge.commitMessage", row.TaskTitle));
                }
                catch
                {
                    row.MergeOutcome = BatchMergeOutcome.Failed;
                    continue;
                }

                switch (result.Status)
                {
                    case "merged":
                        row.MergeOutcome = BatchMergeOutcome.Merged;
                        row.State = WorktreeState.Merged;
                        row.IsChecked = false;
                        break;
                    case "conflict":
                        row.MergeOutcome = BatchMergeOutcome.Conflict;
                        ConflictRows.Add(row);
                        break;
                    case "blocked":
                        row.MergeOutcome = BatchMergeOutcome.Blocked;
                        break;
                    default:
                        row.MergeOutcome = BatchMergeOutcome.Failed;
                        break;
                }
            }
            BatchProgress = Loc.T("vm.worktreesOverview.batchDone",
                selected.Count(r => r.MergeOutcome == BatchMergeOutcome.Merged), ConflictRows.Count);
        }
        finally
        {
            IsMerging = false;
        }
    }
}
