// ==========================================
// Main App Component
// ==========================================

const App = () => {
    // Language & Settings
    const [lang, setLang] = React.useState('bilingual');
    const [showSettings, setShowSettings] = React.useState(false);
    const [copyButtonText, setCopyButtonText] = React.useState("COPY");

    // API Settings (persisted)
    const [apiKey, setApiKey] = React.useState(() => localStorage.getItem('pixel_api_key') || '');
    const [baseUrl, setBaseUrl] = React.useState(() => localStorage.getItem('pixel_base_url') || '');
    const [aiModel, setAiModel] = React.useState(() => localStorage.getItem('pixel_model') || 'gemini-3-pro-image-preview');

    // Background Color Settings
    const [bgColor, setBgColor] = React.useState('#ffffff');
    const [bgTolerance, setBgTolerance] = React.useState(60);

    // Panel States
    const [isAiPanelOpen, setIsAiPanelOpen] = React.useState(true);
    const [isEditorPanelOpen, setIsEditorPanelOpen] = React.useState(true);
    const [isPixelPanelOpen, setIsPixelPanelOpen] = React.useState(true);

    // Pixelation Panel State
    const [pixelState, setPixelState] = React.useState({ isLoading: false, result: null, error: null });
    const [pixelSourceBlob, setPixelSourceBlob] = React.useState(null);
    const [pixelSourcePreview, setPixelSourcePreview] = React.useState(null);

    // Template & Style
    const [selectedTemplate, setSelectedTemplate] = React.useState(window.ANIMATION_TEMPLATES[0]);
    const [currentStyle, setCurrentStyle] = React.useState(window.ART_STYLES[0]);
    const [charStyle, setCharStyle] = React.useState(window.AppConfig.DEFAULT_STYLE_PROMPT);
    const [extraSpecs, setExtraSpecs] = React.useState("");

    // Grid Config
    const [gridCols, setGridCols] = React.useState(4);
    const [gridRows, setGridRows] = React.useState(4);
    const [cellWidth, setCellWidth] = React.useState(256);
    const [cellHeight, setCellHeight] = React.useState(256);

    // Image Generation Config (Gemini 3 Pro)
    const [aspectRatio, setAspectRatio] = React.useState('1:1');
    const [imageQuality, setImageQuality] = React.useState('1K');
    const [autoCalcCellSize, setAutoCalcCellSize] = React.useState(true);

    // Row Steps
    const [rowSteps, setRowSteps] = React.useState([]);

    // Reference Image
    const [refImage, setRefImage] = React.useState(null);
    const [refPreview, setRefPreview] = React.useState(null);

    // Final Prompt (editable)
    const [finalPrompt, setFinalPrompt] = React.useState('');

    // Generation State
    const [genState, setGenState] = React.useState({
        isLoading: false, error: null, generatedImageBase64: null
    });

    // Editor State
    const [filePreview, setFilePreview] = React.useState(null);
    const [spriteSettings, setSpriteSettings] = React.useState({
        columns: 4,
        rows: 4,
        cellWidth: 256,
        cellHeight: 256,
        fps: 12
    });
    const [removeBg, setRemoveBg] = React.useState(false);

    // Export State
    const [isExporting, setIsExporting] = React.useState(false);
    const [resultBlob, setResultBlob] = React.useState(null);
    const [packBlob, setPackBlob] = React.useState(null);

    // Translation Hook
    const { t } = window.useTranslation(lang);

    // Persist API settings
    React.useEffect(() => { localStorage.setItem('pixel_api_key', apiKey); }, [apiKey]);
    React.useEffect(() => { localStorage.setItem('pixel_base_url', baseUrl); }, [baseUrl]);
    React.useEffect(() => { localStorage.setItem('pixel_model', aiModel); }, [aiModel]);

    // Auto-calculate cell size based on aspect ratio, quality, and grid dimensions
    React.useEffect(() => {
        if (!autoCalcCellSize) return;

        const { width: totalWidth, height: totalHeight } = window.GeminiApi.calculateImageDimensions(aspectRatio, imageQuality);
        const { cellWidth: newCellWidth, cellHeight: newCellHeight } = window.GeminiApi.calculateCellSize(totalWidth, totalHeight, gridCols, gridRows);

        setCellWidth(newCellWidth);
        setCellHeight(newCellHeight);
    }, [aspectRatio, imageQuality, gridCols, gridRows, autoCalcCellSize]);

    // Sync generated image to editor
    React.useEffect(() => {
        if (genState.generatedImageBase64) {
            setFilePreview(genState.generatedImageBase64);
            setSpriteSettings({
                columns: Math.max(1, Number(gridCols) || 1),
                rows: Math.max(1, Number(gridRows) || 1),
                cellWidth: Math.max(1, Number(cellWidth) || 64),
                cellHeight: Math.max(1, Number(cellHeight) || 64),
                fps: 12
            });
        }
    }, [genState.generatedImageBase64, gridCols, gridRows, cellWidth, cellHeight]);

    // Helper to generate step label
    const getStepLabel = (rowIdx, cols) => {
        const start = rowIdx * cols + 1;
        const end = (rowIdx + 1) * cols;
        return `${start} - ${end}`;
    };

    // Template change effect
    React.useEffect(() => {
        const cols = selectedTemplate.cols;
        const rows = selectedTemplate.rows;
        const newSteps = Array.from({ length: rows }).map((_, i) => {
            let existing = "";
            if (i < selectedTemplate.steps.length) {
                existing = selectedTemplate.steps[i].action[lang === 'en' ? 'en' : 'cn'];
            }
            return { label: getStepLabel(i, cols), action: existing };
        });
        setRowSteps(newSteps);
        setGridCols(cols);
        setGridRows(rows);
        setCellWidth(selectedTemplate.cellWidth);
        setCellHeight(selectedTemplate.cellHeight);
    }, [selectedTemplate, lang]);

    // ==========================================
    // Pixelation Handlers
    // ==========================================
    const handleUseAiResult = async () => {
        if (!genState.generatedImageBase64) return;
        const blob = await window.FileUtils.base64ToBlob(genState.generatedImageBase64);
        setPixelSourceBlob(blob);
        setPixelSourcePreview(genState.generatedImageBase64);
        setPixelState({ isLoading: false, result: null, error: null });
    };

    const handlePixelUpload = (e) => {
        if (e.target.files?.[0]) {
            const file = e.target.files[0];
            setPixelSourceBlob(file);
            setPixelSourcePreview(URL.createObjectURL(file));
            setPixelState({ isLoading: false, result: null, error: null });
        }
    };

    const handleExecutePixelation = async () => {
        if (!pixelSourceBlob) return;
        setPixelState({ ...pixelState, isLoading: true, error: null });

        try {
            const resultUrl = await window.PixelApi.pixelateImage(pixelSourceBlob);
            setPixelState({ isLoading: false, result: resultUrl, error: null });
        } catch (err) {
            setPixelState({ isLoading: false, result: null, error: err.message });
        }
    };

    const handleApplyPixelToEditor = () => {
        if (pixelState.result) {
            setFilePreview(pixelState.result);
            setIsEditorPanelOpen(true);
            document.getElementById('editor-panel')?.scrollIntoView({ behavior: 'smooth' });
        }
    };

    // Clear handlers for Pixel Panel
    const handleClearPixelSource = () => {
        setPixelSourceBlob(null);
        setPixelSourcePreview(null);
        setPixelState({ isLoading: false, result: null, error: null });
    };

    const handleClearPixelResult = () => {
        setPixelState({ isLoading: false, result: null, error: null });
    };

    // Clear handler for Editor Panel
    const handleClearFilePreview = () => {
        setFilePreview(null);
    };

    // ==========================================
    // Row Management Handlers
    // ==========================================
    const handleAddRow = () => {
        const newRowsCount = Number(gridRows) + 1;
        const newRowIndex = newRowsCount - 1;
        setGridRows(newRowsCount);
        setRowSteps(prev => [
            ...prev,
            { label: getStepLabel(newRowIndex, Number(gridCols)), action: "" }
        ]);
    };

    const handleDeleteRow = (index) => {
        if (gridRows <= 1) return;
        const newRowsCount = Number(gridRows) - 1;
        setGridRows(newRowsCount);

        const newSteps = rowSteps.filter((_, i) => i !== index).map((step, i) => ({
            ...step,
            label: getStepLabel(i, Number(gridCols))
        }));
        setRowSteps(newSteps);
    };

    const handleGridColsChange = (e) => {
        const val = Math.max(1, parseInt(e.target.value) || 1);
        setGridCols(val);
        setRowSteps(prev => prev.map((step, i) => ({
            ...step,
            label: getStepLabel(i, val)
        })));
    };

    const handleGridRowsChange = (e) => {
        const val = Math.max(1, parseInt(e.target.value) || 1);
        setGridRows(val);

        setRowSteps(prev => {
            if (val > prev.length) {
                const added = Array.from({ length: val - prev.length }).map((_, idx) => ({
                    label: getStepLabel(prev.length + idx, Number(gridCols)),
                    action: ""
                }));
                return [...prev, ...added];
            } else {
                return prev.slice(0, val);
            }
        });
    };

    const handleTemplateSwitch = (tpl) => {
        setSelectedTemplate(tpl);
    };

    const handleRefUpload = (e) => {
        if (e.target.files?.[0]) {
            const file = e.target.files[0];
            setRefImage(file);
            setRefPreview(URL.createObjectURL(file));
        }
    };

    const handleManualSpriteUpload = (e) => {
        if (e.target.files?.[0]) {
            const file = e.target.files[0];
            const url = URL.createObjectURL(file);
            setGenState({ isLoading: false, error: null, generatedImageBase64: null });
            setFilePreview(url);
        }
    };

    // ==========================================
    // Calculated Total Dimensions
    // ==========================================
    const totalDimensions = React.useMemo(() => {
        return window.GeminiApi.calculateImageDimensions(aspectRatio, imageQuality);
    }, [aspectRatio, imageQuality]);

    // ==========================================
    // Live Prompt Calculation
    // ==========================================
    const livePrompt = React.useMemo(() => {
        const safeCols = Math.max(1, Number(gridCols) || 1);
        const safeRows = Math.max(1, Number(gridRows) || 1);
        const safeCW = Math.max(8, Number(cellWidth) || 64);
        const safeCH = Math.max(8, Number(cellHeight) || 64);

        const stepsPrompt = rowSteps.map((s, idx) => `第 ${idx + 1} 行 (帧数 ${s.label})：${s.action || '(无动作)'}`).join('\n');

        const hasRefImage = !!refImage;

        return `${window.PromptService.buildSystemPrompt(safeCols, safeRows, safeCW, safeCH, currentStyle.prompt, bgColor, hasRefImage)}\n\n[动作结构：${selectedTemplate.name.cn}]\n${selectedTemplate.flavor.cn}\n\n[各行帧动作分配]\n${stepsPrompt}\n\n[角色细节描述]\n${charStyle || "精致的像素风格角色"}\n\n[额外补充说明]\n${extraSpecs || "动作流畅，细节丰富。"}`;
    }, [gridCols, gridRows, cellWidth, cellHeight, currentStyle, selectedTemplate, rowSteps, charStyle, bgColor, extraSpecs, refImage]);

    // Sync livePrompt to finalPrompt when livePrompt changes (only if finalPrompt is empty or user hasn't edited)
    React.useEffect(() => {
        setFinalPrompt(livePrompt);
    }, [livePrompt]);

    // Reset prompt handler
    const handleResetPrompt = () => {
        setFinalPrompt(livePrompt);
    };

    // ==========================================
    // Generate Handler
    // ==========================================
    const handleGenerate = async () => {
        const safeCols = Math.max(1, Number(gridCols) || 1);
        const safeRows = Math.max(1, Number(gridRows) || 1);
        const safeCW = Math.max(8, Number(cellWidth) || 64);
        const safeCH = Math.max(8, Number(cellHeight) || 64);

        setGenState({ isLoading: true, error: null, generatedImageBase64: null });

        if (!apiKey.trim()) {
            setGenState({ isLoading: false, error: "请先点击右上角设置图标，填写 API Key。", generatedImageBase64: null });
            setShowSettings(true);
            return;
        }

        try {
            const totalW = safeCols * safeCW;
            const totalH = safeRows * safeCH;

            const solidBase64 = await window.CanvasUtils.createSolidImage(totalW, totalH, bgColor);

            let refBase64 = null;
            if (refImage) refBase64 = await window.FileUtils.fileToBase64(refImage);

            // Build image config for Gemini 3 Pro
            const imageConfig = {
                aspectRatio: aspectRatio,
                imageSize: imageQuality
            };

            const result = await window.GeminiApi.generateSprite(
                finalPrompt,
                refBase64,
                solidBase64,
                aiModel,
                { apiKey, baseUrl },
                imageConfig
            );

            setGenState({ isLoading: false, error: null, generatedImageBase64: result });
        } catch (err) {
            setGenState({ isLoading: false, error: err.message, generatedImageBase64: null });
        }
    };

    // ==========================================
    // Export Handler
    // ==========================================
    const handleExportPackage = async () => {
        if (!filePreview || isExporting) return;

        const cols = Math.max(1, Number(spriteSettings.columns) || 1);
        const rows = Math.max(1, Number(spriteSettings.rows) || 1);
        const fps = Math.max(1, Number(spriteSettings.fps) || 12);

        setIsExporting(true);
        try {
            const workerScriptUrl = "https://cdnjs.cloudflare.com/ajax/libs/gif.js/0.2.0/gif.worker.js";
            const response = await fetch(workerScriptUrl);
            if (!response.ok) throw new Error("无法加载 GIF worker 脚本");
            const workerBlob = await response.blob();
            const localWorkerUrl = URL.createObjectURL(workerBlob);

            const zip = new JSZip();
            const framesTransFolder = zip.folder("transparent_frames");
            const framesOrigFolder = zip.folder("original_frames");

            const img = new Image();
            img.crossOrigin = "anonymous";
            img.src = filePreview;
            await img.decode();

            const fw = Math.floor(img.naturalWidth / cols);
            const fh = Math.floor(img.naturalHeight / rows);

            if (fw <= 0 || fh <= 0) throw new Error("Invalid slice dimensions");

            // Process full sheets
            const fullSheetCanvas = document.createElement('canvas');
            fullSheetCanvas.width = img.naturalWidth;
            fullSheetCanvas.height = img.naturalHeight;
            const fullSheetCtx = fullSheetCanvas.getContext('2d');
            const targetRgb = window.ColorUtils.hexToRgb(bgColor);

            // Original sheet
            fullSheetCtx.drawImage(img, 0, 0);
            const sheetOrigBlob = await new Promise(resolve => fullSheetCanvas.toBlob(resolve, 'image/png'));
            zip.file("sheet_original.png", sheetOrigBlob);

            // Transparent sheet
            const sheetIData = fullSheetCtx.getImageData(0, 0, fullSheetCanvas.width, fullSheetCanvas.height);
            const sData = sheetIData.data;
            for (let i = 0; i < sData.length; i += 4) {
                if (window.ColorUtils.isColorMatch(sData[i], sData[i + 1], sData[i + 2], targetRgb, bgTolerance)) {
                    sData[i + 3] = 0;
                }
            }
            fullSheetCtx.putImageData(sheetIData, 0, 0);
            const sheetTransBlob = await new Promise(resolve => fullSheetCanvas.toBlob(resolve, 'image/png'));
            zip.file("sheet_transparent.png", sheetTransBlob);

            const gifTrans = new GIF({ workers: 2, quality: 10, width: fw, height: fh, workerScript: localWorkerUrl, transparent: 0x000000 });
            const gifOrig = new GIF({ workers: 2, quality: 10, width: fw, height: fh, workerScript: localWorkerUrl, background: '#ffffff' });

            const canvas = document.createElement('canvas');
            canvas.width = fw;
            canvas.height = fh;
            const ctx = canvas.getContext('2d', { willReadFrequently: true });

            for (let i = 0; i < rows * cols; i++) {
                const c = i % cols;
                const r = Math.floor(i / cols);

                ctx.globalCompositeOperation = 'source-over';
                ctx.fillStyle = bgColor;
                ctx.fillRect(0, 0, fw, fh);
                ctx.drawImage(img, Math.floor(c * fw), Math.floor(r * fh), fw, fh, 0, 0, fw, fh);

                gifOrig.addFrame(ctx, { copy: true, delay: 1000 / fps });

                const origBlob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
                framesOrigFolder.file(`frame_${String(i + 1).padStart(2, '0')}.png`, origBlob);

                // Transparent frame
                ctx.clearRect(0, 0, fw, fh);
                ctx.drawImage(img, Math.floor(c * fw), Math.floor(r * fh), fw, fh, 0, 0, fw, fh);

                const idata = ctx.getImageData(0, 0, fw, fh);
                const data = idata.data;
                for (let j = 0; j < data.length; j += 4) {
                    if (window.ColorUtils.isColorMatch(data[j], data[j + 1], data[j + 2], targetRgb, bgTolerance)) {
                        data[j + 3] = 0;
                    }
                }
                ctx.putImageData(idata, 0, 0);

                gifTrans.addFrame(canvas, { copy: true, delay: 1000 / fps });

                const transBlob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
                framesTransFolder.file(`frame_${String(i + 1).padStart(2, '0')}.png`, transBlob);
            }

            const renderGifPromise = (gif) => new Promise((resolve) => {
                gif.on('finished', resolve);
                gif.render();
            });

            const [transGifBlob, origGifBlob] = await Promise.all([
                renderGifPromise(gifTrans),
                renderGifPromise(gifOrig)
            ]);

            zip.file("animation_transparent.gif", transGifBlob);
            zip.file("animation_original.gif", origGifBlob);

            const zipBlob = await zip.generateAsync({ type: "blob" });

            // Use original or transparent GIF based on removeBg setting
            setResultBlob(removeBg ? transGifBlob : origGifBlob);
            setPackBlob(zipBlob);
            setIsExporting(false);

        } catch (e) {
            console.error("Export Error:", e);
            alert(`打包失败: ${e.message}`);
            setIsExporting(false);
        }
    };

    // ==========================================
    // Copy Prompt Handler
    // ==========================================
    const handleCopyPrompt = () => {
        const textArea = document.createElement("textarea");
        textArea.value = finalPrompt;
        textArea.style.position = "fixed";
        textArea.style.left = "-9999px";
        document.body.appendChild(textArea);
        textArea.focus();
        textArea.select();

        try {
            document.execCommand('copy');
            setCopyButtonText("COPIED!");
            setTimeout(() => setCopyButtonText("COPY"), 2000);
        } catch (err) {
            console.error('Fallback copy failed', err);
            setCopyButtonText("ERROR");
        }

        document.body.removeChild(textArea);
    };

    // ==========================================
    // Render
    // ==========================================
    return (
        <div className="min-h-screen bg-[#020617] text-slate-100 selection:bg-indigo-500/30">
            {/* Settings Modal */}
            <window.SettingsModal
                show={showSettings}
                onClose={() => setShowSettings(false)}
                apiKey={apiKey}
                setApiKey={setApiKey}
                baseUrl={baseUrl}
                setBaseUrl={setBaseUrl}
                aiModel={aiModel}
                setAiModel={setAiModel}
                t={t}
            />

            {/* Header */}
            <window.Header
                lang={lang}
                setLang={setLang}
                apiKey={apiKey}
                onSettingsClick={() => setShowSettings(true)}
                t={t}
            />

            <main className="w-full px-6 space-y-8">
                {/* AI Panel */}
                <window.AIPanel
                    isOpen={isAiPanelOpen}
                    onToggle={() => setIsAiPanelOpen(!isAiPanelOpen)}
                    lang={lang}
                    t={t}
                    selectedTemplate={selectedTemplate}
                    onTemplateSwitch={handleTemplateSwitch}
                    currentStyle={currentStyle}
                    setCurrentStyle={setCurrentStyle}
                    gridCols={gridCols}
                    gridRows={gridRows}
                    cellWidth={cellWidth}
                    cellHeight={cellHeight}
                    onGridColsChange={handleGridColsChange}
                    onGridRowsChange={handleGridRowsChange}
                    setCellWidth={setCellWidth}
                    setCellHeight={setCellHeight}
                    rowSteps={rowSteps}
                    setRowSteps={setRowSteps}
                    onAddRow={handleAddRow}
                    onDeleteRow={handleDeleteRow}
                    bgColor={bgColor}
                    setBgColor={setBgColor}
                    charStyle={charStyle}
                    setCharStyle={setCharStyle}
                    extraSpecs={extraSpecs}
                    setExtraSpecs={setExtraSpecs}
                    refPreview={refPreview}
                    onRefUpload={handleRefUpload}
                    finalPrompt={finalPrompt}
                    setFinalPrompt={setFinalPrompt}
                    onResetPrompt={handleResetPrompt}
                    onCopyPrompt={handleCopyPrompt}
                    copyButtonText={copyButtonText}
                    genState={genState}
                    onGenerate={handleGenerate}
                    aspectRatio={aspectRatio}
                    setAspectRatio={setAspectRatio}
                    imageQuality={imageQuality}
                    setImageQuality={setImageQuality}
                    totalWidth={totalDimensions.width}
                    totalHeight={totalDimensions.height}
                    autoCalcCellSize={autoCalcCellSize}
                    setAutoCalcCellSize={setAutoCalcCellSize}
                />

                {/* Pixel Panel */}
                <window.PixelPanel
                    isOpen={isPixelPanelOpen}
                    onToggle={() => setIsPixelPanelOpen(!isPixelPanelOpen)}
                    t={t}
                    pixelSourcePreview={pixelSourcePreview}
                    genState={genState}
                    onUseAiResult={handleUseAiResult}
                    onPixelUpload={handlePixelUpload}
                    onClearPixelSource={handleClearPixelSource}
                    pixelState={pixelState}
                    onExecutePixelation={handleExecutePixelation}
                    onApplyToEditor={handleApplyPixelToEditor}
                    onClearPixelResult={handleClearPixelResult}
                />

                {/* Editor Panel */}
                <window.EditorPanel
                    isOpen={isEditorPanelOpen}
                    onToggle={() => setIsEditorPanelOpen(!isEditorPanelOpen)}
                    t={t}
                    filePreview={filePreview}
                    onManualSpriteUpload={handleManualSpriteUpload}
                    onClearFilePreview={handleClearFilePreview}
                    spriteSettings={spriteSettings}
                    setSpriteSettings={setSpriteSettings}
                    bgColor={bgColor}
                    bgTolerance={bgTolerance}
                    setBgTolerance={setBgTolerance}
                    removeBg={removeBg}
                    setRemoveBg={setRemoveBg}
                    currentStyle={currentStyle}
                    isExporting={isExporting}
                    onExportPackage={handleExportPackage}
                />

                {/* Result Modal */}
                <window.ResultModal
                    resultBlob={resultBlob}
                    packBlob={packBlob}
                    onClose={() => setResultBlob(null)}
                    t={t}
                />
            </main>

            {/* Footer */}
            <window.Footer t={t} />
        </div>
    );
};

window.App = App;
