using System.Runtime.InteropServices;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// 클립보드 히스토리 핸들러. "#" 프리픽스로 사용합니다.
/// 예: # (빈 쿼리) → 최근 클립보드 목록
///     # hello     → "hello"가 포함된 항목 필터
/// </summary>
public class ClipboardHistoryHandler : IActionHandler
{
    private readonly ClipboardHistoryService _historyService;

    public string? Prefix => "#";

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

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

    // 카테고리 필터 프리픽스: #url, #코드, #경로
    private static readonly Dictionary<string, string> CategoryFilters = new(StringComparer.OrdinalIgnoreCase)
    {
        { "url", "URL" }, { "코드", "코드" }, { "code", "코드" },
        { "경로", "경로" }, { "path", "경로" }, { "핀", "핀" }, { "pin", "핀" },
    };

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var history = _historyService.History;

        if (history.Count == 0)
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(
            [
                new LauncherItem(
                    "클립보드 히스토리가 없습니다",
                    "텍스트를 복사하면 이 곳에 기록됩니다",
                    null, null, Symbol: Symbols.History)
            ]);
        }

        var q = query.Trim().ToLowerInvariant();

        // 카테고리 필터 감지 (예: #url, #핀)
        string? catFilter = null;
        foreach (var (prefix, cat) in CategoryFilters)
        {
            if (q == prefix || q.StartsWith(prefix + " "))
            {
                catFilter = cat;
                q = q.Length > prefix.Length ? q[(prefix.Length + 1)..].Trim() : "";
                break;
            }
        }

        var filtered = history.AsEnumerable();

        // 카테고리 필터 적용
        if (catFilter == "핀")
            filtered = filtered.Where(e => e.IsPinned);
        else if (catFilter != null)
            filtered = filtered.Where(e => e.Category == catFilter);

        // 텍스트 검색 — 텍스트 내용, Preview, OCR 추출 텍스트 모두 포함 (Phase L2-4)
        if (!string.IsNullOrEmpty(q))
            filtered = filtered.Where(e =>
                e.Preview.ToLowerInvariant().Contains(q) ||
                (e.OcrText != null && e.OcrText.ToLowerInvariant().Contains(q)));

        // 핀 항목을 상단에 배치
        var sorted = filtered
            .OrderByDescending(e => e.IsPinned)
            .ThenByDescending(e => e.CopiedAt);

        var items = sorted
            .Select(e =>
            {
                var pinMark = e.IsPinned ? "\uD83D\uDCCC " : "";
                var catMark = e.Category != "일반" ? $"[{e.Category}] " : "";
                // 이미지 항목에 OCR 인덱스 완료 표시
                var ocrMark = !e.IsText && e.OcrText != null ? "OCR · " : "";
                return new LauncherItem(
                    $"{pinMark}{e.Preview}",
                    $"{catMark}{ocrMark}{e.RelativeTime}  ·  {e.CopiedAt:MM/dd HH:mm}",
                    null,
                    e,
                    Symbol: e.IsPinned ? Symbols.Favorite : (e.IsText ? Symbols.History : Symbols.Picture));
            })
            .ToList();

        if (items.Count == 0)
        {
            items.Add(new LauncherItem(
                $"'{query}'에 해당하는 항목 없음",
                "#pin #url #코드 #경로 로 필터링 가능",
                null, null, Symbol: Symbols.History));
        }

        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is not ClipboardEntry entry) return;

        try
        {
            _historyService.SuppressNextCapture();
            _historyService.PromoteEntry(entry); // 사용 시각 갱신 + 목록 맨 위로

            if (!entry.IsText && entry.Image != null)
            {
                // 원본 이미지가 있으면 원본 해상도로 클립보드 복사
                var originalImg = ClipboardHistoryService.LoadOriginalImage(entry.OriginalImagePath);
                Clipboard.SetImage(originalImg ?? entry.Image);
                return; // 이미지는 붙여넣기 시뮬레이션 없이 클립보드만 설정
            }

            if (string.IsNullOrEmpty(entry.Text)) return;
            Clipboard.SetText(entry.Text);

            var prevWindow = WindowTracker.PreviousWindow;
            if (prevWindow == IntPtr.Zero) return;

            // ── 이전 창 포커스 복원 후 Ctrl+V ────────────────────────────────
            // 런처 창이 완전히 숨겨지고 이전 창이 포커스를 회복할 시간 확보
            await Task.Delay(300, ct);

            // AttachThreadInput으로 포그라운드 전환 권한 획득 후 SetForegroundWindow 호출
            // (Alt 트릭 대비: Alt 키 주입 없이 안정적으로 전환, 대상 앱 메뉴 트리거 방지)
            var targetThread = GetWindowThreadProcessId(prevWindow, out _);
            var currentThread = GetCurrentThreadId();
            AttachThreadInput(currentThread, targetThread, true);
            SetForegroundWindow(prevWindow);
            AttachThreadInput(currentThread, targetThread, false);

            // 포커스 전환이 완전히 반영될 때까지 대기
            await Task.Delay(100, ct);

            // SendInput으로 Ctrl+V 주입
            // Win32 INPUT 구조체: type(4) + 패딩(4) + union(32) = 40 bytes on x64
            // KEYBDINPUT.dwExtraInfo = ULONG_PTR → union 8바이트 정렬 필요 → offset 8에서 시작
            SendCtrlV();
        }
        catch (OperationCanceledException) { }
        catch (Exception ex)
        {
            LogService.Warn($"클립보드 히스토리 붙여넣기 실패: {ex.Message}");
        }
    }

    private static void SendCtrlV()
    {
        const uint INPUT_KEYBOARD = 1;
        const uint KEYEVENTF_KEYUP = 0x0002;
        const ushort VK_CONTROL = 0x11;
        const ushort VK_V = 0x56;

        var inputs = new INPUT[4];

        // Ctrl 누름
        inputs[0].Type = INPUT_KEYBOARD;
        inputs[0].ki.wVk = VK_CONTROL;

        // V 누름
        inputs[1].Type = INPUT_KEYBOARD;
        inputs[1].ki.wVk = VK_V;

        // V 뗌
        inputs[2].Type = INPUT_KEYBOARD;
        inputs[2].ki.wVk = VK_V;
        inputs[2].ki.dwFlags = KEYEVENTF_KEYUP;

        // Ctrl 뗌
        inputs[3].Type = INPUT_KEYBOARD;
        inputs[3].ki.wVk = VK_CONTROL;
        inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;

        SendInput((uint)inputs.Length, inputs, Marshal.SizeOf<INPUT>());
    }

    // ─── P/Invoke ──────────────────────────────────────────────────────────

    // Win32 INPUT 구조체 — x64에서 40바이트
    // type(4) + 패딩(4) + union은 offset 8에서 시작 (MOUSEINPUT 32바이트가 최대)
    [StructLayout(LayoutKind.Explicit, Size = 40)]
    private struct INPUT
    {
        [FieldOffset(0)] public uint Type;
        [FieldOffset(8)] public KEYBDINPUT ki;
    }

    // KEYBDINPUT: 2+2+4+4 = 12, dwExtraInfo(IntPtr)는 8바이트 정렬 → 패딩 포함 24바이트
    [StructLayout(LayoutKind.Sequential)]
    private struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr 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);
}
