using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;

using DodoSSH.Client.Android.Platform;
using DodoSSH.Client.Shell.ViewModels;

namespace DodoSSH.Client.Android.Views;

/// <summary>Designs 06 and 08 — one remote pane, over SFTP or over a bucket.</summary>
internal sealed partial class FilesScreen : UserControl
{
    public FilesScreen() => AvaloniaXamlLoader.Load(this);

    /// <summary>
    /// Opens a directory on a single tap.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The desktop pane opens on a double tap and on Enter. A phone has neither, so the gesture has to be
    /// the one gesture there is — and that is why this is here rather than in the shared view model: making
    /// selection navigate would give the desktop a single click that walks into a directory.
    /// </para>
    /// <para>
    /// <see cref="InputElement.Tapped"/> rather than a command on the row, because the row must stay a
    /// plain <see cref="Border"/>: a button as the item template swallows the press before the list sees it
    /// — it marks the pointer event handled, and the list's own handler is not registered for handled
    /// events — leaving nothing selected and every command that reads the selection doing nothing. Tapped
    /// fires after the list has moved its selection, which is what lets this read it.
    /// </para>
    /// <para>
    /// A file is left selected rather than downloaded, and that is deliberate now rather than forced: the
    /// actions below act on the selection, and a tap that started a transfer would make selecting a row to
    /// read its size the same gesture as fetching it.
    /// </para>
    /// </remarks>
    private void OnRemoteEntryTapped(object? sender, TappedEventArgs e)
    {
        if (DataContext is TransfersViewModel { SelectedRemoteEntry.IsNavigable: true } transfers)
        {
            transfers.OpenRemoteCommand.Execute(null);
        }
    }

    /// <summary>
    /// Picks documents in the system picker and queues them for upload.
    /// </summary>
    /// <remarks>
    /// <para>
    /// In the head rather than in the shared view model, for the reason every other platform difference is:
    /// the picker is Android's, the staging directory is this application's cache, and the desktop reaches
    /// its local files by browsing a pane that does not exist here. What crosses back into shared code is
    /// what the queue understands — paths — through <see cref="TransfersViewModel.QueueStagedUploads"/>.
    /// </para>
    /// <para>
    /// <b>Failures land in the screen's own status line</b>, which is where every other refusal on this
    /// screen already is. The picker itself is a trip out to another application, and it can come back with
    /// a document that has since been deleted or a grant that was revoked; the exception's message is more
    /// use than "the upload failed", and a phone has nowhere else to put it.
    /// </para>
    /// </remarks>
    private async void OnAddFiles(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not TransfersViewModel transfers || TopLevel.GetTopLevel(this) is not { } top)
        {
            return;
        }

        try
        {
            var staged = await DocumentStaging.PickAsync(top, CancellationToken.None).ConfigureAwait(true);

            if (staged.Count == 0)
            {
                return;
            }

            transfers.QueueStagedUploads(staged);
        }
        catch (Exception exception) when (exception is not OutOfMemoryException)
        {
            transfers.Status = $"Those files could not be read: {exception.Message}";
        }
    }

    /// <summary>
    /// Asks where the chosen remote file should be saved, then queues it.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The other direction, and the asymmetry with <see cref="OnAddFiles"/> is the platform's rather than
    /// this screen's: coming in, several documents can be pointed at in one trip; going out, the save picker
    /// names one destination, so this acts on the selected row. Asking for five destinations in a row to
    /// download five files would be a worse screen than pressing the button five times.
    /// </para>
    /// <para>
    /// Nothing is queued when the picker is dismissed. The document it makes when it is *not* dismissed
    /// exists from that moment, which is why the queueing follows immediately — see
    /// <c>DocumentStaging.PickDestinationAsync</c>.
    /// </para>
    /// </remarks>
    private async void OnSaveFile(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not TransfersViewModel transfers || TopLevel.GetTopLevel(this) is not { } top)
        {
            return;
        }

        if (transfers.SelectedRemoteEntry is not { IsFile: true } row)
        {
            transfers.Status = "Choose a file on the host to save.";
            return;
        }

        try
        {
            if (await DocumentStaging.PickDestinationAsync(top, row.Name).ConfigureAwait(true)
                is not { } destination)
            {
                return;
            }

            transfers.QueueDeliveredDownload(
                row,
                DocumentStaging.NewStagingPath(row.Name),
                path => DocumentStaging.DeliverAsync(destination, path));
        }
        catch (Exception exception) when (exception is not OutOfMemoryException)
        {
            transfers.Status = $"That file could not be saved: {exception.Message}";
        }
    }
}
