using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Modals;
using Microsoft.Extensions.DependencyInjection;

namespace ClaudeDo.Ui.ViewModels.Islands;

public sealed partial class MergeSectionViewModel : ViewModelBase
{
    private readonly IWorkerClient _worker;
    private readonly IServiceProvider _services;

    // Context mirrored from parent, updated via Sync* methods
    internal string? TaskId { get; private set; }
    internal string? TaskTitle { get; private set; }
    private string? _worktreePath;
    private string? _worktreeBaseCommit;
    private string? _worktreeHeadCommit;
    private string? _worktreeStateLabel;
    private string? _listWorkingDir;
    private bool _isPlanningParent;
    private int _subtaskCount;
    private bool _hasChildOutcomes;

    [ObservableProperty] private ObservableCollection<string> _mergeTargetBranches = new();
    [ObservableProperty] private string? _selectedMergeTarget;

    // No CanExecute to gate here — RefreshMergePreviewAsync is triggered from the Bind path,
    // not a [RelayCommand], so PreviewOp only drives the indicator, never locks a button.
    public OperationStatus PreviewOp { get; } = new();

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(ShowMergePreviewMuted))]
    private string _mergePreviewText = "";

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(ShowMergePreviewMuted))]
    private bool _mergeIsClean;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(ShowMergePreviewMuted))]
    private bool _mergeIsConflict;

    public bool ShowMergePreviewMuted =>
        !MergeIsClean && !MergeIsConflict && !string.IsNullOrEmpty(MergePreviewText);

    // CanDiffMergedRange covers a worktree-less list-handler host task (no live worktree, but
    // a HandlerBaseCommit/HandlerHeadCommit review range over the list's working dir) so its
    // merge/diff card still renders even though _worktreePath stays null for it.
    public bool ShowMergeSection =>
        _worktreePath != null || _isPlanningParent || _hasChildOutcomes || CanDiffMergedRange;

    public Func<DiffViewerViewModel, System.Threading.Tasks.Task>? ShowDiffViewer { get; set; }
    public Func<MergeModalViewModel, System.Threading.Tasks.Task>? ShowMergeModal { get; set; }

    // Raised when the user opens a diff/combined-diff for the current task, so the
    // review gate can record that the changes were inspected before merging.
    public Action? DiffViewed { get; set; }

    // Mirrors ListsIslandViewModel/TasksIslandViewModel.ErrorReported — DetailsIslandViewModel
    // forwards this into its own ErrorReported, which the shell flashes in the footer strip.
    public event Action<string>? ErrorReported;

    // True when there is something to inspect before merging (a live worktree diff,
    // a merged commit range, or a planning/children combined diff). When false there
    // is nothing to read, so the review gate must not block approve.
    public bool HasReviewableDiff => CanOpenDiff() || CanReviewDiff();

    public MergeSectionViewModel(IWorkerClient worker, IServiceProvider services)
    {
        _worker = worker;
        _services = services;
    }

    partial void OnSelectedMergeTargetChanged(string? value) => _ = RefreshMergePreviewAsync();

    internal void SyncWorktree(
        string? worktreePath,
        string? worktreeBase,
        string? worktreeHead,
        string? worktreeState,
        string? listWorkDir)
    {
        _worktreePath = worktreePath;
        _worktreeBaseCommit = worktreeBase;
        _worktreeHeadCommit = worktreeHead;
        _worktreeStateLabel = worktreeState;
        _listWorkingDir = listWorkDir;
        OnPropertyChanged(nameof(ShowMergeSection));
        OnPropertyChanged(nameof(HasReviewableDiff));
        OpenDiffCommand.NotifyCanExecuteChanged();
        OpenWorktreeCommand.NotifyCanExecuteChanged();
    }

    internal void SyncTaskContext(string? taskId, string? taskTitle, bool isPlanningParent)
    {
        TaskId = taskId;
        TaskTitle = taskTitle;
        _isPlanningParent = isPlanningParent;
        OnPropertyChanged(nameof(ShowMergeSection));
        OnPropertyChanged(nameof(HasReviewableDiff));
    }

    internal void SyncChildOutcomes(bool hasChildOutcomes, int subtaskCount)
    {
        _hasChildOutcomes = hasChildOutcomes;
        _subtaskCount = subtaskCount;
        OnPropertyChanged(nameof(ShowMergeSection));
        OnPropertyChanged(nameof(HasReviewableDiff));
        ReviewCombinedDiffCommand.NotifyCanExecuteChanged();
    }

    internal async System.Threading.Tasks.Task RefreshMergePreviewAsync()
    {
        if (TaskId is null || _worktreePath is null)
        {
            MergePreviewText = ""; MergeIsClean = false; MergeIsConflict = false;
            return;
        }
        if (_worktreeStateLabel is { } label && label != "Active")
        {
            MergePreviewText = label; MergeIsClean = false; MergeIsConflict = false;
            return;
        }
        var capturedTaskId = TaskId;
        var capturedTarget = SelectedMergeTarget;
        using var op = PreviewOp.Begin(Loc.T("ops.review.previewing"));
        var dto = await _worker.PreviewMergeAsync(capturedTaskId, capturedTarget ?? "");
        if (TaskId != capturedTaskId || SelectedMergeTarget != capturedTarget) return;
        var (text, clean, conflict) = MergePreviewPresenter.Describe(dto);
        MergePreviewText = text; MergeIsClean = clean; MergeIsConflict = conflict;
    }

    internal void Clear()
    {
        MergeTargetBranches.Clear();
        SelectedMergeTarget = null;
        MergePreviewText = "";
        MergeIsClean = false;
        MergeIsConflict = false;
        SyncWorktree(null, null, null, null, null);
        SyncTaskContext(null, null, false);
        SyncChildOutcomes(false, 0);
    }

    [RelayCommand(CanExecute = nameof(CanReviewDiff))]
    private async System.Threading.Tasks.Task ReviewCombinedDiffAsync()
    {
        if (TaskId is null || ShowDiffViewer is null) return;
        var vm = _services.GetRequiredService<DiffViewerViewModel>();
        vm.ConfigurePlanning(TaskId, SelectedMergeTarget ?? "main");
        await vm.LoadAsync();
        DiffViewed?.Invoke();
        await ShowDiffViewer(vm);
    }

    private bool CanReviewDiff() => (_isPlanningParent && _subtaskCount > 0) || _hasChildOutcomes;

    [RelayCommand(CanExecute = nameof(CanOpenDiff))]
    private async System.Threading.Tasks.Task OpenDiffAsync()
    {
        if (ShowDiffViewer is null) return;

        var hasLiveWorktree =
            _worktreePath != null
            && _worktreeStateLabel == "Active"
            && System.IO.Directory.Exists(_worktreePath);

        var vm = _services.GetRequiredService<DiffViewerViewModel>();
        if (hasLiveWorktree)
        {
            vm.ConfigureWorktree(_worktreePath!, _worktreeBaseCommit, TaskId, TaskTitle ?? "");
            vm.ShowMergeModal = ShowMergeModal;
            vm.ResolveMergeVm = () => _services.GetRequiredService<MergeModalViewModel>();
        }
        else if (CanDiffMergedRange)
        {
            vm.ConfigureCommitRange(_listWorkingDir!, _worktreeBaseCommit, _worktreeHeadCommit, TaskId, TaskTitle ?? "");
        }
        else return;

        await vm.LoadAsync();
        DiffViewed?.Invoke();
        await ShowDiffViewer(vm);
    }

    private bool CanDiffMergedRange =>
        _worktreeBaseCommit != null && _worktreeHeadCommit != null && _listWorkingDir != null;

    private bool CanOpenDiff() => _worktreePath != null || CanDiffMergedRange;

    [RelayCommand(CanExecute = nameof(CanOpenWorktree))]
    private void OpenWorktree()
    {
        var (ok, error) = ShellOpen.Path(_worktreePath);
        if (!ok && error is not null)
            ErrorReported?.Invoke(Loc.T("vm.mergeSection.openWorktreeFailed", error));
    }

    private bool CanOpenWorktree() => _worktreePath != null;
}
