using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.VisualTree;
using ClaudeDo.Ui.ViewModels.Islands;
using ClaudeDo.Ui.ViewModels.Modals;
using ClaudeDo.Ui.Views.Modals;

namespace ClaudeDo.Ui.Views.Islands;

public partial class TasksIslandView : UserControl
{
    private static readonly DataFormat<string> TaskRowFormat =
        DataFormat.CreateStringApplicationFormat("claudedo-task-row");

    public TasksIslandView()
    {
        InitializeComponent();
        AddHandler(PointerPressedEvent, OnTunnelPointerPressed, RoutingStrategies.Tunnel);
        DataContextChanged += (_, _) =>
        {
            if (DataContext is TasksIslandViewModel vm)
            {
                vm.FocusAddTaskRequested += (_, _) => AddTaskBox.Focus();
                vm.ShowUnfinishedPlanningModal = async (modalVm) =>
                {
                    var owner = TopLevel.GetTopLevel(this) as Window;
                    if (owner is null) { modalVm.CancelCommand.Execute(null); return; }
                    var modal = new UnfinishedPlanningModalView { DataContext = modalVm };
                    // Closing via the OS title-bar (if ever enabled) also resolves the TCS.
                    modal.Closed += (_, _) => modalVm.CancelCommand.Execute(null);
                    await modal.ShowDialog(owner);
                    // ShowDialog completes once the window is closed (CloseAction or OS close).
                };
                vm.ConfirmAsync = ShowConfirmAsync;
            }
        };
    }

    private async System.Threading.Tasks.Task<bool> ShowConfirmAsync(string message)
    {
        var owner = TopLevel.GetTopLevel(this) as Window;
        if (owner is null) return false;

        var tcs = new TaskCompletionSource<bool>();
        var cancel  = new Button { Content = "Cancel",  MinWidth = 90 };
        var confirm = new Button { Content = "Confirm", MinWidth = 90, Classes = { "danger" } };

        var dialog = new Window
        {
            Title = "Confirm",
            Width = 380,
            SizeToContent = SizeToContent.Height,
            CanResize = false,
            WindowStartupLocation = WindowStartupLocation.CenterOwner,
            ShowInTaskbar = false,
            Background = this.FindResource("SurfaceBrush") as IBrush,
            Content = new StackPanel
            {
                Margin  = new Thickness(20),
                Spacing = 16,
                Children =
                {
                    new TextBlock { Text = message, TextWrapping = TextWrapping.Wrap },
                    new StackPanel
                    {
                        Orientation = Orientation.Horizontal,
                        HorizontalAlignment = HorizontalAlignment.Right,
                        Spacing = 8,
                        Children = { cancel, confirm },
                    },
                },
            },
        };

        cancel.Click  += (_, _) => { tcs.TrySetResult(false); dialog.Close(); };
        confirm.Click += (_, _) => { tcs.TrySetResult(true);  dialog.Close(); };
        dialog.Closed += (_, _) => tcs.TrySetResult(false);

        _ = dialog.ShowDialog(owner);
        return await tcs.Task;
    }

    private async void OnTunnelPointerPressed(object? sender, PointerPressedEventArgs e)
    {
        if (DataContext is not TasksIslandViewModel vm) return;
        if (e.Source is not Visual src) return;

        var button = src as Button ?? src.FindAncestorOfType<Button>();
        if (button?.DataContext is not TaskRowViewModel row) return;
        if (!e.GetCurrentPoint(button).Properties.IsLeftButtonPressed) return;

        // Select now so the details pane updates whether the gesture becomes a click or a drag.
        // (Button.Click doesn't fire once DoDragDropAsync captures the pointer.)
        vm.SelectedTask = row;

        // If the click landed on a nested Button (e.g. the done-toggle checkbox or star),
        // don't start a drag — that would capture the pointer and swallow the inner Click.
        var nestedInsideButton = button.Parent is Visual parentVisual
            && parentVisual.FindAncestorOfType<Button>() is not null;
        if (nestedInsideButton) return;

        if (!vm.CanReorder || row.IsRunning) return;

        var data = new DataTransfer();
        data.Add(DataTransferItem.Create(TaskRowFormat, row.Id));
        try
        {
            await DragDrop.DoDragDropAsync(e, data, DragDropEffects.Move);
        }
        finally
        {
            vm.ClearDropHints();
        }
    }

