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

namespace ClaudeDo.Ui.Views.Islands;

public partial class ListsIslandView : UserControl
{
    private static readonly DataFormat<string> ListRowFormat =
        DataFormat.CreateStringApplicationFormat("claudedo-list-row");

    public ListsIslandView()
    {
        InitializeComponent();
        AddHandler(PointerPressedEvent, OnTunnelPointerPressed, RoutingStrategies.Tunnel);
        DataContextChanged += (_, _) =>
        {
            if (DataContext is ListsIslandViewModel vm)
                vm.FocusSearchRequested += (_, _) => SearchBox.Focus();
        };
    }

    private void OnItemTapped(object? sender, RoutedEventArgs e)
    {
        if (sender is Border { DataContext: ListNavItemViewModel item }
            && DataContext is ListsIslandViewModel vm)
            vm.SelectCommand.Execute(item);
    }

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

        var border = FindListItemBorder(src);
        if (border?.DataContext is not ListNavItemViewModel row || row.Kind != ListKind.User) return;
        if (!e.GetCurrentPoint(border).Properties.IsLeftButtonPressed) return;

        // Double-click opens the list's settings instead of starting a drag. Handled here
        // because DoDragDropAsync captures the pointer and would swallow a DoubleTapped event.
        if (e.ClickCount == 2)
        {
            vm.OpenListSettingsCommand.Execute(row);
            return;
        }

        // Select now so the right pane updates whether the gesture becomes a click or a drag
        // (the Tapped handler doesn't fire once DoDragDropAsync captures the pointer).
        vm.SelectCommand.Execute(row);

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

    private void OnListDragOver(object? sender, DragEventArgs e)
    {
        if (DataContext is not ListsIslandViewModel vm) { e.DragEffects = DragDropEffects.None; return; }
        if (!e.DataTransfer?.Contains(ListRowFormat) ?? true)
        {
            e.DragEffects = DragDropEffects.None;
            vm.ClearDropHints();
            return;
        }
        if (sender is not Border b || b.DataContext is not ListNavItemViewModel target || target.Kind != ListKind.User)
        {
            e.DragEffects = DragDropEffects.None;
            vm.ClearDropHints();
            return;
        }

        var sourceId = e.DataTransfer?.TryGetValue(ListRowFormat);
        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". Only the last row shows a below-line.
        ListNavItemViewModel hintRow = target;
        bool hintBelow = false;
        if (placeBelow)
        {
            var next = FindNextUserList(vm, target);
            if (next is not null) { hintRow = next; hintBelow = false; }
            else { hintRow = target; hintBelow = true; }
        }

        if (hintRow.Id == sourceId)
        {
            e.DragEffects = DragDropEffects.None;
            vm.ClearDropHints();
            return;
        }

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

    private async void OnListDrop(object? sender, DragEventArgs e)
    {
        if (DataContext is not ListsIslandViewModel vm) return;
        try
        {
            if (sender is not Border b || b.DataContext is not ListNavItemViewModel target || target.Kind != ListKind.User) return;

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

            var source = vm.UserLists.FirstOrDefault(r => r.Id == sourceId);
            if (source is null) return;

            var placeBelow = e.GetPosition(b).Y > b.Bounds.Height / 2;
            vm.ClearDropHints();
            await vm.ReorderAsync(source, target, placeBelow);
        }
        catch
        {
            vm.ClearDropHints();
            throw;
        }
    }

    private static Border? FindListItemBorder(Visual? v)
    {
        while (v is not null)
        {
            if (v is Border b && b.Classes.Contains("list-item")) return b;
            v = v.GetVisualParent();
        }
        return null;
    }

    private static ListNavItemViewModel? FindNextUserList(ListsIslandViewModel vm, ListNavItemViewModel row)
    {
        var idx = vm.UserLists.IndexOf(row);
        if (idx < 0) return null;
        return idx + 1 < vm.UserLists.Count ? vm.UserLists[idx + 1] : null;
    }

}
