using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Media.Imaging;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// 화면 캡처 핸들러. "cap" 프리픽스로 사용합니다.
///
/// 예: cap            → 전체 화면 캡처
///     cap screen     → 전체 화면 캡처
///     cap window     → 런처 호출 전 활성 창 캡처
///     cap scroll     → 활성 창 스크롤 캡처 (페이지 전체)
///     cap region     → 마우스로 영역 선택 후 캡처
/// 파일 저장 여부 / 경로는 설정 → 캡처 탭에서 변경 가능.
/// 기본값: 저장 안 함, 클립보드에만 복사.
/// </summary>
public partial class ScreenCaptureHandler : IActionHandler
{
    private readonly AxCopilot.Services.SettingsService _settings;

    public ScreenCaptureHandler(AxCopilot.Services.SettingsService settings)
    {
        _settings = settings;
    }

    public string? Prefix => string.IsNullOrWhiteSpace(_settings.Settings.ScreenCapture.Prefix)
        ? "cap"
        : _settings.Settings.ScreenCapture.Prefix.Trim();

    public PluginMetadata Metadata => new(
        "ScreenCapture",
        "화면 캡처 — cap screen/window/scroll/region",
        "1.0",
        "AX");

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

    [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
    [DllImport("user32.dll")] private static extern bool IsWindow(IntPtr hWnd);
    [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    [DllImport("user32.dll")] private static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);
    [DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr hWnd);

    // 스크롤/키 메시지 전송
    [DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll")] private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr child, string? className, string? windowText);

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT { public int left, top, right, bottom; }

    private const int SW_RESTORE = 9;
    private const byte VK_NEXT = 0x22;   // Page Down
    private const uint WM_VSCROLL = 0x0115;
    private const uint WM_KEYDOWN = 0x0100;
    private const uint WM_KEYUP = 0x0101;
    private const int SB_PAGEDOWN = 3;

    // 보안 정책: 캡처 결과는 클립보드에만 복사. 파일 저장 기능 없음.

    private static readonly (string Key, string Label, string Desc)[] _options =
    [
        ("region", "영역 선택 캡처",    "마우스로 드래그하여 원하는 영역만 캡처  ·  Shift+Enter: 타이머 캡처"),
        ("window", "활성 창 캡처",      "런처 호출 전 활성 창만 캡처  ·  Shift+Enter: 타이머 캡처"),
        ("scroll", "스크롤 캡처",       "활성 창을 끝까지 스크롤하며 페이지 전체 캡처  ·  Shift+Enter: 타이머 캡처"),
        ("screen", "전체 화면 캡처",    "모든 모니터를 포함한 전체 화면  ·  Shift+Enter: 타이머 캡처"),
    ];

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q       = query.Trim().ToLowerInvariant();
        var saveHint = "클립보드에 복사";

        IEnumerable<(string Key, string Label, string Desc)> options = string.IsNullOrWhiteSpace(q)
            ? _options
            : _options.Where(o => o.Key.StartsWith(q) || o.Label.Contains(q));

        var items = options.Select(o => new LauncherItem(
            o.Label,
            $"{o.Desc}  ·  {saveHint}",
            null,
            o.Key,
            Symbol: Symbols.CaptureIcon)).ToList<LauncherItem>();

        if (!items.Any())
            items.Add(new LauncherItem(
                $"알 수 없는 캡처 모드: {q}",
                "screen / window / scroll / region",
                null, null, Symbol: Symbols.Warning));

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

    /// <summary>
    /// 지연 캡처 타이머 선택 항목을 반환합니다.
    /// Data 형식: "delay:모드:초" (예: "delay:region:3")
    /// </summary>
    public IEnumerable<LauncherItem> GetDelayItems(string mode)
    {
        var label = _options.FirstOrDefault(o => o.Key == mode).Label ?? mode;
        return new[]
        {
            new LauncherItem($"3초 후 {label}",  "Shift+Enter로 지연 캡처", null, $"delay:{mode}:3",  Symbol: Symbols.Timer),
            new LauncherItem($"5초 후 {label}",  "Shift+Enter로 지연 캡처", null, $"delay:{mode}:5",  Symbol: Symbols.Timer),
            new LauncherItem($"10초 후 {label}", "Shift+Enter로 지연 캡처", null, $"delay:{mode}:10", Symbol: Symbols.Timer),
        };
    }

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

        // 지연 캡처: "delay:모드:초"
        if (data.StartsWith("delay:"))
        {
            var parts = data.Split(':');
            if (parts.Length == 3 && int.TryParse(parts[2], out var delaySec))
            {
                await ExecuteDelayedCaptureAsync(parts[1], delaySec, ct);
                return;
            }
        }

        // 런처가 닫히는 동안 잠시 대기
        await Task.Delay(150, ct);
        await CaptureDirectAsync(data, ct);
    }

    /// <summary>
    /// 지정된 초만큼 대기 후 캡처를 실행합니다. 완료 시 알림이 표시됩니다.
    /// </summary>
    private async Task ExecuteDelayedCaptureAsync(string mode, int delaySec, CancellationToken ct)
    {
        // 런처가 닫히는 동안 잠시 대기
        await Task.Delay(200, ct);

        // 알림 없이 조용히 대기 후 캡처
        for (int i = delaySec; i > 0; i--)
        {
            ct.ThrowIfCancellationRequested();
            await Task.Delay(1000, ct);
        }

        await CaptureDirectAsync(mode, ct);
    }

    /// <summary>
    /// 런처 없이 직접 캡처를 실행합니다. 글로벌 단축키용.
    /// </summary>
    public async Task CaptureDirectAsync(string mode, CancellationToken ct = default)
    {
        try
        {
            var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            // 캡처 결과는 클립보드에만 복사 (보안 정책)

            switch (mode)
            {
                case "screen":
                    await CaptureScreenAsync(timestamp);
                    break;
                case "window":
                    await CaptureWindowAsync(timestamp);
                    break;
                case "scroll":
                    await CaptureScrollAsync(timestamp, ct);
                    break;
                case "region":
                    await CaptureRegionAsync(timestamp, ct);
                    break;
            }
        }
        catch (Exception ex)
        {
            LogService.Error($"캡처 실패: {ex.Message}");
            NotificationService.Notify("AX Copilot", $"캡처 실패: {ex.Message}");
        }
    }

    /// <summary>
    /// 스크롤 캡처에 사용되는 프레임 간 대기 시간(ms). 설정에서 읽기.
    /// </summary>
    internal int ScrollDelayMs => Math.Max(50, _settings.Settings.ScreenCapture.ScrollDelayMs);

    // ─── 전체 화면 캡처 ────────────────────────────────────────────────────────

    private async Task CaptureScreenAsync(string timestamp)
    {
        var bounds = GetAllScreenBounds();
        using var bmp = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);
        using var g = Graphics.FromImage(bmp);
        g.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size, CopyPixelOperation.SourceCopy);

