"use client";

import { useState, useEffect, useCallback, useRef, useMemo } from "react";
import { PatchesPuzzle, Region } from "@/lib/generators/patches";
import WinBanner from "./WinBanner";

interface Props {
  puzzle: PatchesPuzzle;
  date: string;
  onSolve?: (elapsed: number) => void;
}

const MAX_CELL = 72;
const STORAGE_KEY = (d: string) => `patches-${d}`;

/** Tiny pixel-art preview of the polyomino shape. */
function ShapePreview({
  relCells, previewRows, previewCols, color, cellPx,
}: {
  relCells: [number, number][];
  previewRows: number;
  previewCols: number;
  color: string;
  cellPx: number;
}) {
  const filled = new Set(relCells.map(([r, c]) => `${r},${c}`));
  const gap = 1;
  return (
    <div style={{
      display: "grid",
      gridTemplateColumns: `repeat(${previewCols}, ${cellPx}px)`,
      gridTemplateRows: `repeat(${previewRows}, ${cellPx}px)`,
      gap,
      pointerEvents: "none",
    }}>
      {Array.from({ length: previewRows }, (_, r) =>
        Array.from({ length: previewCols }, (_, c) => (
          <div
            key={`${r}-${c}`}
            style={{
              width: cellPx,
              height: cellPx,
              backgroundColor: filled.has(`${r},${c}`) ? "rgba(255,255,255,0.9)" : "rgba(255,255,255,0.2)",
              borderRadius: 1,
            }}
          />
        ))
      )}
    </div>
  );
}

function checkWin(userGrid: number[][], puzzle: PatchesPuzzle): boolean {
  const { size, grid } = puzzle;
  for (let r = 0; r < size; r++)
    for (let c = 0; c < size; c++)
      if (userGrid[r][c] !== grid[r][c]) return false;
  return true;
}

