using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using DodoSSH.Client.Shell.ViewModels;

namespace DodoSSH.Client.App.Views;

/// <summary>The tab strip, above every screen.</summary>
internal sealed partial class TerminalTabs : UserControl
{
    public TerminalTabs() => InitializeComponent();

    /// <summary>
    /// Closes a tab on a middle click.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Wired on the tab's own template root, which is the whole answer to "and not on the strip itself".
    /// A middle press on the background, on the sentence, or on the button that opens a connection reaches
    /// no handler at all, because there is none there to reach. Nothing has to test what was clicked.
    /// </para>
    /// <para>
    /// <b><c>PointerUpdateKind</c>, not <c>IsMiddleButtonPressed</c>.</b> The latter reports button
    /// <em>state</em>: it is equally true for a left press made while the middle button happens to be held,
    /// and for every press during a middle drag. The question here is which button caused this press, and
    /// that is the one thing only <c>PointerUpdateKind</c> answers.
    /// </para>
    /// <para>
    /// On press rather than on release, which is what every browser and every terminal does. Matching a
    /// release to its press would need capture tracking, to buy the ability to change your mind about a
    /// middle click — a gesture nobody makes by accident and nobody aborts.
    /// </para>
    /// </remarks>
    private void OnTabPointerPressed(object? sender, PointerPressedEventArgs e)
    {
        if (sender is not Visual { DataContext: TerminalTabViewModel tab }
            || DataContext is not MainWindowViewModel shell)
        {
            return;
        }

        if (e.GetCurrentPoint((Visual)sender).Properties.PointerUpdateKind
            is not PointerUpdateKind.MiddleButtonPressed)
        {
            return;
        }

        // Handled, so the strip's ScrollViewer does not also take this as the start of a pan.
        e.Handled = true;

        // Fire-and-forget, as the host sidebar's double-tap connect is: CloseTabCommand is asynchronous —
        // it waits for the workspace to tear the session down — and an event handler has nowhere to await
        // it. Its failures are the workspace's to report, not this strip's.
        shell.CloseTabCommand.Execute(tab);
    }

    /// <summary>
    /// Opens the vault menu, on the Vaults tab.
    /// </summary>
    /// <remarks>
    /// <para>
    /// <b>The tab is selected before the menu opens, and that order is the whole reason this is a handler
    /// rather than <c>Button.Flyout</c>.</b> Selecting it puts the shell on a page, which collapses the
    /// renderer — so the popup never has to drop over the WebView's native child window, and the question
    /// this strip's comment refuses to answer without a screenshot does not come up. See the comment on the
    /// caret in the markup, and <c>docs/platform-flags.md</c> for what treating such a question as settled
    /// has already cost this project.
    /// </para>
    /// <para>
    /// It is also what a user expects. The caret belongs to the Vaults tab, so pressing it arriving at
    /// Vaults is the same gesture as pressing the tab, with a menu on the end.
    /// </para>
    /// </remarks>
    private void OnVaultMenuPressed(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not MainWindowViewModel shell || sender is not Control caret)
        {
            return;
        }

        shell.ShowVaultsCommand.Execute(null);

        FlyoutBase.ShowAttachedFlyout(caret);
    }

    /// <summary>Leaves for the teams screen with the new-vault form open, shutting the menu behind it.</summary>
    /// <remarks>
    /// The menu is closed first, because the command navigates and a flyout left open would be hanging over
    /// a screen it has nothing to do with. A <c>Flyout</c> does not close when something inside it is
    /// pressed — which is what the switches above it want, and not what this wants.
    /// </remarks>
    private void OnNewVaultPressed(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not MainWindowViewModel shell)
        {
            return;
        }

        if (this.FindControl<Button>("VaultMenu") is { } caret)
        {
            FlyoutBase.GetAttachedFlyout(caret)?.Hide();
        }

        shell.ShowNewVaultCommand.Execute(null);
    }
}
