// ============================================================
// CanvasViewport — 核心画布组件
//
// CSS Transform 虚拟视口：
//   - Grid 模式：固定列数，均匀网格布局
//   - Single 模式：按 groupKey 分组，每组一行横向排列
//   - 滚轮缩放：滚轮直接调整 zoom（无需修饰键），Ctrl/Cmd + 滚轮精细缩放
//   - Ctrl/Cmd + 鼠标拖拽：按住 Ctrl(Win) 或 Cmd(Mac) 后 mousedown + mousemove 平移
//   - 分页叠加层：PagerOverlay + ZoomOverlay
// ============================================================

import { useRef, useCallback, useEffect, useState, useMemo } from 'react';
import { logger } from '@/shared/utils/bus';
import { useTheme } from '@/app/providers/ThemeProvider';
import { useCanvasStore } from '../../stores/useCanvasStore';
import { MediaCard } from './MediaCard';
import { MediaCardContextMenu } from './MediaCardContextMenu';
import { PagerOverlay, ZoomOverlay, ModeSwitch } from './ViewportControls';
import {
    gridPosition,
    gridPageInfo,
    gridCanvasSize,
    singleLayout,
    singlePageInfo,
} from '../../utils/layout';
import { ITEM_H, ITEM_SPACING, ROW_LABEL_WIDTH, MAX_ITEMS_PER_PAGE } from '../../utils/constants';

/** 缩放倍率范围 */
const ZOOM_MIN = 0.02;
const ZOOM_MAX = 16.0;

interface CanvasViewportProps {
    /** 双击卡片时回调 */
    onPreviewOpen: (itemId: number) => void;
    /** 预览是否打开（阻止画布缩放/拖拽） */
    previewActive: boolean;
}