export default function PatchesBoard({ puzzle, date, onSolve }: Props) {
  const { size, regions, grid } = puzzle;

  const [CELL, setCELL] = useState(() =>
    typeof window === "undefined" ? MAX_CELL : Math.min(MAX_CELL, Math.floor((window.innerWidth - 32) / size))
  );

  const hintCellSet = useMemo(
    () => new Set(regions.map(reg => `${reg.hintCell[0]},${reg.hintCell[1]}`)),
    [regions]
  );
  const regionById = useMemo(
    () => new Map<number, Region>(regions.map(reg => [reg.id, reg])),
    [regions]
  );
  const hintCellToRegion = useMemo(() => {
    const m = new Map<string, Region>();
    for (const reg of regions) m.set(`${reg.hintCell[0]},${reg.hintCell[1]}`, reg);
    return m;
  }, [regions]);

  const initGrid = useCallback((): number[][] => {
    if (typeof window !== "undefined") {
      const s = localStorage.getItem(STORAGE_KEY(date));
      if (s) return JSON.parse(s);
    }
    const g: number[][] = Array.from({ length: size }, () => Array(size).fill(-1));
    for (const reg of regions) g[reg.hintCell[0]][reg.hintCell[1]] = reg.id;
    return g;
  }, [date, regions, size]);

  const [userGrid, setUserGrid] = useState<number[][]>(initGrid);
  const [activeId, setActiveId] = useState<number | null>(null);   // active region for painting
  const [won, setWon] = useState(false);
  const [elapsed, setElapsed] = useState(0);
  const [t0] = useState(() => Date.now());

  const history = useRef<number[][][]>([]);

  // Painting state
  const painting = useRef(false);
  const paintMode = useRef<"add" | "erase">("add");
  const boardRef = useRef<HTMLDivElement>(null);
  const userGridSnap = useRef(userGrid);
  useEffect(() => { userGridSnap.current = userGrid; }, [userGrid]);

  useEffect(() => {
    const update = () => setCELL(Math.min(MAX_CELL, Math.floor((window.innerWidth - 32) / size)));
    window.addEventListener("resize", update);
    return () => window.removeEventListener("resize", update);
  }, [size]);

  useEffect(() => {
    if (won) return;
    const id = setInterval(() => setElapsed(Math.floor((Date.now() - t0) / 1000)), 500);
    return () => clearInterval(id);
  }, [won, t0]);

  useEffect(() => {
    localStorage.setItem(STORAGE_KEY(date), JSON.stringify(userGrid));
    if (!won && checkWin(userGrid, puzzle)) { setWon(true); onSolve?.(Math.floor((Date.now() - t0) / 1000)); }
  }, [userGrid, date, puzzle, won, onSolve, t0]);

  const fmt = (s: number) => `${String(Math.floor(s / 60)).padStart(2, "0")}:${String(s % 60).padStart(2, "0")}`;

  function getCellFromPoint(clientX: number, clientY: number): [number, number] | null {
    if (!boardRef.current) return null;
    const rect = boardRef.current.getBoundingClientRect();
    const r = Math.floor((clientY - rect.top) / CELL);
    const c = Math.floor((clientX - rect.left) / CELL);
    return (r >= 0 && r < size && c >= 0 && c < size) ? [r, c] : null;
  }

  /** Paint or erase a single cell for the active region. */
  const paintCell = useCallback((r: number, c: number) => {
    if (activeId === null || won) return;
    const key = `${r},${c}`;
    // Don't erase hint cells
    if (hintCellSet.has(key)) return;

    setUserGrid(prev => {
      const next = prev.map(row => [...row]);
      if (paintMode.current === "erase") {
        if (next[r][c] === activeId) next[r][c] = -1;
      } else {
        // Only paint if empty or belongs to a different non-hint region (replace)
        if (next[r][c] === -1 || (next[r][c] !== activeId && !hintCellSet.has(key))) {
          // Remove from old region if any
          next[r][c] = activeId;
        } else if (next[r][c] === activeId) {
          // Toggle off
          next[r][c] = -1;
        }
      }
      return next;
    });
  }, [activeId, won, hintCellSet]);

  const stopPainting = useCallback(() => {
    painting.current = false;
  }, []);

  useEffect(() => {
    window.addEventListener("mouseup", stopPainting);
    window.addEventListener("touchend", stopPainting);
    return () => {
      window.removeEventListener("mouseup", stopPainting);
      window.removeEventListener("touchend", stopPainting);
    };
  }, [stopPainting]);

  const undo = useCallback(() => {
    if (won || history.current.length === 0) return;
    setUserGrid(history.current.pop()!);
  }, [won]);

  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && e.key === "z") { e.preventDefault(); undo(); }
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [undo]);

  const handleCellMouseDown = useCallback((r: number, c: number, e: React.MouseEvent) => {
    if (won) return;
    e.preventDefault();
    const key = `${r},${c}`;
    const isHint = hintCellSet.has(key);

    if (isHint) {
      // Click hint cell → activate/deactivate region
      const reg = hintCellToRegion.get(key);
      if (reg) setActiveId(prev => prev === reg.id ? null : reg.id);
      return;
    }

    // Non-hint cell: need active region
    if (activeId === null) {
      // Try to detect which region the cell belongs to and erase it
      const cur = userGridSnap.current[r][c];
      if (cur !== -1) {
        history.current.push(userGridSnap.current.map(row => [...row]));
        setUserGrid(prev => {
          const next = prev.map(row => [...row]);
          for (let rr = 0; rr < size; rr++)
            for (let cc = 0; cc < size; cc++)
              if (next[rr][cc] === cur && !hintCellSet.has(`${rr},${cc}`)) next[rr][cc] = -1;
          return next;
        });
      }
      return;
    }

    // Determine paint mode: if cell already belongs to active region → erase, else → add
    const cur = userGridSnap.current[r][c];
    paintMode.current = cur === activeId ? "erase" : "add";
    painting.current = true;
    history.current.push(userGridSnap.current.map(row => [...row]));
    paintCell(r, c);
  }, [won, hintCellSet, hintCellToRegion, activeId, size, paintCell]);

  const handleMouseMove = useCallback((e: React.MouseEvent) => {
    if (!painting.current || won || activeId === null) return;
    const cell = getCellFromPoint(e.clientX, e.clientY);
    if (cell) paintCell(cell[0], cell[1]);
  }, [won, activeId, paintCell]);

  const handleTouchStart = useCallback((e: React.TouchEvent) => {
    if (won) return;
    e.preventDefault();
    const cell = getCellFromPoint(e.touches[0].clientX, e.touches[0].clientY);
    if (!cell) return;
    const [r, c] = cell;
    const key = `${r},${c}`;
    const isHint = hintCellSet.has(key);

    if (isHint) {
      const reg = hintCellToRegion.get(key);
      if (reg) setActiveId(prev => prev === reg.id ? null : reg.id);
      return;
    }

    if (activeId === null) {
      const cur = userGridSnap.current[r][c];
      if (cur !== -1) {
        setUserGrid(prev => {
          const next = prev.map(row => [...row]);
          for (let rr = 0; rr < size; rr++)
            for (let cc = 0; cc < size; cc++)
              if (next[rr][cc] === cur && !hintCellSet.has(`${rr},${cc}`)) next[rr][cc] = -1;
          return next;
        });
      }
      return;
    }

    const cur = userGridSnap.current[r][c];
    paintMode.current = cur === activeId ? "erase" : "add";
    painting.current = true;
    paintCell(r, c);
  }, [won, hintCellSet, hintCellToRegion, activeId, size, paintCell]);

  const handleTouchMove = useCallback((e: React.TouchEvent) => {
    if (!painting.current || won || activeId === null) return;
    e.preventDefault();
    const cell = getCellFromPoint(e.touches[0].clientX, e.touches[0].clientY);
    if (cell) paintCell(cell[0], cell[1]);
  }, [won, activeId, paintCell]);

  const handleHint = useCallback(() => {
    if (won) return;
    // Find first region not correctly placed, reveal one correct cell
    for (const reg of regions) {
      const correct = reg.cells.every(([r, c]) => userGridSnap.current[r][c] === reg.id);
      if (!correct) {
        // Activate this region and fill one missing correct cell
        setActiveId(reg.id);
        for (const [r, c] of reg.cells) {
          if (userGridSnap.current[r][c] !== reg.id) {
            setUserGrid(prev => {
              const next = prev.map(row => [...row]);
              next[r][c] = reg.id;
              return next;
            });
            return;
          }
        }
      }
    }
  }, [won, regions]);

  const reset = () => {
    history.current = [];
    const g: number[][] = Array.from({ length: size }, () => Array(size).fill(-1));
    for (const reg of regions) g[reg.hintCell[0]][reg.hintCell[1]] = reg.id;
    setUserGrid(g);
    setActiveId(null);
    setWon(false);
  };

  const filled = userGrid.flat().filter(v => v !== -1).length;

  // How many cells each region needs (for progress indicator inside hint cell)
  const regionCorrectCount = useMemo(() => {
    const m = new Map<number, number>();
    for (const reg of regions) {
      const correct = reg.cells.filter(([r, c]) => userGrid[r][c] === reg.id).length;
      m.set(reg.id, correct);
    }
    return m;
  }, [userGrid, regions]);

  return (
    <div className="flex flex-col items-center gap-4">
      <div className="flex items-center gap-8 text-sm text-gray-500 font-medium">
        <span>{filled} / {size * size}</span>
        <span className="tabular-nums">{fmt(elapsed)}</span>
      </div>

      {won && <WinBanner game="patches" date={date} elapsed={elapsed} />}

      {/* Board */}
      <div
        ref={boardRef}
        style={{
          display: "grid",
          gridTemplateColumns: `repeat(${size}, ${CELL}px)`,
          border: "2px solid #374151",
          borderRadius: 6,
          overflow: "hidden",
          touchAction: "none",
          userSelect: "none",
          cursor: won ? "default" : "crosshair",
        }}
        onMouseMove={handleMouseMove}
        onMouseUp={() => { painting.current = false; }}
        onMouseLeave={() => { painting.current = false; }}
        onTouchStart={handleTouchStart}
        onTouchMove={handleTouchMove}
        onTouchEnd={() => { painting.current = false; }}
      >
        {Array.from({ length: size }, (_, r) =>
          Array.from({ length: size }, (_, c) => {
            const key = `${r},${c}`;
            const isHint = hintCellSet.has(key);
            const assignedId = userGrid[r][c];
            const region = assignedId !== -1 ? regionById.get(assignedId) : undefined;
            const hintRegion = isHint ? hintCellToRegion.get(key) : undefined;
            const isActive = hintRegion ? hintRegion.id === activeId : (region?.id === activeId && activeId !== null);

            // Background
            let bg: string;
            if (region) {
              bg = region.color;
            } else if (isHint && hintRegion) {
              bg = hintRegion.color;
            } else {
              bg = "#f3f4f6";
            }

            // Opacity: if cell belongs to active region and is not a hint → show at 80%
            const opacity = !isHint && assignedId === activeId && activeId !== null ? 0.75 : 1;

            // Seamless borders within same region
            const sameAs = (dr: number, dc: number) => {
              const nr = r + dr, nc = c + dc;
              if (nr < 0 || nr >= size || nc < 0 || nc >= size) return false;
              return userGrid[nr][nc] === assignedId && assignedId !== -1;
            };
            const seamless = `1px solid ${bg}`;
            const bTop    = sameAs(-1, 0) ? seamless : "1px solid #9ca3af";
            const bBottom = sameAs( 1, 0) ? seamless : "1px solid #9ca3af";
            const bLeft   = sameAs( 0,-1) ? seamless : "1px solid #9ca3af";
            const bRight  = sameAs( 0, 1) ? seamless : "1px solid #9ca3af";

            // Hint cell: active ring
            const outline = isHint && activeId === hintRegion?.id
              ? "3px dashed rgba(255,255,255,0.9)"
              : undefined;

            // Hint cell preview size: fit shape in ~65% of cell
            const maxDim = hintRegion ? Math.max(hintRegion.previewRows, hintRegion.previewCols) : 1;
            const miniCellPx = Math.max(4, Math.floor((CELL * 0.6) / maxDim));

            return (
              <div
                key={key}
                onMouseDown={(e) => handleCellMouseDown(r, c, e)}
                style={{
                  width: CELL,
                  height: CELL,
                  backgroundColor: bg,
                  opacity,
                  borderTop: bTop,
                  borderBottom: bBottom,
                  borderLeft: bLeft,
                  borderRight: bRight,
                  boxSizing: "border-box",
                  display: "flex",
                  flexDirection: "column",
                  alignItems: "center",
                  justifyContent: "center",
                  gap: 2,
                  transition: "background-color 0.06s, opacity 0.06s",
                  position: "relative",
                  outline,
                  outlineOffset: "-3px",
                  cursor: isHint ? "pointer" : "crosshair",
                  zIndex: isHint ? 1 : 0,
                }}
              >
                {isHint && hintRegion && (
                  <>
                    <ShapePreview
                      relCells={hintRegion.relCells}
                      previewRows={hintRegion.previewRows}
                      previewCols={hintRegion.previewCols}
                      color={hintRegion.color}
                      cellPx={miniCellPx}
                    />
                    <span style={{
                      fontSize: Math.max(9, CELL * 0.18),
                      fontWeight: 800,
                      color: "rgba(255,255,255,0.95)",
                      textShadow: "0 1px 3px rgba(0,0,0,0.4)",
                      lineHeight: 1,
                      pointerEvents: "none",
                    }}>
                      {regionCorrectCount.get(hintRegion.id) ?? 0}/{hintRegion.size}
                    </span>
                  </>
                )}
              </div>
            );
          })
        )}
      </div>

      {activeId !== null && !won && (
        <p className="text-sm font-medium" style={{ color: regionById.get(activeId)?.color ?? "#6b7280" }}>
          Région active — peignez les {regionById.get(activeId)?.size} cases
        </p>
      )}

      <p className="text-xs text-gray-400 text-center max-w-[320px]">
        Cliquez sur une icône pour activer sa région, puis peignez les cases. La forme en aperçu indique la disposition à reproduire.
      </p>

      <div className="flex gap-3">
        {!won && (
          <>
            <button
              onClick={undo}
              disabled={history.current.length === 0}
              className="px-4 py-2 rounded-xl border border-gray-200 text-gray-500 hover:bg-gray-50 text-sm transition-colors disabled:opacity-30"
              title="Annuler (Ctrl+Z)"
            >
              ↺ Annuler
            </button>
            <button onClick={handleHint}
              className="px-4 py-2 rounded-xl border border-amber-200 text-amber-600 hover:bg-amber-50 text-sm transition-colors">
              Indice
            </button>
          </>
        )}
        <button
          onClick={reset}
          className="px-4 py-2 rounded-xl border border-gray-200 text-gray-500 hover:bg-gray-50 text-sm transition-colors"
        >
          Recommencer
        </button>
      </div>
    </div>
  );
}
