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;

public partial class ScreenCaptureHandler
{
    // ─── 스크롤 캡처 + 영역 캡처 + 헬퍼 ────────────────────────────────────

    private async Task CaptureScrollAsync(string timestamp, CancellationToken ct)
    {
        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, ct);
        }

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

        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;

        // 스크롤 가능한 자식 창 찾기 (웹브라우저, 텍스트뷰어 등)
        var scrollTarget = FindScrollableChild(hwnd);

        const int maxPages = 15;
        var frames = new List<Bitmap>();

        // 첫 프레임
        frames.Add(CaptureWindow(hwnd, w, h, rect));

        for (int i = 0; i < maxPages - 1; i++)
        {
            ct.ThrowIfCancellationRequested();

            // Page Down 전송
            if (scrollTarget != IntPtr.Zero)
                SendMessage(scrollTarget, WM_VSCROLL, new IntPtr(SB_PAGEDOWN), IntPtr.Zero);
            else
                SendPageDown(hwnd);

            await Task.Delay(ScrollDelayMs, ct);

            // 현재 프레임 캡처
            if (!GetWindowRect(hwnd, out var newRect)) break;
            var frame = CaptureWindow(hwnd, w, h, newRect);

            // 이전 프레임과 동일하면 끝 (스크롤 종료 감지)
            if (frames.Count > 0 && AreSimilar(frames[^1], frame))
            {
                frame.Dispose();
                break;
            }

            frames.Add(frame);
        }

        // 프레임들을 수직으로 이어 붙이기 (오버랩 제거)
        using var stitched = StitchFrames(frames, h);

        // 각 프레임 해제
        foreach (var f in frames) f.Dispose();

        CopyToClipboard(stitched);
        NotificationService.Notify("스크롤 캡처 완료", $"{stitched.Height}px  ·  클립보드에 복사되었습니다");
    }

    // ─── 헬퍼: 창 캡처 (PrintWindow 우선, 실패 시 BitBlt 폴백) ──────────────

    private static Bitmap CaptureWindow(IntPtr hwnd, int w, int h, RECT rect)
    {
        var bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
        using var g = Graphics.FromImage(bmp);

        // PrintWindow로 창 내용 캡처 (최소화된 창도 동작)
        var hdc = g.GetHdc();
        bool ok = PrintWindow(hwnd, hdc, 2); // PW_RENDERFULLCONTENT = 2
        g.ReleaseHdc(hdc);

        if (!ok)
        {
            // 폴백: 화면에서 BitBlt
            g.CopyFromScreen(rect.left, rect.top, 0, 0,
                new System.Drawing.Size(w, h), CopyPixelOperation.SourceCopy);
        }

        return bmp;
    }

    // ─── 헬퍼: 전체 화면 범위 ─────────────────────────────────────────────────

    private static System.Drawing.Rectangle GetAllScreenBounds()
    {
        var screens = System.Windows.Forms.Screen.AllScreens;
        int minX = screens.Min(s => s.Bounds.X);
        int minY = screens.Min(s => s.Bounds.Y);
        int maxX = screens.Max(s => s.Bounds.Right);
        int maxY = screens.Max(s => s.Bounds.Bottom);
        return new System.Drawing.Rectangle(minX, minY, maxX - minX, maxY - minY);
    }

    // ─── 헬퍼: 스크롤 가능 자식 창 찾기 ──────────────────────────────────────

    private static IntPtr FindScrollableChild(IntPtr hwnd)
    {
        // 공통 스크롤 가능 클래스 탐색
        foreach (var cls in new[] { "Internet Explorer_Server", "Chrome_RenderWidgetHostHWND",
                                    "MozillaWindowClass", "RichEdit20W", "RICHEDIT50W",
                                    "TextBox", "EDIT" })
        {
            var child = FindWindowEx(hwnd, IntPtr.Zero, cls, null);
            if (child != IntPtr.Zero) return child;
        }
        return IntPtr.Zero;
    }

    // ─── 헬퍼: Page Down 키 전송 ─────────────────────────────────────────────

    private static void SendPageDown(IntPtr hwnd)
    {
        SendMessage(hwnd, WM_KEYDOWN, new IntPtr(VK_NEXT), IntPtr.Zero);
        SendMessage(hwnd, WM_KEYUP,   new IntPtr(VK_NEXT), IntPtr.Zero);
    }

    // ─── 헬퍼: 두 비트맵이 유사한지 비교 (스크롤 종료 감지) ─────────────────
    // LockBits를 사용하여 GetPixel 대비 ~50× 빠르게 처리.

    private static bool AreSimilar(Bitmap a, Bitmap b)
    {
        if (a.Width != b.Width || a.Height != b.Height) return false;

        int startY = (int)(a.Height * 0.8);
        int w = a.Width;
        int h = a.Height;

        var rectA = new System.Drawing.Rectangle(0, startY, w, h - startY);
        var rectB = new System.Drawing.Rectangle(0, startY, w, h - startY);

        var dataA = a.LockBits(rectA, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
        var dataB = b.LockBits(rectB, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

        try
        {
            int sameCount = 0;
            int totalSamples = 0;
            int stride = dataA.Stride;
            int sampleW = w / 16 + 1;
            int sampleH = (h - startY) / 8 + 1;

            unsafe
            {
                byte* ptrA = (byte*)dataA.Scan0.ToPointer();
                byte* ptrB = (byte*)dataB.Scan0.ToPointer();

                for (int sy = 0; sy < sampleH; sy++)
                {
                    int row = sy * 8;
                    if (row >= h - startY) break;
                    for (int sx = 0; sx < sampleW; sx++)
                    {
                        int col = sx * 16;
                        if (col >= w) break;
                        int idx = row * stride + col * 4;
                        if (Math.Abs(ptrA[idx] - ptrB[idx]) < 5 &&
                            Math.Abs(ptrA[idx + 1] - ptrB[idx + 1]) < 5 &&
                            Math.Abs(ptrA[idx + 2] - ptrB[idx + 2]) < 5)
                            sameCount++;
                        totalSamples++;
                    }
                }
            }

            return totalSamples > 0 && (double)sameCount / totalSamples > 0.97;
        }
        finally
        {
            a.UnlockBits(dataA);
            b.UnlockBits(dataB);
        }
    }

    // ─── 헬퍼: 프레임 이어 붙이기 (증분만 추가) ──────────────────────────────

    private static Bitmap StitchFrames(List<Bitmap> frames, int windowHeight)
    {
        if (frames.Count == 0)
            return new Bitmap(1, 1);
        if (frames.Count == 1)
            return new Bitmap(frames[0]);

        int w = frames[0].Width;

        // 각 프레임에서 새로운 부분의 시작 Y (오버랩 제외)
        var newPartStarts = new List<int>();  // 인덱스 1부터: frames[i]에서 오버랩 이후 시작 행
        var newPartHeights = new List<int>(); // 새로운 부분 높이

        int totalHeight = windowHeight; // 첫 프레임은 전체 사용

        for (int i = 1; i < frames.Count; i++)
        {
            int overlap = FindOverlap(frames[i - 1], frames[i]);
            int newStart = overlap > 0 ? overlap : windowHeight / 5; // 오버랩 감지 실패 시 상단 20% 제거
            int newH = windowHeight - newStart;
            if (newH <= 0) { newH = windowHeight / 4; newStart = windowHeight - newH; }
            newPartStarts.Add(newStart);
            newPartHeights.Add(newH);
            totalHeight += newH;
        }

        var result = new Bitmap(w, totalHeight, PixelFormat.Format32bppArgb);
        using var g = Graphics.FromImage(result);

        // 첫 프레임: 전체 그리기
        g.DrawImage(frames[0], 0, 0, w, windowHeight);

        // 이후 프레임: 새로운 부분(증분)만 잘라서 붙이기
        int yPos = windowHeight;
        for (int i = 1; i < frames.Count; i++)
        {
            int srcY = newPartStarts[i - 1];
            int srcH = newPartHeights[i - 1];
            var srcRect = new System.Drawing.Rectangle(0, srcY, w, srcH);
            var dstRect = new System.Drawing.Rectangle(0, yPos, w, srcH);
            g.DrawImage(frames[i], dstRect, srcRect, System.Drawing.GraphicsUnit.Pixel);
            yPos += srcH;
        }

        return result;
    }

    // ─── 헬퍼: 두 프레임 사이 오버랩 픽셀 수 계산 (다중 행 비교) ────────────

    private static int FindOverlap(Bitmap prev, Bitmap next)
    {
        int w = Math.Min(prev.Width, next.Width);
        int h = prev.Height;
        if (h < 16 || w < 16) return 0;

        int searchRange = (int)(h * 0.7); // 최대 70% 오버랩 탐색
        const int checkRows = 8; // 오버랩 후보당 검증할 행 수

        var dataPrev = prev.LockBits(
            new System.Drawing.Rectangle(0, 0, prev.Width, prev.Height),
            ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
        var dataNext = next.LockBits(
            new System.Drawing.Rectangle(0, 0, next.Width, next.Height),
            ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

        try
        {
            int stridePrev = dataPrev.Stride;
            int strideNext = dataNext.Stride;
            int bestOverlap = 0;

            unsafe
            {
                byte* ptrPrev = (byte*)dataPrev.Scan0.ToPointer();
                byte* ptrNext = (byte*)dataNext.Scan0.ToPointer();

                // 큰 오버랩부터 탐색 (스크롤은 보통 1페이지 미만)
                for (int overlap = searchRange; overlap > 8; overlap -= 2)
                {
                    int prevStartY = h - overlap;
                    if (prevStartY < 0) continue;

                    int totalMatch = 0;
                    int totalCheck = 0;

                    // 오버랩 영역 내 여러 행을 검증
                    for (int r = 0; r < checkRows; r++)
                    {
                        int rowInOverlap = r * (overlap / checkRows);
                        int prevRow = prevStartY + rowInOverlap;
                        int nextRow = rowInOverlap;
                        if (prevRow >= h || nextRow >= next.Height) continue;

                        // 행 내 샘플 픽셀 비교
                        for (int x = 4; x < w - 4; x += 12)
                        {
                            int idxP = prevRow * stridePrev + x * 4;
                            int idxN = nextRow * strideNext + x * 4;
                            if (idxP + 2 >= dataPrev.Height * stridePrev) continue;
                            if (idxN + 2 >= dataNext.Height * strideNext) continue;

                            if (Math.Abs(ptrPrev[idxP] - ptrNext[idxN]) < 10 &&
                                Math.Abs(ptrPrev[idxP + 1] - ptrNext[idxN + 1]) < 10 &&
                                Math.Abs(ptrPrev[idxP + 2] - ptrNext[idxN + 2]) < 10)
                                totalMatch++;
                            totalCheck++;
                        }
                    }

                    if (totalCheck > 0 && (double)totalMatch / totalCheck > 0.80)
                    {
                        bestOverlap = overlap;
                        break;
                    }
                }
            }

            return bestOverlap;
        }
        finally
        {
            prev.UnlockBits(dataPrev);
            next.UnlockBits(dataNext);
        }
    }

    // ─── 영역 선택 캡처 ──────────────────────────────────────────────────────

    private async Task CaptureRegionAsync(string timestamp, CancellationToken ct)
    {
        // 전체 화면을 먼저 캡처 (배경으로 사용)
        var bounds = GetAllScreenBounds();
        using var fullBmp = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);
        using (var g = Graphics.FromImage(fullBmp))
            g.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size, CopyPixelOperation.SourceCopy);

        // WPF 오버레이 창으로 영역 선택
        System.Drawing.Rectangle? selected = null;
        System.Windows.Application.Current?.Dispatcher.Invoke(() =>
        {
            var overlay = new AxCopilot.Views.RegionSelectWindow(fullBmp, bounds);
            overlay.ShowDialog();
            selected = overlay.SelectedRect;
        });

        if (selected == null || selected.Value.Width < 4 || selected.Value.Height < 4)
        {
            NotificationService.Notify("AX Copilot", "영역 선택이 취소되었습니다.");
            return;
        }

        var r = selected.Value;
        using var crop = new Bitmap(r.Width, r.Height, PixelFormat.Format32bppArgb);
        using (var g = Graphics.FromImage(crop))
            g.DrawImage(fullBmp, new System.Drawing.Rectangle(0, 0, r.Width, r.Height), r, System.Drawing.GraphicsUnit.Pixel);

        CopyToClipboard(crop);
        NotificationService.Notify("영역 캡처 완료", $"{r.Width}×{r.Height}  ·  클립보드에 복사되었습니다");

        await Task.CompletedTask;
    }

    // ─── 헬퍼: 클립보드에 이미지 복사 ───────────────────────────────────────

    private static void CopyToClipboard(Bitmap bmp)
    {
        try
        {
            // WPF Clipboard에 BitmapSource로 복사
            System.Windows.Application.Current?.Dispatcher.Invoke(() =>
            {
                using var ms = new MemoryStream();
                bmp.Save(ms, ImageFormat.Bmp);
                ms.Position = 0;
                var bitmapImage = new BitmapImage();
                bitmapImage.BeginInit();
                bitmapImage.StreamSource = ms;
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.EndInit();
                bitmapImage.Freeze();
                System.Windows.Clipboard.SetImage(bitmapImage);
            });
        }
        catch (Exception ex)
        {
            LogService.Warn($"클립보드 이미지 복사 실패: {ex.Message}");
        }
    }

    // ─── 헬퍼: 런처 창 핸들 조회 (캡처 시 런처 숨김 대기용) ───────────────

    private static IntPtr GetLauncherHwnd()
    {
        try
        {
            IntPtr hwnd = IntPtr.Zero;
            System.Windows.Application.Current?.Dispatcher.Invoke(() =>
            {
                var launcher = System.Windows.Application.Current.Windows
                    .OfType<System.Windows.Window>()
                    .FirstOrDefault(w => w.GetType().Name == "LauncherWindow");
                if (launcher != null)
                    hwnd = new System.Windows.Interop.WindowInteropHelper(launcher).Handle;
            });
            return hwnd;
        }
        catch (Exception) { return IntPtr.Zero; }
    }
}