export function CanvasViewport({ onPreviewOpen, previewActive }: CanvasViewportProps) {
    const { isDark } = useTheme();
    const items = useCanvasStore((s) => s.items);
    const layoutMode = useCanvasStore((s) => s.layoutMode);
    const cols = useCanvasStore((s) => s.cols);
    const rowsPerPage = useCanvasStore((s) => s.rowsPerPage);
    const singleRowsPerPage = useCanvasStore((s) => s.singleRowsPerPage);
    const currentPage = useCanvasStore((s) => s.currentPage);
    const zoom = useCanvasStore((s) => s.zoom);
    const panOffset = useCanvasStore((s) => s.panOffset);
    const interactionMode = useCanvasStore((s) => s.interactionMode);
    const favoriteOnly = useCanvasStore((s) => s.favoriteOnly);
    const setZoom = useCanvasStore((s) => s.setZoom);
    const setPanOffset = useCanvasStore((s) => s.setPanOffset);

    // 拖拽状态（需按住 Ctrl/Cmd 修饰键）
    const [isDragging, setIsDragging] = useState(false);
    const dragStart = useRef({ x: 0, y: 0, panX: 0, panY: 0 });
    const containerRef = useRef<HTMLDivElement>(null);

    // 拖拽修饰键状态（Ctrl / Cmd）—— 双通道
    const [isPanModifier, setIsPanModifier] = useState(false);
    const panModifierRef = useRef(false);

    // 预览打开时锁定视口事件（防止穿透缩放/拖拽画布）
    const previewActiveRef = useRef(false);

    // ref 版 setZoom + currentZoom — 供原生 wheel handler 稳定读写。
    // 避免 Zustand v5 中 getState() 在连续原生事件中返回批处理前的旧值，
    // 导致每次滚轮都从同一个起点计算（日志反复出现"100% → 90%"）。
    const setZoomRef = useRef(setZoom);
    useEffect(() => { setZoomRef.current = setZoom; }, [setZoom]);
    const currentZoomRef = useRef(zoom);
    useEffect(() => { currentZoomRef.current = zoom; }, [zoom]);
    // 同步预览状态到 ref（供原生 wheel 监听器读取，避免闭包过期）
    useEffect(() => {
        previewActiveRef.current = previewActive;
    }, [previewActive]);


    // ── 收藏过滤 ──
    // 客户端过滤：当 favoriteOnly 为 true 时仅展示 metadata.favorite === true 的条目

    const displayItems = useMemo(() => {
        if (!favoriteOnly) return items;
        return items.filter((item) => item.metadata?.favorite === true);
    }, [items, favoriteOnly]);

    // ── 分页计算 ──

    const pageItems = useMemo(() => {
        if (displayItems.length === 0) return [];
        if (layoutMode === 'grid') {
            const { startIndex, endIndex } = gridPageInfo(
                displayItems.length,
                cols,
                rowsPerPage,
                currentPage,
            );
            return displayItems.slice(startIndex, endIndex);
        }
        // single mode
        const { startIndex, endIndex } = singlePageInfo(
            displayItems,
            singleRowsPerPage,
            currentPage,
        );
        return displayItems.slice(startIndex, endIndex);
    }, [displayItems, layoutMode, cols, rowsPerPage, singleRowsPerPage, currentPage]);

    // ── 位置计算 ──

    const { positions, canvasWidth, canvasHeight, rowLabels } = useMemo(() => {
        if (layoutMode === 'grid') {
            const positions = new Map<number, { x: number; y: number }>();
            pageItems.forEach((item, idx) => {
                positions.set(item.id, gridPosition(idx, cols));
            });
            const size = gridCanvasSize(pageItems.length, cols);
            return {
                positions,
                canvasWidth: Math.max(size.width, 400),
                canvasHeight: Math.max(size.height, 300),
                rowLabels: new Map<number, string>(),
            };
        }
        // single mode
        const layout = singleLayout(pageItems);
        // 构建行标签映射
        const labels = new Map<number, string>();
        const seen = new Set<string>();
        let row = 0;
        for (const item of pageItems) {
            if (!seen.has(item.groupKey)) {
                seen.add(item.groupKey);
                labels.set(row, item.groupKey);
                row++;
            }
        }
        return {
            positions: layout.positions,
            canvasWidth: Math.max(layout.canvasWidth, 400),
            canvasHeight: Math.max(layout.canvasHeight, 300),
            rowLabels: labels,
        };
    }, [layoutMode, pageItems, cols]);

    // ── Ctrl/Cmd + 拖拽平移 ──
    // 按住 Ctrl（Windows）或 Cmd（Mac）后鼠标左键拖拽平移画布。
    // 修饰键松开时自动取消拖拽，防止画布粘滞。
    // 同时支持 Ctrl 和 Cmd 组合（keyup 后检查是否仍有一个修饰键被按住）。

    useEffect(() => {
        const handleKeyDown = (e: KeyboardEvent) => {
            if (e.key !== 'Control' && e.key !== 'Meta') return;
            if (e.repeat) return;
            e.preventDefault();
            setIsPanModifier(true);
            panModifierRef.current = true;
        };
        const handleKeyUp = (e: KeyboardEvent) => {
            if (e.key !== 'Control' && e.key !== 'Meta') return;
            // 检查是否所有拖拽修饰键均已松开
            if (!e.ctrlKey && !e.metaKey) {
                setIsPanModifier(false);
                panModifierRef.current = false;
                setIsDragging(false); // 松修饰键时取消拖拽
            }
        };
        // 窗口失焦时重置修饰键状态（防止切出应用导致状态卡住）
        const handleBlur = () => {
            setIsPanModifier(false);
            panModifierRef.current = false;
            setIsDragging(false);
        };
        window.addEventListener('keydown', handleKeyDown);
        window.addEventListener('keyup', handleKeyUp);
        window.addEventListener('blur', handleBlur);
        return () => {
            window.removeEventListener('keydown', handleKeyDown);
            window.removeEventListener('keyup', handleKeyUp);
            window.removeEventListener('blur', handleBlur);
        };
    }, []);

    // ── 滚轮缩放 ──
    // 纯滚轮 → 快速缩放（10%/tick），Ctrl/Cmd + 滚轮 → 精细缩放（3%/tick）。
    // Mac 触控板捏合手势 → macOS 自动合成 Ctrl+wheel 事件 → 精细缩放。
    //
    // 使用 window 级原生监听器（{ passive: false }）而非 React onWheel，
    // 因为 React 18+ / Electron 42 中 React 合成事件一律为 passive，preventDefault 会抛异常。
    //
    // ⚠️ 通过 ref（setZoomRef + currentZoomRef）读写 zoom，
    // 避免 Zustand v5 中 getState() 在原生事件循环中返回批处理前的旧值。

    useEffect(() => {
        const handler = (e: WheelEvent) => {
            // 仅处理容器内部的滚轮事件（避免 ShotList 等其他区域触发）
            if (!containerRef.current?.contains(e.target as Node)) return;
            // 预览打开时锁定视口，防止穿透缩放
            if (previewActiveRef.current) return;
            try {
                e.preventDefault();
            } catch (err) {
                logger.warn('canvas', 'preventDefault 失败（passive 冲突）', err instanceof Error ? err : undefined);
                return;
            }
            // 从 ref 读取当前 zoom（避免 Zustand v5 getState() 在原生事件中返回旧值）
            const currentZoom = currentZoomRef.current;
            // Ctrl/Cmd → 精细缩放（3%/tick），纯滚轮 → 快速缩放（10%/tick）
            const sensitivity = (e.ctrlKey || e.metaKey) ? 0.0003 : 0.001;
            const delta = -e.deltaY * sensitivity;
            const newZoom = Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, currentZoom + delta));
            // 同步更新 ref（供下一帧滚轮事件读取最新值）
            currentZoomRef.current = newZoom;
            setZoomRef.current(newZoom);
        };
        window.addEventListener('wheel', handler, { passive: false });
        return () => window.removeEventListener('wheel', handler);
    }, []);

    // ── 拖拽平移 ──

    const handleMouseDown = useCallback(
        (e: React.MouseEvent) => {
            // 预览打开时锁定视口，防止穿透拖拽
            if (previewActiveRef.current) return;
            // 仅鼠标左键 + 非交互子元素（如按钮）
            if (e.button !== 0) return;
            const target = e.target as HTMLElement;
            if (target.closest('button')) return;
            // 必须按住 Ctrl/Cmd 才能拖拽平移画布
            if (!panModifierRef.current) return;
            setIsDragging(true);
            dragStart.current = {
                x: e.clientX,
                y: e.clientY,
                panX: panOffset.x,
                panY: panOffset.y,
            };
        },
        [panOffset],
    );

    const handleMouseMove = useCallback(
        (e: React.MouseEvent) => {
            if (!isDragging) return;
            const dx = e.clientX - dragStart.current.x;
            const dy = e.clientY - dragStart.current.y;
            setPanOffset({
                x: dragStart.current.panX + dx,
                y: dragStart.current.panY + dy,
            });
        },
        [isDragging, setPanOffset],
    );

    const handleMouseUp = useCallback(() => {
        setIsDragging(false);
    }, []);

    // 全局 mouseup（拖拽到视口外时释放）
    useEffect(() => {
        const handler = () => setIsDragging(false);
        window.addEventListener('mouseup', handler);
        return () => window.removeEventListener('mouseup', handler);
    }, []);

    // ── 键盘快捷键 ──

    useEffect(() => {
        const handler = (e: KeyboardEvent) => {
            // 缩放重置
            if ((e.ctrlKey || e.metaKey) && e.key === '0') {
                e.preventDefault();
                setZoom(1.0);
            }
        };
        window.addEventListener('keydown', handler);
        return () => window.removeEventListener('keydown', handler);
    }, [setZoom]);

    // ── 渲染 ──

    const transform = `scale(${zoom}) translate(${panOffset.x}px, ${panOffset.y}px)`;

    return (
        <div
            ref={containerRef}
            style={{
                flex: 1,
                overflow: 'hidden',
                position: 'relative',
                cursor: isDragging
                    ? 'grabbing'
                    : isPanModifier
                        ? 'grab'
                        : 'default',
                background: isDark ? '#0F0D1E' : '#F9FAFB',
                // Ctrl/Cmd 拖拽时禁用文本选择，防止误抓媒体元素
                userSelect: isPanModifier ? 'none' : undefined,
            }}
            onMouseDown={handleMouseDown}
            onMouseMove={handleMouseMove}
            onMouseUp={handleMouseUp}
            onDragStart={(e) => {
                // Ctrl/Cmd 拖拽时阻止浏览器原生的图片/链接拖拽行为
                if (panModifierRef.current) e.preventDefault();
            }}
        >
            {/* 画布内容 */}
            <div
                style={{
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    transform,
                    transformOrigin: '0 0',
                    width: canvasWidth,
                    height: canvasHeight,
                    // 拖拽修饰键激活时禁用缩略图的鼠标事件，所有事件穿透到容器层
                    pointerEvents: isPanModifier ? 'none' : undefined,
                }}
            >
                {/* Single 模式的行标签 */}
                {layoutMode === 'single' &&
                    Array.from(rowLabels.entries()).map(([row, label]) => (
                        <div
                            key={`label-${row}`}
                            style={{
                                position: 'absolute',
                                left: 0,
                                top: row * (ITEM_H + ITEM_SPACING),
                                width: ROW_LABEL_WIDTH,
                                height: ITEM_H,
                                display: 'flex',
                                alignItems: 'center',
                                justifyContent: 'center',
                                fontSize: 12,
                                fontWeight: 600,
                                color: isDark ? '#C4B5FD' : '#6366F1',
                                background: isDark
                                    ? 'rgba(30, 27, 75, 0.6)'
                                    : 'rgba(238, 242, 255, 0.8)',
                                borderRadius: 4,
                                whiteSpace: 'nowrap',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}
                            title={label}
                        >
                            {label}
                        </div>
                    ))}

                {/* MediaCard 渲染（受硬上限保护） */}
                {pageItems.slice(0, MAX_ITEMS_PER_PAGE).map((item) => {
                    const pos = positions.get(item.id);
                    if (!pos) return null;
                    return (
                        <MediaCardContextMenu key={item.id} itemId={item.id} x={pos.x} y={pos.y}>
                            <MediaCard
                                itemId={item.id}
                                x={0}
                                y={0}
                                zoom={zoom}
                                mode={interactionMode}
                                onDoubleClick={onPreviewOpen}
                            />
                        </MediaCardContextMenu>
                    );
                })}

                {/* DOM 硬上限警告 */}
                {pageItems.length > MAX_ITEMS_PER_PAGE && (
                    <div
                        style={{
                            position: 'absolute',
                            top: canvasHeight + 16,
                            left: 0,
                            width: canvasWidth,
                            padding: '8px 16px',
                            borderRadius: 4,
                            background: 'rgba(255, 77, 79, 0.1)',
                            border: '1px solid rgba(255, 77, 79, 0.3)',
                            color: '#FF4D4F',
                            fontSize: 13,
                            textAlign: 'center',
                        }}
                    >
                        ⚠️ 当前页共 {pageItems.length} 个文件，已渲染前 {MAX_ITEMS_PER_PAGE} 个。
                        请切换为更小的页面尺寸或使用筛选。
                    </div>
                )}

                {/* 空画布提示 */}
                {pageItems.length === 0 && (
                    <div
                        style={{
                            width: canvasWidth,
                            height: canvasHeight,
                            display: 'flex',
                            alignItems: 'center',
                            justifyContent: 'center',
                            color: '#9CA3AF',
                            fontSize: 14,
                        }}
                    >
                        无匹配文件
                    </div>
                )}
            </div>

            {/* 浮层控件 */}
            <PagerOverlay />
            <ZoomOverlay />
            <ModeSwitch />

        </div>
    );
}
