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

namespace AxCopilot.Handlers;

public class SnippetHandler : IActionHandler
{
	private struct INPUT
	{
		public uint type;

		public InputUnion u;
	}

	[StructLayout(LayoutKind.Explicit)]
	private struct InputUnion
	{
		[FieldOffset(0)]
		public KEYBDINPUT ki;
	}

	private struct KEYBDINPUT
	{
		public ushort wVk;

		public ushort wScan;

		public uint dwFlags;

		public uint time;

		public nint dwExtraInfo;
	}

	private readonly SettingsService _settings;

	public string? Prefix => ";";

	public PluginMetadata Metadata => new PluginMetadata("Snippets", "텍스트 스니펫 — ; 뒤에 키워드 입력", "1.0", "AX");

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

	public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
	{
		List<SnippetEntry> snippets = _settings.Settings.Snippets;
		if (!snippets.Any())
		{
			return Task.FromResult((IEnumerable<LauncherItem>)new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("등록된 스니펫이 없습니다", "설정 → 스니펫 탭에서 추가하세요", null, null, null, "\ue70b")));
		}
		string q = query.Trim().ToLowerInvariant();
		List<LauncherItem> list = (from s in snippets
			where string.IsNullOrEmpty(q) || s.Key.ToLowerInvariant().Contains(q) || s.Name.ToLowerInvariant().Contains(q)
			select new LauncherItem((s.Name.Length > 0) ? s.Name : s.Key, TruncatePreview(s.Content), null, s, null, "\ue70b")).ToList();
		if (list.Count == 0)
		{
			list.Add(new LauncherItem("'" + query + "'에 해당하는 스니펫 없음", "설정에서 새 스니펫을 추가하세요", null, null, null, "\ue70b"));
		}
		return Task.FromResult((IEnumerable<LauncherItem>)list);
	}

	public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
	{
		if (!(item.Data is SnippetEntry snippetEntry))
		{
			return Task.CompletedTask;
		}
		try
		{
			string text = ExpandVariables(snippetEntry.Content);
			Clipboard.SetText(text);
			Task.Delay(100, ct).ContinueWith(delegate
			{
				((DispatcherObject)Application.Current).Dispatcher.Invoke((Action)delegate
				{
					INPUT[] array = new INPUT[4]
					{
						new INPUT
						{
							type = 1u,
							u = new InputUnion
							{
								ki = new KEYBDINPUT
								{
									wVk = 17
								}
							}
						},
						new INPUT
						{
							type = 1u,
							u = new InputUnion
							{
								ki = new KEYBDINPUT
								{
									wVk = 86
								}
							}
						},
						new INPUT
						{
							type = 1u,
							u = new InputUnion
							{
								ki = new KEYBDINPUT
								{
									wVk = 86,
									dwFlags = 2u
								}
							}
						},
						new INPUT
						{
							type = 1u,
							u = new InputUnion
							{
								ki = new KEYBDINPUT
								{
									wVk = 17,
									dwFlags = 2u
								}
							}
						}
					};
					SendInput((uint)array.Length, array, Marshal.SizeOf<INPUT>());
				});
			}, ct, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
		}
		catch (Exception ex)
		{
			LogService.Warn("스니펫 실행 실패: " + ex.Message);
		}
		return Task.CompletedTask;
	}

	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 string TruncatePreview(string content)
	{
		string text = content.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ");
		return (text.Length > 60) ? (text.Substring(0, 57) + "…") : text;
	}

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