using Insight.Remote.Shared.Abstractions;
using Insight.Remote.Shared.Messages;
using Insight.Remote.Shared.Native.Windows;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
using Vaitr.Bus;
using Clipboard = System.Windows.Clipboard;

namespace Insight.Remote.Windows.Services;

internal class WinClipboardProvider : IClipboardProvider
{
    private readonly ConcurrentQueue<string> _setQueue = new();

    private readonly Bus _bus;
    private readonly ILogger<WinClipboardProvider> _logger;

    public WinClipboardProvider(Bus bus, ILogger<WinClipboardProvider> logger)
    {
        _bus = bus;
        _logger = logger;
    }

    public async Task InitAsync(CancellationToken cancellationToken)
    {
        var thread = new Thread(async () =>
        {
            string? lastClipboardValue = null;

            while (cancellationToken.IsCancellationRequested is false)
            {
                try
                {
                    if (_setQueue.TryDequeue(out var item))
                    {
                        //_logger.LogCritical("set clipboard");

                        if (string.IsNullOrWhiteSpace(item)) Clipboard.Clear();
                        else Clipboard.SetText(item);
                    }
                    else
                    {
                        // _logger.LogCritical("get clipboard");

                        Win32Interop.SwitchToInputDesktop();

                        if (Clipboard.ContainsText() && Clipboard.GetText() != lastClipboardValue)
                        {
                            lastClipboardValue = Clipboard.GetText();

                            await _bus.PublishAsync(new ClipboardChanged(lastClipboardValue), cancellationToken);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error while setting clipboard text.");
                }
                finally
                {
                    Thread.Sleep(1000);
                }
            }
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();

        while (cancellationToken.IsCancellationRequested is false)
        {
            try
            {
                if (thread.IsAlive)
                {
                    await Task.Delay(1000, cancellationToken);
                }
            }
            catch (TaskCanceledException) { }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
            }
        }
    }

    public Task SetText(string clipboardText)
    {
        _setQueue.Enqueue(clipboardText);
        return Task.CompletedTask;
    }
}