using System.Windows.Input;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;

namespace ClaudeDo.Ui.Views.Controls;

/// <summary>Reusable modal chrome: titlebar (drag + close) wrapping a body and optional footer.</summary>
public class ModalShell : ContentControl
{
    public static readonly StyledProperty<string?> TitleProperty =
        AvaloniaProperty.Register<ModalShell, string?>(nameof(Title));

    public static readonly StyledProperty<object?> FooterProperty =
        AvaloniaProperty.Register<ModalShell, object?>(nameof(Footer));

    public static readonly StyledProperty<ICommand?> CloseCommandProperty =
        AvaloniaProperty.Register<ModalShell, ICommand?>(nameof(CloseCommand));

    // Opt-in: most modals are fixed-purpose dialogs that don't need maximise. Set true only
    // on windows (e.g. the diff viewer) that benefit from a maximised full-screen layout.
    public static readonly StyledProperty<bool> ShowMaximizeButtonProperty =
        AvaloniaProperty.Register<ModalShell, bool>(nameof(ShowMaximizeButton));

    public string? Title { get => GetValue(TitleProperty); set => SetValue(TitleProperty, value); }
    public object? Footer { get => GetValue(FooterProperty); set => SetValue(FooterProperty, value); }
    public ICommand? CloseCommand { get => GetValue(CloseCommandProperty); set => SetValue(CloseCommandProperty, value); }
    public bool ShowMaximizeButton { get => GetValue(ShowMaximizeButtonProperty); set => SetValue(ShowMaximizeButtonProperty, value); }

    private Window? _window;
    private PathIcon? _maxIcon;
    private Button? _maxButton;

    protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
    {
        base.OnApplyTemplate(e);
        if (e.NameScope.Find<Border>("PART_TitleBar") is { } bar)
            bar.PointerPressed += OnTitleBarPressed;
        _maxButton = e.NameScope.Find<Button>("PART_MaxButton");
        if (_maxButton is not null) _maxButton.Click += OnMaxButtonClick;
        _maxIcon = e.NameScope.Find<PathIcon>("PART_MaxIcon");
        UpdateMaxIcon();
    }

    /// Mirrors what MainWindow does for itself: with an extended client area a maximised window
    /// overhangs the screen by the invisible resize border, so the chrome has to be inset by
    /// OffScreenMargin or its edges (and the close button) get clipped. Doing it here covers
    /// every modal at once instead of per code-behind.
    protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
    {
        base.OnAttachedToVisualTree(e);
        _window = TopLevel.GetTopLevel(this) as Window;
        if (_window is null) return;
        Margin = _window.OffScreenMargin;
        _window.PropertyChanged += OnWindowPropertyChanged;
        UpdateMaxIcon();
    }

    protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
    {
        base.OnDetachedFromVisualTree(e);
        if (_window is not null) _window.PropertyChanged -= OnWindowPropertyChanged;
        _window = null;
    }

    private void OnWindowPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
    {
        if (e.Property == Window.OffScreenMarginProperty && _window is not null)
            Margin = _window.OffScreenMargin;
        if (e.Property == Window.WindowStateProperty)
            UpdateMaxIcon();
    }

    private void UpdateMaxIcon()
    {
        if (_maxIcon is null || _window is null) return;
        var maximized = _window.WindowState == WindowState.Maximized;
        var key = maximized ? "Icon.WinRestore" : "Icon.WinMax";
        if (this.TryFindResource(key, out var geometry) && geometry is Geometry g)
            _maxIcon.Data = g;
        if (_maxButton is not null)
        {
            var tipKey = maximized ? "modals.diff.restore" : "modals.diff.maximize";
            ToolTip.SetTip(_maxButton, ClaudeDo.Ui.Localization.Loc.T(tipKey));
        }
    }

    private void OnMaxButtonClick(object? sender, RoutedEventArgs e) => ToggleMaximize();

    private void ToggleMaximize()
    {
        if (_window is null) return;
        _window.WindowState = _window.WindowState == WindowState.Maximized
            ? WindowState.Normal
            : WindowState.Maximized;
    }

    /// <summary>
    /// Hands the drag to the window manager instead of moving the window by hand.
    /// </summary>
    /// <remarks>
    /// This is what makes Windows snap work — drag to the top edge to maximise, to a side for
    /// half the screen, with the usual preview overlay. Assigning <c>Window.Position</c> per
    /// pointer-move (what this used to do) bypasses the OS move loop entirely, so no snap
    /// gesture is ever recognised. The OS only snaps resizable windows, so the non-resizable
    /// modals opt out on their own without a flag here.
    ///
    /// Note: <c>VisualRoot</c> is a TopLevelHost rather than the Window in Avalonia 12, so the
    /// window has to come from <see cref="TopLevel.GetTopLevel"/>. Do not capture the pointer —
    /// <see cref="Window.BeginMoveDrag"/> runs its own modal loop and needs the input.
    /// </remarks>
    private void OnTitleBarPressed(object? sender, PointerPressedEventArgs e)
    {
        if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return;
        if (ShowMaximizeButton && e.ClickCount == 2)
        {
            ToggleMaximize();
            return;
        }
        (TopLevel.GetTopLevel(this) as Window)?.BeginMoveDrag(e);
    }
}
