using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using AxCopilot.Services;

namespace AxCopilot.Core;

public class InputListener : IDisposable
{
	private delegate nint LowLevelKeyboardProc(int nCode, nint wParam, nint lParam);

	private const int WH_KEYBOARD_LL = 13;

	private const int WM_KEYDOWN = 256;

	private const int WM_SYSKEYDOWN = 260;

	private const int WM_KEYUP = 257;

	private const int WM_SYSKEYUP = 261;

	private const int VK_SHIFT = 16;

	private const int VK_CONTROL = 17;

	private const int VK_MENU = 18;

	private const int VK_LWIN = 91;

	private const int VK_RWIN = 92;

	private nint _hookHandle = IntPtr.Zero;

	private LowLevelKeyboardProc? _proc;

	private int _retryCount = 0;

	private const int MaxRetry = 3;

	private volatile bool _suppressNextAltUp;

	private volatile bool _suppressNextKeyUp;

	private volatile int _suppressKeyUpVk;

	private HotkeyDefinition _hotkey = new HotkeyDefinition(32, Ctrl: false, Alt: true, Shift: false, Win: false);

	private HotkeyDefinition _captureHotkey;

	private bool _captureHotkeyEnabled;

	public bool SuspendHotkey { get; set; }

	public Func<int, bool>? KeyFilter { get; set; }

	public event EventHandler? HotkeyTriggered;

	public event EventHandler? CaptureHotkeyTriggered;

	public event EventHandler? HookFailed;

	public void UpdateHotkey(string hotkeyStr)
	{
		if (HotkeyParser.TryParse(hotkeyStr, out var result))
		{
			_hotkey = result;
			LogService.Info("핫키 변경: " + hotkeyStr);
		}
		else
		{
			LogService.Warn("핫키 파싱 실패: '" + hotkeyStr + "' — 기존 핫키 유지");
		}
	}

	public void UpdateCaptureHotkey(string hotkeyStr, bool enabled)
	{
		_captureHotkeyEnabled = enabled;
		if (enabled && HotkeyParser.TryParse(hotkeyStr, out var result))
		{
			_captureHotkey = result;
			LogService.Info("캡처 단축키 활성화: " + hotkeyStr);
		}
		else if (!enabled)
		{
			LogService.Info("캡처 단축키 비활성화");
		}
	}

	public void Start()
	{
		_proc = HookCallback;
		Register();
	}

	private void Register()
	{
		using Process process = Process.GetCurrentProcess();
		using ProcessModule processModule = process.MainModule;
		_hookHandle = SetWindowsHookEx(13, _proc, GetModuleHandle(processModule.ModuleName), 0u);
		if (_hookHandle == IntPtr.Zero)
		{
			int lastWin32Error = Marshal.GetLastWin32Error();
			LogService.Error($"Global Hook 등록 실패 (에러 코드: {lastWin32Error})");
			TryRetryRegister();
		}
		else
		{
			_retryCount = 0;
			LogService.Info("Global Keyboard Hook 등록 완료 (" + HotkeyParser.Format(_hotkey) + ")");
		}
	}

	private void TryRetryRegister()
	{
		if (_retryCount < 3)
		{
			_retryCount++;
			LogService.Warn($"Hook 재등록 시도 {_retryCount}/{3}");
			Task.Delay(1000).ContinueWith(delegate
			{
				Register();
			});
		}
		else
		{
			LogService.Error("Hook 재등록 최대 횟수 초과");
			this.HookFailed?.Invoke(this, EventArgs.Empty);
		}
	}

	private static bool IsSuppressedForegroundWindow()
	{
		nint foregroundWindow = GetForegroundWindow();
		if (foregroundWindow == IntPtr.Zero)
		{
			return false;
		}
		StringBuilder stringBuilder = new StringBuilder(64);
		GetClassName(foregroundWindow, stringBuilder, 64);
		string text = stringBuilder.ToString();
		return text == "#32770" || text == "SunAwtDialog";
	}

	private nint HookCallback(int nCode, nint wParam, nint lParam)
	{
		if (nCode < 0)
		{
			return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
		}
		int num = Marshal.ReadInt32(lParam);
		if (wParam == 257 || wParam == 261)
		{
			if (_suppressNextAltUp && num == 18)
			{
				_suppressNextAltUp = false;
				return 1;
			}
			if (_suppressNextKeyUp && num == _suppressKeyUpVk)
			{
				_suppressNextKeyUp = false;
				return 1;
			}
			return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
		}
		if (wParam != 256 && wParam != 260)
		{
			return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
		}
		if (IsSuppressedForegroundWindow())
		{
			return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
		}
		if (!SuspendHotkey && num == _hotkey.VkCode)
		{
			bool flag = !_hotkey.Ctrl || (GetAsyncKeyState(17) & 0x8000) != 0;
			bool flag2 = !_hotkey.Alt || (GetAsyncKeyState(18) & 0x8000) != 0;
			bool flag3 = !_hotkey.Shift || (GetAsyncKeyState(16) & 0x8000) != 0;
			bool flag4 = !_hotkey.Win || (GetAsyncKeyState(91) & 0x8000) != 0 || (GetAsyncKeyState(92) & 0x8000) != 0;
			if (flag && flag2 && flag3 && flag4)
			{
				this.HotkeyTriggered?.Invoke(this, EventArgs.Empty);
				_suppressNextKeyUp = true;
				_suppressKeyUpVk = num;
				if (_hotkey.Alt)
				{
					_suppressNextAltUp = true;
				}
				return 1;
			}
		}
		if (!SuspendHotkey && _captureHotkeyEnabled && num == _captureHotkey.VkCode)
		{
			bool flag5 = !_captureHotkey.Ctrl || (GetAsyncKeyState(17) & 0x8000) != 0;
			bool flag6 = !_captureHotkey.Alt || (GetAsyncKeyState(18) & 0x8000) != 0;
			bool flag7 = !_captureHotkey.Shift || (GetAsyncKeyState(16) & 0x8000) != 0;
			bool flag8 = !_captureHotkey.Win || (GetAsyncKeyState(91) & 0x8000) != 0 || (GetAsyncKeyState(92) & 0x8000) != 0;
			if (flag5 && flag6 && flag7 && flag8)
			{
				this.CaptureHotkeyTriggered?.Invoke(this, EventArgs.Empty);
				_suppressNextKeyUp = true;
				_suppressKeyUpVk = num;
				if (_captureHotkey.Alt)
				{
					_suppressNextAltUp = true;
				}
				return 1;
			}
		}
		Func<int, bool>? keyFilter = KeyFilter;
		if (keyFilter != null && keyFilter(num))
		{
			return 1;
		}
		return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
	}

	public void Dispose()
	{
		if (_hookHandle != IntPtr.Zero)
		{
			UnhookWindowsHookEx(_hookHandle);
			_hookHandle = IntPtr.Zero;
		}
	}

	[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
	private static extern nint SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, nint hMod, uint dwThreadId);

	[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
	[return: MarshalAs(UnmanagedType.Bool)]
	private static extern bool UnhookWindowsHookEx(nint hhk);

	[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
	private static extern nint CallNextHookEx(nint hhk, int nCode, nint wParam, nint lParam);

	[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
	private static extern nint GetModuleHandle(string lpModuleName);

	[DllImport("user32.dll")]
	private static extern short GetAsyncKeyState(int vKey);

	[DllImport("user32.dll")]
	private static extern nint GetForegroundWindow();

	[DllImport("user32.dll", CharSet = CharSet.Unicode)]
	private static extern int GetClassName(nint hWnd, StringBuilder lpClassName, int nMaxCount);
}
