using System.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.ViewModels.Islands;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Ui.Views.Islands;

public partial class TaskRowView : UserControl
{
    private TaskRowViewModel? _pendingScheduleRow;

    public TaskRowView() { InitializeComponent(); }

    private TasksIslandViewModel? FindTasksVm() =>
        this.GetVisualAncestors().OfType<ItemsControl>()
            .Select(ic => ic.DataContext).OfType<TasksIslandViewModel>().FirstOrDefault();

    // The context menu is built here instead of declared in XAML so the MenuItems only exist
    // while a row's menu is actually open, not for every row rendered in the list.
    private void OnRowContextRequested(object? sender, ContextRequestedEventArgs e)
    {
        if (sender is not Border border) return;
        if (DataContext is not TaskRowViewModel row) return;
        if (FindTasksVm() is not { } vm) return;

        // OnTunnelPointerPressed (TasksIslandView) only selects on the left button, so
        // right-click needs its own explicit selection before the menu opens.
        vm.SelectFrom(row, "context-menu");

        var menu = new ContextMenu { DataContext = row };

        // reason non-null => IsEnabled=false with that text as the tooltip (ToolTip.ShowOnDisabled
        // renders it even though the item is disabled — verified headlessly on Avalonia 12, see
        // task A3). Used for Group 1's four gated entries; submenu items keep isVisible hiding.
        MenuItem MakeItem(string key, EventHandler<RoutedEventArgs> click, bool isVisible = true, string? tag = null, string? reason = null)
        {
            var item = new MenuItem { Header = Loc.T(key), DataContext = row, IsVisible = isVisible };
            if (tag is not null) item.Tag = tag;
            if (reason is not null)
            {
                item.IsEnabled = false;
                ToolTip.SetTip(item, reason);
                ToolTip.SetShowOnDisabled(item, true);
            }
            item.Click += click;
            return item;
        }

        // Group 1 — fixed length and order, always visible; an unmet precondition grays the
        // entry out instead of hiding it (Remove from queue has no dedicated reason text, only
        // the IsEnabled toggle).
        menu.Items.Add(MakeItem("tasks.ctxSendToQueue", OnSendToQueueClick, reason: row.SendToQueueDisabledReason));
        var removeFromQueueItem = MakeItem("tasks.ctxRemoveFromQueue", OnRemoveFromQueueClick);
        removeFromQueueItem.IsEnabled = row.CanRemoveFromQueue;
        menu.Items.Add(removeFromQueueItem);
        menu.Items.Add(MakeItem("tasks.ctxCancelExecution", OnCancelExecutionClick, reason: row.CancelDisabledReason));
        menu.Items.Add(MakeItem("tasks.ctxOpenConPtySession", OnOpenConPtySessionClick, reason: row.QuickSessionDisabledReason));
        menu.Items.Add(MakeItem("tasks.ctxOpenWorktree", OnOpenWorktreeClick, reason: row.OpenWorktreeDisabledReason));
        menu.Items.Add(MakeItem("tasks.ctxRefineTask", OnRefineTaskClick, reason: row.RefineDisabledReason));
        menu.Items.Add(new Separator());

        // "Mark as" — exclusive-choice hiding inside is unchanged; the header is never empty.
        var markAs = new MenuItem { Header = Loc.T("tasks.ctxMarkAs"), DataContext = row };
        markAs.Items.Add(MakeItem("tasks.ctxMarkDone", OnSetStatusClick, tag: "Done", reason: row.MarkAsDisabledReason));
        markAs.Items.Add(MakeItem("tasks.ctxMarkCancelled", OnSetStatusClick, tag: "Cancelled", reason: row.MarkAsDisabledReason));
        markAs.Items.Add(new Separator());
        markAs.Items.Add(MakeItem("tasks.ctxMarkManual", OnToggleManualClick, !row.IsManual));
        markAs.Items.Add(MakeItem("tasks.ctxMarkClaudeTask", OnToggleManualClick, row.IsManual));
        menu.Items.Add(markAs);

        // "Planning" — the header itself disables + explains when it would otherwise open empty.
        var planning = new MenuItem { Header = Loc.T("tasks.ctxPlanning"), DataContext = row };
        if (row.PlanningDisabledReason is { } planningReason)
        {
            planning.IsEnabled = false;
            ToolTip.SetTip(planning, planningReason);
            ToolTip.SetShowOnDisabled(planning, true);
        }
        planning.Items.Add(MakeItem("tasks.ctxOpenPlanningSession", OnOpenPlanningSessionClick, row.CanOpenPlanningSession));
        planning.Items.Add(MakeItem("tasks.ctxResumePlanningSession", OnResumePlanningSessionClick, row.CanResumeOrDiscardPlanning));
        planning.Items.Add(MakeItem("tasks.ctxFinalizePlanningSession", OnFinalizePlanningSessionClick, row.CanFinalizePlanning));
        planning.Items.Add(MakeItem("tasks.ctxDiscardPlanningSession", OnDiscardPlanningSessionClick, row.CanResumeOrDiscardPlanning));
        menu.Items.Add(planning);

        // "Schedule" — always has at least "Schedule for…", so the header is never empty.
        var schedule = new MenuItem { Header = Loc.T("tasks.ctxSchedule"), DataContext = row };
        schedule.Items.Add(MakeItem("tasks.ctxScheduleFor", OnScheduleForClick));
        schedule.Items.Add(MakeItem("tasks.ctxClearSchedule", OnClearScheduleClick, row.HasSchedule));
        schedule.Items.Add(new Separator());
        schedule.Items.Add(MakeItem("tasks.ctxAddToMyDay", OnAddToMyDayClick, row.CanAddToMyDay));
        schedule.Items.Add(MakeItem("tasks.ctxRemoveFromMyDay", OnRemoveFromMyDayClick, row.IsMyDay));
        menu.Items.Add(schedule);

        menu.Items.Add(new Separator());
        menu.Items.Add(MakeItem("tasks.ctxDeleteTask", OnDeleteTaskClick, reason: row.DeleteDisabledReason));

        menu.Open(border);
        e.Handled = true;
    }

