using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Threading;
using AxCopilot.Models;
using AxCopilot.Services;

namespace AxCopilot.Core;

public class SnippetExpander
{
	private struct INPUT
	{
		public int type;

		public InputUnion u;
	}

	[StructLayout(LayoutKind.Explicit)]
	private struct InputUnion
	{
		[FieldOffset(0)]
		public MOUSEINPUT mi;

		[FieldOffset(0)]
		public KEYBDINPUT ki;

		[FieldOffset(0)]
		public HARDWAREINPUT hi;
	}

	private struct KEYBDINPUT
	{
		public ushort wVk;

		public ushort wScan;

		public uint dwFlags;

		public uint time;

		public nint dwExtraInfo;
	}

	private struct MOUSEINPUT
	{
		public int dx;

		public int dy;

		public uint mouseData;

		public uint dwFlags;

		public uint time;

		public nint dwExtraInfo;
	}

	private struct HARDWAREINPUT
	{
		public uint uMsg;

		public ushort wParamL;

		public ushort wParamH;
	}

	private readonly SettingsService _settings;

	private readonly StringBuilder _buffer = new StringBuilder();

	private bool _tracking;

	private const ushort VK_BACK = 8;

	private const int VK_ESCAPE = 27;

	private const int VK_SPACE = 32;

	private const int VK_RETURN = 13;

	private const int VK_OEM_1 = 186;

	private const int VK_SHIFT = 16;

	private const int VK_CONTROL = 17;

	private const int VK_MENU = 18;

	private const ushort VK_CTRL_US = 17;

	private static readonly HashSet<int> ClearKeys = new HashSet<int> { 33, 34, 35, 36, 37, 38, 39, 40, 46 };

	public SnippetExpander(SettingsService settings)
	{
		_settings = settings;
	}

	public bool HandleKey(int vkCode)
	{
		if (!_settings.Settings.Launcher.SnippetAutoExpand)
		{
			return false;
		}
		if ((GetAsyncKeyState(17) & 0x8000) != 0)
		{
			_tracking = false;
			_buffer.Clear();
			return false;
		}
		if ((GetAsyncKeyState(18) & 0x8000) != 0)
		{
			_tracking = false;
			_buffer.Clear();
			return false;
		}
		if (vkCode == 186 && (GetAsyncKeyState(16) & 0x8000) == 0)
		{
			_tracking = true;
			_buffer.Clear();
			_buffer.Append(';');
			return false;
		}
		if (!_tracking)
		{
			return false;
		}
		if ((vkCode >= 65 && vkCode <= 90) || (vkCode >= 48 && vkCode <= 57) || (vkCode >= 96 && vkCode <= 105) || vkCode == 189)
		{
			bool shifted = (GetAsyncKeyState(16) & 0x8000) != 0;
			char c = VkToChar(vkCode, shifted);
			if (c != 0)
			{
				_buffer.Append(char.ToLowerInvariant(c));
			}
			return false;
		}
		switch (vkCode)
		{
		case 8:
			if (_buffer.Length > 1)
			{
				_buffer.Remove(_buffer.Length - 1, 1);
			}
			else
			{
				_tracking = false;
				_buffer.Clear();
			}
			return false;
		default:
			if (vkCode != 13)
			{
				if (vkCode == 27 || ClearKeys.Contains(vkCode) || vkCode >= 112)
				{
					_tracking = false;
					_buffer.Clear();
				}
				return false;
			}
			goto case 32;
		case 32:
			if (_buffer.Length > 1)
			{
				string keyword = _buffer.ToString(1, _buffer.Length - 1);
				_tracking = false;
				_buffer.Clear();
				SnippetEntry snippetEntry = _settings.Settings.Snippets.FirstOrDefault((SnippetEntry s) => s.Key.Equals(keyword, StringComparison.OrdinalIgnoreCase));
				if (snippetEntry != null)
				{
					string expanded = ExpandVariables(snippetEntry.Content);
					int deleteCount = keyword.Length + 1;
					((DispatcherObject)Application.Current).Dispatcher.BeginInvoke((Delegate)(Action)delegate
					{
						PasteExpansion(expanded, deleteCount);
					}, Array.Empty<object>());
					return true;
				}
			}
			_tracking = false;
			_buffer.Clear();
			return false;
		}
	}

	private static void PasteExpansion(string text, int deleteCount)
	{
		try
		{
			INPUT[] array = new INPUT[deleteCount * 2];
			for (int i = 0; i < deleteCount; i++)
			{
				array[i * 2] = MakeKeyInput(8, keyUp: false);
				array[i * 2 + 1] = MakeKeyInput(8, keyUp: true);
			}
			SendInput((uint)array.Length, array, Marshal.SizeOf<INPUT>());
			Clipboard.SetText(text);
			INPUT[] array2 = new INPUT[4]
			{
				MakeKeyInput(17, keyUp: false),
				MakeKeyInput(86, keyUp: false),
				MakeKeyInput(86, keyUp: true),
				MakeKeyInput(17, keyUp: true)
			};
			SendInput((uint)array2.Length, array2, Marshal.SizeOf<INPUT>());
			LogService.Info($"스니펫 확장 완료: {deleteCount}자 삭제 후 붙여넣기");
		}
		catch (Exception ex)
		{
			LogService.Warn("스니펫 확장 실패: " + ex.Message);
		}
	}

	private static INPUT MakeKeyInput(ushort vk, bool keyUp)
	{
		INPUT result = new INPUT
		{
			type = 1
		};
		result.u.ki.wVk = vk;
		result.u.ki.dwFlags = (keyUp ? 2u : 0u);
		return result;
	}

	private static string ExpandVariables(string content)
	{
		DateTime now = DateTime.Now;
		return content.Replace("{date}", now.ToString("yyyy-MM-dd")).Replace("{time}", now.ToString("HH:mm:ss")).Replace("{datetime}", now.ToString("yyyy-MM-dd HH:mm:ss"))
			.Replace("{year}", now.Year.ToString())
			.Replace("{month}", now.Month.ToString("D2"))
			.Replace("{day}", now.Day.ToString("D2"));
	}

	private static char VkToChar(int vk, bool shifted)
	{
		if (vk >= 65 && vk <= 90)
		{
			return shifted ? ((char)vk) : char.ToLowerInvariant((char)vk);
		}
		if (vk >= 48 && vk <= 57)
		{
			return shifted ? ")!@#$%^&*("[vk - 48] : ((char)vk);
		}
		if (vk >= 96 && vk <= 105)
		{
			return (char)(48 + (vk - 96));
		}
		if (vk == 189)
		{
			return shifted ? '_' : '-';
		}
		return '\0';
	}

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

	[DllImport("user32.dll", SetLastError = true)]
	private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
}