    private void OnRowPointerPressed(object? sender, PointerPressedEventArgs e) { }
    private void OnRowPointerMoved(object? sender, PointerEventArgs e) { }
    private void OnRowPointerReleased(object? sender, PointerReleasedEventArgs e) { }

    private void OnRowDragOver(object? sender, DragEventArgs e)
    {
        if (DataContext is not TasksIslandViewModel vm) { e.DragEffects = DragDropEffects.None; return; }
        if (!e.DataTransfer?.Contains(TaskRowFormat) ?? true)
        {
            e.DragEffects = DragDropEffects.None;
            vm.ClearDropHints();
            return;
        }
        if (sender is not Button b || b.DataContext is not TaskRowViewModel target || target.IsRunning)
        {
            e.DragEffects = DragDropEffects.None;
            vm.ClearDropHints();
            return;
        }

        var sourceId = e.DataTransfer?.TryGetValue(TaskRowFormat);
        if (string.IsNullOrEmpty(sourceId) || sourceId == target.Id)
        {
            e.DragEffects = DragDropEffects.None;
            vm.ClearDropHints();
            return;
        }

        var placeBelow = e.GetPosition(b).Y > b.Bounds.Height / 2;

        // Canonicalize: "drop below X" == "drop above X+1". Render the indicator
        // above X+1 when there is one; only the last row in a section shows a below-line.
        TaskRowViewModel hintRow = target;
        bool hintBelow = false;
        if (placeBelow)
        {
            var next = FindNextInSameSection(vm, target);
            if (next is not null && !next.IsRunning)
            {
                hintRow = next;
                hintBelow = false;
            }
            else
            {
                hintRow = target;
                hintBelow = true;
            }
        }

        // A hint that lands right where the dragged row already sits is a no-op.
        if (hintRow.Id == sourceId)
        {
            e.DragEffects = DragDropEffects.None;
            vm.ClearDropHints();
            return;
        }

        vm.SetDropHint(hintRow, hintBelow);
        e.DragEffects = DragDropEffects.Move;
    }

    private static TaskRowViewModel? FindNextInSameSection(TasksIslandViewModel vm, TaskRowViewModel row)
    {
        foreach (var section in new[] { vm.OverdueItems, vm.OpenItems, vm.CompletedItems })
        {
            var idx = section.IndexOf(row);
            if (idx >= 0) return idx + 1 < section.Count ? section[idx + 1] : null;
        }
        return null;
    }

    private async void OnRowDrop(object? sender, DragEventArgs e)
    {
        if (DataContext is not TasksIslandViewModel vm) return;
        try
        {
            if (sender is not Button b || b.DataContext is not TaskRowViewModel target) return;
            if (target.IsRunning) return;

            var sourceId = e.DataTransfer?.TryGetValue(TaskRowFormat);
            if (string.IsNullOrEmpty(sourceId) || sourceId == target.Id) return;

            var source = FindRowById(vm, sourceId);
            if (source is null || source.IsRunning) return;

            var placeBelow = e.GetPosition(b).Y > b.Bounds.Height / 2;

            // Clear the 6px drop-hint spacer BEFORE the move so the reorder animates
            // into its truly-final layout in one step (otherwise the row lands in the
            // gap, then the gap collapses and everything shifts up a second time).
            vm.ClearDropHints();

            await vm.ReorderAsync(source, target, placeBelow);
        }
        catch
        {
            vm.ClearDropHints();
            throw;
        }
    }

    private static TaskRowViewModel? FindRowById(TasksIslandViewModel vm, string id)
    {
        foreach (var r in vm.Items)
            if (r.Id == id) return r;
        return null;
    }
}
