import {useState, useEffect, useRef} from "react";
import ToolBar from "./components/Toolbar";
import MapEditor from "./components/MapEditor";
import SpritePalette from "./components/SpritePalette";
import LayerPanel from "./components/LayerPanel";
import {DEMO_MAP} from "./demo/demoMap";
import {getAllSpritesFromLib} from "./utils/extractSprites";
import type {SpriteEntry} from "./_types/spriteTypes";
import type {MapFile} from "./_types/mapTypes";
import {Box, Paper} from "@mui/material";
import Snackbar from "@mui/material/Snackbar";

export default function App() {
    const [map, setMap] = useState<MapFile>(DEMO_MAP);
    const [mapName, setMapName] = useState(DEMO_MAP.name);
    const [history, setHistory] = useState<MapFile[]>([]);
    const [future, setFuture] = useState<MapFile[]>([]);
    const [tileZoom, setTileZoom] = useState(1);
    const [snack, setSnack] = useState({open: false, msg: ""});
    const [selectedSprite, setSelectedSprite] = useState<SpriteEntry | null>(null);
    const [allSprites, setAllSprites] = useState<SpriteEntry[]>([]);
    const [hoverCell, setHoverCell] = useState<{ row: number, col: number } | null>(null);
    const [activeLayerId, setActiveLayerId] = useState(map.layers[0].id);


    // Loader dynamique
    useEffect(() => {
        if (window.Sprites?.isLoaded) {
            const sprites = getAllSpritesFromLib(window.Sprites);
            setAllSprites(sprites);
            window.ALL_SPRITES = sprites;
        } else {
            window.Sprites?.preloadAll?.().then(() => {
                const sprites = getAllSpritesFromLib(window.Sprites);
                setAllSprites(sprites);
                window.ALL_SPRITES = sprites;
            });
        }
    }, []);

    const minimapRef = useRef<HTMLCanvasElement>(null);

    useEffect(() => {
        // Redessine la minimap à chaque modif map
        const tileSize = 8;
        if (!minimapRef.current) return;
        minimapRef.current.width = map.width * tileSize;
        minimapRef.current.height = map.height * tileSize;
        const ctx = minimapRef.current.getContext("2d")!;
        ctx.clearRect(0, 0, map.width * tileSize, map.height * tileSize);
        for (let l = 0; l < map.layers.length; l++) {
            const layer = map.layers[l];
            if (!layer.visible) continue;
            for (let row = 0; row < map.height; row++) {
                for (let col = 0; col < map.width; col++) {
                    const cell = layer.cells[row][col];
                    if (cell) {
                        const sprite = allSprites.find(s => s.key === cell.sprite);
                        if (sprite) {
                            sprite.drawFn(ctx, col * tileSize, row * tileSize, {w: tileSize, h: tileSize});
                        }
                    }
                }
            }
        }
    }, [map, allSprites]);

    // Ajout couche
    function handleAddLayer() {
        pushHistory(prev => {
            const id = "layer" + (prev.layers.length + 1);
            return {
                ...prev,
                layers: [
                    ...prev.layers,
                    {
                        id,
                        name: "Layer " + (prev.layers.length + 1),
                        visible: true,
                        cells: Array.from({length: prev.height}, () => Array.from({length: prev.width}, () => null)),
                    }
                ]
            }
        });
    }

    // Suppression couche
    function handleRemoveLayer(id: string) {
        pushHistory(prev => {
            const layers = prev.layers.filter(l => l.id !== id);
            setActiveLayerId(layers[0].id);
            return {...prev, layers};
        });
    }

    // Renommage
    function handleRenameLayer(id: string, name: string) {
        pushHistory(prev => {
            const layers = prev.layers.map(l => l.id === id ? {...l, name} : l);
            return {...prev, layers};
        });
    }

    // Afficher/Masquer
    function handleToggleLayer(id: string) {
        pushHistory(prev => {
            const layers = prev.layers.map(l => l.id === id ? {...l, visible: !l.visible} : l);
            return {...prev, layers};
        });
    }

    // Ordre des couches
    function handleMoveLayer(id: string, dir: "up" | "down") {
        pushHistory(prev => {
            const idx = prev.layers.findIndex(l => l.id === id);
            if (idx === -1) return prev;
            const layers = [...prev.layers];
            const swapIdx = dir === "up" ? idx - 1 : idx + 1;
            if (swapIdx < 0 || swapIdx >= layers.length) return prev;
            [layers[idx], layers[swapIdx]] = [layers[swapIdx], layers[idx]];
            return {...prev, layers};
        });
    }

    // Pour savoir quelle couche est active (celle sur laquelle on dessine)
    function handleCellClick(row: number, col: number) {
        if (!selectedSprite) return;
        pushHistory(prev => {
            const activeLayer = prev.layers.find(l => l.id === activeLayerId);
            if (!activeLayer) return prev;
            const layers = prev.layers.map(l => {
                if (l.id !== activeLayerId) return l;
                const cells = l.cells.map(rowArr => [...rowArr]);
                // toggle
                if (cells[row][col]) {
                    cells[row][col] = null;
                } else {
                    cells[row][col] = {sprite: selectedSprite.key};
                }
                return {...l, cells};
            });
            return {...prev, layers};
        });
    }

    // Export JSON
    function handleExportJson() {
        const data = {...map, name: mapName};
        const blob = new Blob([JSON.stringify(data, null, 2)], {type: "application/json"});
        const url = URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = (mapName || "map") + ".json";
        a.click();
        URL.revokeObjectURL(url);
    }

    // Export PNG (snapshot de la map)
    function handleExportPng() {
        // On crée un canvas temporaire pour toute la map
        const tileSize = map.tileSize;
        const w = map.width * tileSize;
        const h = map.height * tileSize;
        const canvas = document.createElement("canvas");
        canvas.width = w;
        canvas.height = h;
        const ctx = canvas.getContext("2d")!;
        // Rendu de toutes les layers visibles
        for (let l = 0; l < map.layers.length; l++) {
            const layer = map.layers[l];
            if (!layer.visible) continue;
            for (let row = 0; row < map.height; row++) {
                for (let col = 0; col < map.width; col++) {
                    const cell = layer.cells[row][col];
                    if (cell) {
                        const sprite = allSprites.find(s => s.key === cell.sprite);
                        if (sprite) {
                            sprite.drawFn(ctx, col * tileSize, row * tileSize, {w: tileSize, h: tileSize});
                        }
                    }
                }
            }
        }
        // Téléchargement PNG
        canvas.toBlob(blob => {
            if (blob) {
                const url = URL.createObjectURL(blob);
                const a = document.createElement("a");
                a.href = url;
                a.download = (mapName || "map") + ".png";
                a.click();
                URL.revokeObjectURL(url);
            }
        });
    }

    // Import Map
    function handleImportMap(file: File) {
        const reader = new FileReader();
        reader.onload = e => {
            try {
                const data = JSON.parse(e.target!.result as string);
                pushHistory(data);
                setMapName(data.name || "map");
            } catch (err) {
                alert("Fichier de map invalide.");
            }
        };
        reader.readAsText(file);
    }

    // ---- Helpers undo/redo ----
    function pushHistory(newMap: any) {
        setHistory(prev => [...prev.slice(-20), map]);
        setFuture([]);
        setMap(newMap);
    }

    function handleUndo() {
        if (!history.length) return;
        const prev = history[history.length - 1];
        setHistory(h => h.slice(0, -1));
        setFuture(f => [map, ...f]);
        setMap(prev);
        setSnack({open: true, msg: "Annulation"});
    }

    function handleRedo() {
        if (!future.length) return;
        const next = future[0];
        setHistory(h => [...h, map]);
        setFuture(f => f.slice(1));
        setMap(next);
        setSnack({open: true, msg: "Rétablissement"});
    }

    // ---- Effacer ----
    function handleClearAll() {
        if (!window.confirm("Effacer toute la map ?")) return;
        const clearedMap = {
            ...map,
            layers: map.layers.map(l => ({
                ...l,
                cells: l.cells.map(row => row.map(() => null))
            }))
        };
        pushHistory(clearedMap);
        setSnack({open: true, msg: "Carte effacée"});
    }

    function handleClearLayer() {
        if (!window.confirm("Effacer la couche courante ?")) return;
        // suppose activeLayerId state (ou adapte si différemment)
        const clearedMap = {
            ...map,
            layers: map.layers.map(l => l.id !== activeLayerId ? l : ({
                ...l,
                cells: l.cells.map(row => row.map(() => null))
            }))
        };
        pushHistory(clearedMap);
        setSnack({open: true, msg: "Couche effacée"});
    }

    // ---- Export ----
    function handleExportJson() {
        const data = {...map, name: mapName};
        const blob = new Blob([JSON.stringify(data, null, 2)], {type: "application/json"});
        const url = URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = (mapName || "map") + ".json";
        a.click();
        URL.revokeObjectURL(url);
        setSnack({open: true, msg: "Carte exportée (.json)"});
    }

    function handleExportPng() {
        // (Voir l'étape précédente)
        setSnack({open: true, msg: "PNG exporté"});
    }

    function handleImportMap(file: File) {
        const reader = new FileReader();
        reader.onload = e => {
            try {
                const data = JSON.parse(e.target!.result as string);
                setMap(data);
                setMapName(data.name || "map");
                setSnack({open: true, msg: "Carte importée !"});
            } catch {
                alert("Fichier de map invalide.");
            }
        };
        reader.readAsText(file);
    }

    // ---- Help Popover ----
    const [showHelp, setShowHelp] = useState(false);


    // --- rendu ---
    return (
        <Box sx={{display: "flex", flexDirection: "column", height: "100vh"}}>
                <ToolBar
                    mapName={mapName}
                    setMapName={setMapName}
                    onExportJson={handleExportJson}
                    onExportPng={handleExportPng}
                    onImport={handleImportMap}
                    onUndo={handleUndo}
                    onRedo={handleRedo}
                    canUndo={history.length > 0}
                    canRedo={future.length > 0}
                    onClearAll={handleClearAll}
                    onClearLayer={handleClearLayer}
                    zoom={tileZoom}
                    setZoom={setTileZoom}
                    onShowHelp={() => setShowHelp(true)}
                />
                <Snackbar
                    open={snack.open}
                    autoHideDuration={1600}
                    onClose={() => setSnack(s => ({...s, open: false}))}
                    message={snack.msg}
                />
            <Paper elevation={3}
                   sx={{
                       width: 110,
                       minWidth: 110,
                       bgcolor: "#fff",
                       borderRadius: 0,
                       borderRight: "1px solid #bbc",
                       p: 0,
                       display: "flex",
                       flexDirection: "column"
                   }}>
                <SpritePalette
                    allSprites={allSprites}
                    selected={selectedSprite}
                    onSelect={setSelectedSprite}
                    orientation="vertical"
                />
            </Paper>
            <LayerPanel
                map={map}
                activeLayerId={activeLayerId}
                setActiveLayer={setActiveLayerId}
                onAddLayer={handleAddLayer}
                onRemoveLayer={handleRemoveLayer}
                onRenameLayer={handleRenameLayer}
                onToggleLayer={handleToggleLayer}
                onMoveLayer={handleMoveLayer}
            />
            <Box sx={{flex: 1, p: 4, display: "flex", alignItems: "center", justifyContent: "center"}}>
                <MapEditor
                    map={map}
                    onCellClick={handleCellClick}
                    onCellHover={setHoverCell}
                    allSprites={allSprites}
                    selectedSprite={selectedSprite}
                    hoverCell={hoverCell}
                    activeLayerId={activeLayerId}
                />
            </Box>
            {showHelp && (
                <div style={{
                    position: "fixed", left: "50%", top: "12%", transform: "translateX(-50%)",
                    background: "#fff", padding: 24, borderRadius: 10, boxShadow: "0 2px 16px #2222",
                    zIndex: 1000
                }}>
                    <b>Astuce d'utilisation :</b>
                    <ul>
                        <li>Click sur une case pour poser/effacer un sprite</li>
                        <li>Undo/Redo avec Annuler/Rétablir</li>
                        <li>Zoom la grille pour plus de détails</li>
                        <li>Importer/exporter vos maps (JSON/PNG)</li>
                        <li>Gérez vos couches (ajout, ordre, suppression...)</li>
                    </ul>
                    <button onClick={() => setShowHelp(false)}>Fermer</button>
                </div>
            )}
            <canvas
                ref={minimapRef}
                style={{
                    position: "fixed",
                    bottom: 16, right: 16,
                    border: "2px solid #1976d2", borderRadius: 8, background: "#fff", boxShadow: "0 2px 8px #1976d266",
                    width: map.width * 8, height: map.height * 8, zIndex: 99
                }}
                width={map.width * 8}
                height={map.height * 8}
            />
        </Box>

    );
}