        CopyToClipboard(bmp);
        await Task.Delay(10);
        NotificationService.Notify("화면 캡처 완료", "클립보드에 복사되었습니다");
    }

    // ─── 창 캡처 ───────────────────────────────────────────────────────────────

    private async Task CaptureWindowAsync(string timestamp)
    {
        var hwnd = WindowTracker.PreviousWindow;
        if (hwnd == IntPtr.Zero || !IsWindow(hwnd))
        {
            NotificationService.Notify("AX Copilot", "캡처할 창이 없습니다. 런처 호출 전 창을 확인하세요.");
            return;
        }

        // 런처가 완전히 사라질 때까지 대기 (런처 자체가 캡처되는 버그 방지)
        var launcherHwnd = GetLauncherHwnd();
        if (launcherHwnd != IntPtr.Zero)
        {
            for (int i = 0; i < 10 && IsWindowVisible(launcherHwnd); i++)
                await Task.Delay(50);
        }

        ShowWindow(hwnd, SW_RESTORE);
        SetForegroundWindow(hwnd);
        await Task.Delay(150);

        if (!GetWindowRect(hwnd, out var rect)) return;

        int w = rect.right - rect.left;
        int h = rect.bottom - rect.top;
        if (w <= 0 || h <= 0) return;

        using var bmp = CaptureWindow(hwnd, w, h, rect);
        CopyToClipboard(bmp);

        NotificationService.Notify("창 캡처 완료", "클립보드에 복사되었습니다");
    }

}
