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

namespace AxCopilot.Handlers;

/// <summary>
/// L27-6: 클립보드 순차 붙여넣기 핸들러. "paste" 프리픽스로 사용합니다.
///
/// 예: paste              → 번호 매긴 클립보드 히스토리 목록
///     paste 3 1 5        → 3번→1번→5번 항목을 순서대로 붙여넣기
///     paste all          → 전체 히스토리를 순서대로 붙여넣기
/// Enter → 이전 창에서 순서대로 Ctrl+V 실행.
/// Raycast "Paste Sequentially" 대응.
/// </summary>
public class PasteHandler : IActionHandler
{
    private readonly ClipboardHistoryService _history;

    public string? Prefix => "paste";

    public PluginMetadata Metadata => new(
        "순차 붙여넣기",
        "클립보드 히스토리를 순서대로 붙여넣기 (Paste Sequentially)",
        "1.0",
        "AX");

    public PasteHandler(ClipboardHistoryService historyService)
    {
        _history = historyService;
    }

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim();
        var items = new List<LauncherItem>();
        var history = _history.History.Where(e => e.IsText && !string.IsNullOrEmpty(e.Text)).ToList();

        if (history.Count == 0)
        {
            items.Add(new LauncherItem(
                "클립보드 히스토리가 비어 있습니다",
                "텍스트를 복사하면 사용할 수 있습니다",
                null, null, Symbol: Symbols.ClipPaste));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // ── 번호 시퀀스 파싱 ──────────────────────────────────────────────────
        if (!string.IsNullOrWhiteSpace(q) && q != "all")
        {
            var nums = q.Split(' ', StringSplitOptions.RemoveEmptyEntries);
            var indices = new List<int>();
            foreach (var n in nums)
            {
                if (int.TryParse(n, out int idx) && idx >= 1 && idx <= history.Count)
                    indices.Add(idx);
            }

            if (indices.Count > 0)
            {
                var preview = string.Join(" → ", indices.Select(i => $"#{i}"));
                var texts = indices.Select(i => history[i - 1].Text ?? "").ToList();
                var totalLen = texts.Sum(t => t.Length);

                items.Add(new LauncherItem(
                    $"순차 붙여넣기: {preview}",
                    $"{indices.Count}개 항목 · {totalLen}자 · Enter: 순서대로 붙여넣기",
                    null, ("seq", texts), Symbol: Symbols.ClipPaste));

                // 미리보기
                for (int i = 0; i < indices.Count; i++)
                {
                    var entry = history[indices[i] - 1];
                    items.Add(new LauncherItem(
                        $"  {i + 1}. #{indices[i]}: {Truncate(entry.Preview, 60)}",
                        entry.RelativeTime,
                        null, null, Symbol: Symbols.History));
                }

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

        // ── all 명령 ──────────────────────────────────────────────────────────
        if (q.Equals("all", StringComparison.OrdinalIgnoreCase))
        {
            var texts = history.Take(20).Select(e => e.Text ?? "").ToList();
            items.Add(new LauncherItem(
                $"전체 순차 붙여넣기 ({texts.Count}개)",
                $"Enter: 최근 {texts.Count}개 항목을 순서대로 붙여넣기",
                null, ("seq", texts), Symbol: Symbols.ClipPaste));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // ── 빈 쿼리 → 번호 매긴 목록 ─────────────────────────────────────────
        items.Add(new LauncherItem(
            "순차 붙여넣기 — 번호를 입력하세요",
            "예: paste 3 1 5 → 3번→1번→5번 순서로 붙여넣기 · paste all → 전체",
            null, null, Symbol: Symbols.ClipPaste));

        for (int i = 0; i < Math.Min(history.Count, 15); i++)
        {
            var entry = history[i];
            var pinMark = entry.IsPinned ? "\uD83D\uDCCC " : "";
            items.Add(new LauncherItem(
                $"  #{i + 1}  {pinMark}{Truncate(entry.Preview, 50)}",
                $"{entry.RelativeTime}  ·  {entry.CopiedAt:MM/dd HH:mm}",
                null, ("single", entry.Text ?? ""), Symbol: Symbols.History));
        }

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

    public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is ("single", string singleText))
        {
            // 단일 항목 붙여넣기
            await PasteTexts([singleText], ct);
        }
        else if (item.Data is ("seq", List<string> texts))
        {
            // 순차 붙여넣기
            await PasteTexts(texts, ct);
        }
    }

    private async Task PasteTexts(List<string> texts, CancellationToken ct)
    {
        if (texts.Count == 0) return;

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

            _history.SuppressNextCapture();

            // 이전 창 포커스 복원 대기
            await Task.Delay(300, ct);

            var targetThread = GetWindowThreadProcessId(prevWindow, out _);
            var currentThread = GetCurrentThreadId();
            AttachThreadInput(currentThread, targetThread, true);
            SetForegroundWindow(prevWindow);
            AttachThreadInput(currentThread, targetThread, false);

            await Task.Delay(100, ct);

            foreach (var text in texts)
            {
                if (ct.IsCancellationRequested) break;

                _history.SuppressNextCapture();
                Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(text));
                await Task.Delay(50, ct);

                SendCtrlV();
                await Task.Delay(200, ct); // 항목 간 간격
            }

            NotificationService.Notify("paste", $"{texts.Count}개 항목 붙여넣기 완료");
        }
        catch (OperationCanceledException) { }
        catch (Exception ex)
        {
            NotificationService.Notify("paste", $"붙여넣기 실패: {ex.Message}");
        }
    }

    private static string Truncate(string s, int max)
        => s.Length <= max ? s : s[..max] + "…";

    // ─── Ctrl+V 주입 (ClipboardHistoryHandler와 동일 패턴) ────────────────────

    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];
        inputs[0] = new INPUT { Type = INPUT_KEYBOARD, ki = new KEYBDINPUT { wVk = VK_CONTROL } };
        inputs[1] = new INPUT { Type = INPUT_KEYBOARD, ki = new KEYBDINPUT { wVk = VK_V } };
        inputs[2] = new INPUT { Type = INPUT_KEYBOARD, ki = new KEYBDINPUT { wVk = VK_V, dwFlags = KEYEVENTF_KEYUP } };
        inputs[3] = new INPUT { Type = INPUT_KEYBOARD, ki = new KEYBDINPUT { wVk = VK_CONTROL, dwFlags = KEYEVENTF_KEYUP } };
        SendInput((uint)inputs.Length, inputs, Marshal.SizeOf<INPUT>());
    }

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

    [StructLayout(LayoutKind.Explicit, Size = 40)]
    private struct INPUT
    {
        [FieldOffset(0)] public uint Type;
        [FieldOffset(8)] public KEYBDINPUT ki;
    }

    [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);
}
