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.Media.Imaging;
using AxCopilot.SDK;
using AxCopilot.Services;

namespace AxCopilot.Handlers;

public class ClipboardHistoryHandler : IActionHandler
{
	[StructLayout(LayoutKind.Explicit, Size = 40)]
	private struct INPUT
	{
		[FieldOffset(0)]
		public uint Type;

		[FieldOffset(8)]
		public KEYBDINPUT ki;
	}

	private struct KEYBDINPUT
	{
		public ushort wVk;

		public ushort wScan;

		public uint dwFlags;

		public uint time;

		public nint dwExtraInfo;
	}

	private readonly ClipboardHistoryService _historyService;

	private static readonly Dictionary<string, string> CategoryFilters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
	{
		{ "url", "URL" },
		{ "코드", "코드" },
		{ "code", "코드" },
		{ "경로", "경로" },
		{ "path", "경로" },
		{ "핀", "핀" },
		{ "pin", "핀" }
	};

	public string? Prefix => "#";

	public PluginMetadata Metadata => new PluginMetadata("ClipboardHistory", "클립보드 히스토리 — # 뒤에 검색어 (또는 빈 입력으로 전체 보기)", "1.0", "AX");

	public ClipboardHistoryHandler(ClipboardHistoryService historyService)
	{
		_historyService = historyService;
	}

	public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
	{
		IReadOnlyList<ClipboardEntry> history = _historyService.History;
		if (history.Count == 0)
		{
			return Task.FromResult((IEnumerable<LauncherItem>)new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("클립보드 히스토리가 없습니다", "텍스트를 복사하면 이 곳에 기록됩니다", null, null, null, "\ue81c")));
		}
		string q = query.Trim().ToLowerInvariant();
		string catFilter = null;
		foreach (var (text3, text4) in CategoryFilters)
		{
			if (q == text3 || q.StartsWith(text3 + " "))
			{
				catFilter = text4;
				object obj;
				if (q.Length <= text3.Length)
				{
					obj = "";
				}
				else
				{
					string text5 = q;
					int num = text3.Length + 1;
					obj = text5.Substring(num, text5.Length - num).Trim();
				}
				q = (string)obj;
				break;
			}
		}
		IEnumerable<ClipboardEntry> source = history.AsEnumerable();
		if (catFilter == "핀")
		{
			source = source.Where((ClipboardEntry e) => e.IsPinned);
		}
		else if (catFilter != null)
		{
			source = source.Where((ClipboardEntry e) => e.Category == catFilter);
		}
		if (!string.IsNullOrEmpty(q))
		{
			source = source.Where((ClipboardEntry e) => e.Preview.ToLowerInvariant().Contains(q));
		}
		IOrderedEnumerable<ClipboardEntry> source2 = from e in source
			orderby e.IsPinned descending, e.CopiedAt descending
			select e;
		List<LauncherItem> list = source2.Select(delegate(ClipboardEntry e)
		{
			string text6 = (e.IsPinned ? "\ud83d\udccc " : "");
			string value = ((e.Category != "일반") ? ("[" + e.Category + "] ") : "");
			return new LauncherItem(text6 + e.Preview, $"{value}{e.RelativeTime}  ·  {e.CopiedAt:MM/dd HH:mm}", null, e, null, e.IsPinned ? "\ue728" : (e.IsText ? "\ue81c" : "\ueb9f"));
		}).ToList();
		if (list.Count == 0)
		{
			list.Add(new LauncherItem("'" + query + "'에 해당하는 항목 없음", "#pin #url #코드 #경로 로 필터링 가능", null, null, null, "\ue81c"));
		}
		return Task.FromResult((IEnumerable<LauncherItem>)list);
	}

	public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
	{
		object data = item.Data;
		if (!(data is ClipboardEntry entry))
		{
			return;
		}
		try
		{
			_historyService.SuppressNextCapture();
			_historyService.PromoteEntry(entry);
			if (!entry.IsText && entry.Image != null)
			{
				BitmapSource originalImg = ClipboardHistoryService.LoadOriginalImage(entry.OriginalImagePath);
				Clipboard.SetImage(originalImg ?? entry.Image);
			}
			else if (!string.IsNullOrEmpty(entry.Text))
			{
				Clipboard.SetText(entry.Text);
				nint prevWindow = WindowTracker.PreviousWindow;
				if (prevWindow != IntPtr.Zero)
				{
					await Task.Delay(300, ct);
					uint lpdwProcessId;
					uint targetThread = GetWindowThreadProcessId(prevWindow, out lpdwProcessId);
					uint currentThread = GetCurrentThreadId();
					AttachThreadInput(currentThread, targetThread, fAttach: true);
					SetForegroundWindow(prevWindow);
					AttachThreadInput(currentThread, targetThread, fAttach: false);
					await Task.Delay(100, ct);
					SendCtrlV();
				}
			}
		}
		catch (OperationCanceledException)
		{
		}
		catch (Exception ex2)
		{
			LogService.Warn("클립보드 히스토리 붙여넣기 실패: " + ex2.Message);
		}
	}

	private static void SendCtrlV()
	{
		INPUT[] array = new INPUT[4];
		array[0].Type = 1u;
		array[0].ki.wVk = 17;
		array[1].Type = 1u;
		array[1].ki.wVk = 86;
		array[2].Type = 1u;
		array[2].ki.wVk = 86;
		array[2].ki.dwFlags = 2u;
		array[3].Type = 1u;
		array[3].ki.wVk = 17;
		array[3].ki.dwFlags = 2u;
		SendInput((uint)array.Length, array, Marshal.SizeOf<INPUT>());
	}

	[DllImport("user32.dll")]
	private static extern bool SetForegroundWindow(nint hWnd);

	[DllImport("user32.dll")]
	private static extern uint GetWindowThreadProcessId(nint hWnd, out uint lpdwProcessId);

	[DllImport("user32.dll")]
	private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, [MarshalAs(UnmanagedType.Bool)] bool fAttach);

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

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