    private async void OnSendToQueueClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.SendToQueueCommand.ExecuteAsync(row);
    }

    private async void OnRemoveFromQueueClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.RemoveFromQueueCommand.ExecuteAsync(row);
    }

    private async void OnCancelExecutionClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.CancelRunningTaskCommand.ExecuteAsync(row);
    }

    private async void OnRefineTaskClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.RefineTaskCommand.ExecuteAsync(row);
    }

    private async void OnClearScheduleClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.ClearScheduleCommand.ExecuteAsync(row);
    }

    private async void OnToggleManualClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.ToggleManualCommand.ExecuteAsync(row);
    }

    private async void OnAddToMyDayClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.AddToMyDayCommand.ExecuteAsync(row);
    }

    private async void OnRemoveFromMyDayClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.RemoveFromMyDayCommand.ExecuteAsync(row);
    }

    private async void OnDeleteTaskClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.DeleteTaskCommand.ExecuteAsync(row);
    }

    private void OnOpenPlanningSessionClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            vm.OpenPlanningSessionCommand.Execute(row);
    }

    private void OnOpenConPtySessionClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            vm.OpenConPtySessionCommand.Execute(row);
    }

    private void OnOpenWorktreeClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            vm.OpenTaskWorktreeCommand.Execute(row);
    }

    // The status chip is only actionable while a ConPTY session is open; the open command dedupes
    // by task id, so it surfaces Mission Control and focuses the existing pane.
    private void OnStatusChipTapped(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not TaskRowViewModel { HasInteractiveSession: true } row) return;
        if (FindTasksVm() is { } vm)
            vm.OpenConPtySessionCommand.Execute(row);
    }

    private async void OnResumePlanningSessionClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.ResumePlanningSessionCommand.ExecuteAsync(row);
    }

    private async void OnDiscardPlanningSessionClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.DiscardPlanningSessionCommand.ExecuteAsync(row);
    }

    private async void OnFinalizePlanningSessionClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
            await vm.FinalizePlanningSessionCommand.ExecuteAsync(row);
    }

    private async void OnSetStatusClick(object? sender, RoutedEventArgs e)
    {
        if (sender is not MenuItem mi) return;
        if (DataContext is not TaskRowViewModel row) return;
        if (FindTasksVm() is not { } vm) return;
        if (mi.Tag is not string tag) return;
        if (!System.Enum.TryParse<TaskStatus>(tag, ignoreCase: true, out var status)) return;
        await vm.SetStatusOnRowAsync(row, status);
    }

    private void OnScheduleForClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not TaskRowViewModel row) return;
        _pendingScheduleRow = row;
        ScheduleDate.SelectedDate = row.ScheduledFor?.ToLocalTime() ?? DateTime.Now.AddHours(1);
        ScheduleAnchor.Flyout?.ShowAt(ScheduleAnchor);
    }

    private async void OnScheduleSetClick(object? sender, RoutedEventArgs e)
    {
        ScheduleAnchor.Flyout?.Hide();
        if (_pendingScheduleRow is null || ScheduleDate.SelectedDate is null) return;
        var when = ScheduleDate.SelectedDate.Value;
        if (FindTasksVm() is { } tvm)
            await tvm.SetScheduledForAsync(_pendingScheduleRow, when);
        _pendingScheduleRow = null;
    }

    private void OnScheduleCancelClick(object? sender, RoutedEventArgs e)
    {
        ScheduleAnchor.Flyout?.Hide();
        _pendingScheduleRow = null;
    }

    private void OnRowPointerEntered(object? sender, PointerEventArgs e)
    {
        if (DataContext is TaskRowViewModel row) row.IsHovered = true;
    }

    private void OnRowPointerExited(object? sender, PointerEventArgs e)
    {
        if (DataContext is TaskRowViewModel row) row.IsHovered = false;
    }
}
