using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace Hua.Todo.Avalonia.Services.Platforms;

/// <summary>
/// Windows 平台全局热键服务实现。
/// 通过 <c>RegisterHotKey</c> 将热键注册到专用线程的消息队列中，避免依赖窗口句柄与 WndProc Hook。
/// </summary>
public sealed class WindowsGlobalHotKeyService : IGlobalHotKeyService, IDisposable
{
    private const int HotKeyId = 9000;
    private const uint WmHotKey = 0x0312;
    private const uint WmQuit = 0x0012;
    private const uint WmAppExecute = 0x8001;

    public const uint ModAlt = 0x0001;
    public const uint ModControl = 0x0002;
    public const uint ModShift = 0x0004;
    public const uint ModWin = 0x0008;

    private readonly ConcurrentDictionary<int, Action> _actions = new();
    private int _actionId;
    private Thread? _thread;
    private uint _threadId;
    private ManualResetEventSlim? _threadStarted;

    private Action? _callback;
    private bool _isRegistered;
    private uint _currentModifiers;
    private uint _currentKey;
    private bool _isDisposed;

    /// <inheritdoc />
    public bool IsSupported => OperatingSystem.IsWindows();

    /// <inheritdoc />
    public void RegisterHotKey(string modifiers, string key, Action callback)
    {
        if (!IsSupported) return;

        _callback = callback;
        _currentModifiers = ParseModifiers(modifiers);
        _currentKey = ParseKey(key);

        StartThreadIfNeeded();
        InvokeOnHotKeyThread(() =>
        {
            UnregisterHotKeyInternal();

            if (_currentKey == 0)
            {
                return;
            }

            if (RegisterHotKey(IntPtr.Zero, HotKeyId, _currentModifiers, _currentKey))
            {
                _isRegistered = true;
            }
            else
            {
                Debug.WriteLine("[WindowsGlobalHotKeyService] Failed to register hotkey.");
            }
        });
    }

    /// <inheritdoc />
    public void UnregisterHotKey()
    {
        if (!IsSupported) return;

        StartThreadIfNeeded();
        InvokeOnHotKeyThread(UnregisterHotKeyInternal);
    }

    /// <inheritdoc />
    public void UpdateHotKey(string modifiers, string key)
    {
        if (_callback == null) return;
        RegisterHotKey(modifiers, key, _callback);
    }

    private void UnregisterHotKeyInternal()
    {
        if (_isRegistered)
        {
            UnregisterHotKey(IntPtr.Zero, HotKeyId);
            _isRegistered = false;
        }
    }

    private void StartThreadIfNeeded()
    {
        if (_thread != null) return;

        _threadStarted = new ManualResetEventSlim(false);
        _thread = new Thread(MessageLoop)
        {
            IsBackground = true,
            Name = "Hua.Todo HotKey Thread"
        };
        if (OperatingSystem.IsWindows())
        {
            _thread.SetApartmentState(ApartmentState.STA);
        }
        _thread.Start();
        _threadStarted.Wait();
    }

    private void MessageLoop()
    {
        _threadId = GetCurrentThreadId();
        _threadStarted?.Set();

        while (GetMessage(out var msg, IntPtr.Zero, 0, 0) != 0)
        {
            if (msg.message == WmHotKey && msg.wParam == (UIntPtr)HotKeyId)
            {
                try
                {
                    _callback?.Invoke();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"[WindowsGlobalHotKeyService] Hotkey callback failed: {ex}");
                }
                continue;
            }

            if (msg.message == WmAppExecute)
            {
                var id = unchecked((int)msg.wParam);
                if (_actions.TryRemove(id, out var action))
                {
                    try
                    {
                        action();
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine($"[WindowsGlobalHotKeyService] Action execution failed: {ex}");
                    }
                }
                continue;
            }

            if (msg.message == WmQuit)
            {
                break;
            }

            TranslateMessage(ref msg);
            DispatchMessage(ref msg);
        }

        _threadStarted?.Dispose();
        _threadStarted = null;
    }

    private void InvokeOnHotKeyThread(Action action)
    {
        var id = Interlocked.Increment(ref _actionId);
        _actions[id] = action;

        if (!PostThreadMessage(_threadId, WmAppExecute, (UIntPtr)id, IntPtr.Zero))
        {
            _actions.TryRemove(id, out _);
        }
    }

    private static uint ParseModifiers(string modifiers)
    {
        uint mod = 0;
        if (string.IsNullOrWhiteSpace(modifiers)) return mod;

        var parts = modifiers.Split(',');
        foreach (var part in parts)
        {
            var p = part.Trim();
            if (p.Equals("Control", StringComparison.OrdinalIgnoreCase)) mod |= ModControl;
            if (p.Equals("Alt", StringComparison.OrdinalIgnoreCase)) mod |= ModAlt;
            if (p.Equals("Shift", StringComparison.OrdinalIgnoreCase)) mod |= ModShift;
            if (p.Equals("Windows", StringComparison.OrdinalIgnoreCase)) mod |= ModWin;
        }

        return mod;
    }

    private static uint ParseKey(string key)
    {
        if (string.IsNullOrWhiteSpace(key)) return 0;

        if (key.Length == 1)
        {
            var c = char.ToUpperInvariant(key[0]);
            if (c is >= 'A' and <= 'Z') return c;
            if (c is >= '0' and <= '9') return c;
        }

        return 0x58; // Default 'X'
    }

    /// <inheritdoc />
    public void Dispose()
    {
        if (_isDisposed) return;
        _isDisposed = true;

        try
        {
            UnregisterHotKey();
        }
        catch
        {
        }

        if (_threadId != 0)
        {
            PostThreadMessage(_threadId, WmQuit, UIntPtr.Zero, IntPtr.Zero);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct Msg
    {
        public IntPtr hwnd;
        public uint message;
        public UIntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public POINT pt;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    }

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    [DllImport("user32.dll")]
    private static extern sbyte GetMessage(out Msg lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);

    [DllImport("user32.dll")]
    private static extern bool TranslateMessage(ref Msg lpMsg);

    [DllImport("user32.dll")]
    private static extern IntPtr DispatchMessage(ref Msg lpMsg);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool PostThreadMessage(uint idThread, uint msg, UIntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll")]
    private static extern uint GetCurrentThreadId();
}
