using System.Windows;
using AxCopilot.Models;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// 텍스트 스니펫 핸들러. ";" 프리픽스로 사용합니다.
/// 예: ;addr  → 미리 저장된 주소 텍스트를 클립보드에 복사 후 붙여넣기
///     ;sig   → 서명 텍스트 붙여넣기
/// </summary>
public class SnippetHandler : IActionHandler
{
    private readonly SettingsService _settings;

    public string? Prefix => ";";

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

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

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var snippets = _settings.Settings.Snippets;

        if (!snippets.Any())
        {
            return Task.FromResult<IEnumerable<LauncherItem>>(
            [
                new LauncherItem(
                    "등록된 스니펫이 없습니다",
                    "설정 → 스니펫 탭에서 추가하세요",
                    null, null, Symbol: Symbols.Snippet)
            ]);
        }

        var q = query.Trim().ToLowerInvariant();
        var results = snippets
            .Where(s => string.IsNullOrEmpty(q) ||
                        s.Key.ToLowerInvariant().Contains(q) ||
                        s.Name.ToLowerInvariant().Contains(q))
            .Select(s => new LauncherItem(
                s.Name.Length > 0 ? s.Name : s.Key,
                TruncatePreview(s.Content),
                null,
                s,
                Symbol: Symbols.Snippet))
            .ToList();

        if (results.Count == 0)
        {
            results.Add(new LauncherItem(
                $"'{query}'에 해당하는 스니펫 없음",
                "설정에서 새 스니펫을 추가하세요",
                null, null, Symbol: Symbols.Snippet));
        }

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

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is not SnippetEntry snippet) return Task.CompletedTask;

        try
        {
            // 변수 치환: {date}, {time}, {datetime}
            var expanded = ExpandVariables(snippet.Content);

            Clipboard.SetText(expanded);

            // 100ms 후 Ctrl+V 시뮬레이션
            Task.Delay(100, ct).ContinueWith(_ =>
            {
                System.Windows.Application.Current.Dispatcher.Invoke(() =>
                {
                    var inputs = new[]
                    {
                        new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0x11 } } }, // Ctrl down
                        new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0x56 } } }, // V down
                        new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0x56, dwFlags = 0x0002 } } }, // V up
                        new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0x11, dwFlags = 0x0002 } } }, // Ctrl up
                    };
                    SendInput((uint)inputs.Length, inputs, System.Runtime.InteropServices.Marshal.SizeOf<INPUT>());
                });
            }, ct, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
        }
        catch (Exception ex)
        {
            LogService.Warn($"스니펫 실행 실패: {ex.Message}");
        }

        return Task.CompletedTask;
    }

    // ─── 변수 치환 ─────────────────────────────────────────────────────────

    private static string ExpandVariables(string content)
    {
        var now = DateTime.Now;
        var result = 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"))
            .Replace("{weekday}",  now.ToString("dddd", System.Globalization.CultureInfo.GetCultureInfo("ko-KR")))
            .Replace("{user}",     Environment.UserName)
            .Replace("{computer}", Environment.MachineName);

        // {clipboard} → 현재 클립보드 텍스트 (UI 스레드에서 실행)
        if (result.Contains("{clipboard}"))
        {
            try
            {
                var clip = System.Windows.Application.Current.Dispatcher.Invoke(
                    () => System.Windows.Clipboard.GetText()) ?? "";
                result = result.Replace("{clipboard}", clip);
            }
            catch { result = result.Replace("{clipboard}", ""); }
        }

        // {app} → 이전 포커스 앱 이름
        if (result.Contains("{app}"))
        {
            var appName = "";
            try
            {
                var hwnd = Services.WindowTracker.PreviousWindow;
                if (hwnd != IntPtr.Zero)
                {
                    GetWindowThreadProcessId(hwnd, out uint pid);
                    using var proc = System.Diagnostics.Process.GetProcessById((int)pid);
                    appName = proc.ProcessName;
                }
            }
            catch { }
            result = result.Replace("{app}", appName);
        }

        return result;
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

    private static string TruncatePreview(string content)
    {
        var oneLine = content.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ");
        return oneLine.Length > 60 ? oneLine[..57] + "…" : oneLine;
    }

    // ─── P/Invoke (SendInput) ──────────────────────────────────────────────

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

    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    private struct INPUT { public uint type; public InputUnion u; }

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

    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    private struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; }
}
