using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Olive.Helpers;
using Olive.Klipy;
using Olive.Services;

namespace Olive.Commands;

internal sealed partial class CopyGifCommand : InvokableCommand
{
  private static readonly SemaphoreSlim CopyPasteGate = new(1, 1);

  private readonly KlipyGif _gif;
  private readonly SettingsManager _settingsManager;

  public CopyGifCommand(KlipyGif gif, SettingsManager settingsManager)
  {
    _gif = gif;
    _settingsManager = settingsManager;
    Name = "Copy GIF";
    Icon = new IconInfo("\uE8C8");
  }

  public override ICommandResult Invoke()
  {
    var closeAfterCopy = _settingsManager.CloseAfterCopy;
    var pasteAfterCopy = _settingsManager.PasteAfterCopy;

    _ = CopyAsync(closeAfterCopy, pasteAfterCopy);
    return closeAfterCopy ? CommandResult.Dismiss() : CommandResult.KeepOpen();
  }

  private async Task CopyAsync(bool closeAfterCopy, bool pasteAfterCopy)
  {
    using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
    if (!await CopyPasteGate.WaitAsync(0, cancellation.Token).ConfigureAwait(false))
    {
      ShowStatus("A GIF is already being copied.", MessageState.Warning, 2000);
      return;
    }

    try
    {
      var targetWindowTask = CapturePasteTargetAsync(closeAfterCopy, pasteAfterCopy, cancellation.Token);

      ShowStatus("Downloading GIF...", MessageState.Info, 800);
      var downloadTask = KlipyClient.DownloadGifBytesAsync(_gif.GifUrl, cancellation.Token);

      var bytes = await downloadTask.ConfigureAwait(false);
      var targetWindow = await targetWindowTask.ConfigureAwait(false);
      await ClipboardService.CopyGifBytesAsync(bytes, cancellation.Token).ConfigureAwait(false);

      if (pasteAfterCopy)
        await PasteShortcutService.SendPasteAfterRestoringFocusAsync(targetWindow, cancellation.Token)
          .ConfigureAwait(false);
      else
        ShowStatus("GIF copied - paste with Ctrl + V", MessageState.Success, 3500);
    }
    catch (OperationCanceledException)
    {
      ShowStatus("Copy cancelled or timed out.", MessageState.Warning, 2500);
    }
    catch (Exception ex) when (ex is HttpRequestException or IOException or InvalidOperationException
                                 or UnauthorizedAccessException)
    {
      ShowStatus("Could not copy this GIF.", MessageState.Error, 3500);
    }
    finally
    {
      CopyPasteGate.Release();
    }
  }

  private static async Task<nint> CapturePasteTargetAsync(bool closeAfterCopy, bool pasteAfterCopy,
    CancellationToken cancellationToken)
  {
    if (!pasteAfterCopy) return 0;
    if (closeAfterCopy) await Task.Delay(150, cancellationToken).ConfigureAwait(false);
    return PasteShortcutService.GetForegroundWindowHandle();
  }

  private static void ShowStatus(string message, MessageState state, int duration)
  {
    new ToastStatusMessage(new StatusMessage { Message = message, State = state }) { Duration = duration }.Show();
  }
}
