"use client";

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

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

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

/** Canonical key for shape comparison: sorted "r,c" list. */
function shapeKey(cells: [number, number][]): string {
  return cells
    .map(([r, c]) => `${r},${c}`)
    .sort()
    .join("|");
}

/** Mini polyomino preview rendered as a small grid. */
function ShapePreview({
  relCells, previewRows, previewCols, color, cellPx, faded,
}: {
  relCells: [number, number][];
  previewRows: number;
  previewCols: number;
  color: string;
  cellPx: number;
  faded?: boolean;
}) {
  const filled = new Set(relCells.map(([r, c]) => `${r},${c}`));
  return (
    <div
      style={{
        display: "inline-grid",
        gridTemplateColumns: `repeat(${previewCols}, ${cellPx}px)`,
        gridTemplateRows: `repeat(${previewRows}, ${cellPx}px)`,
        gap: 1.5,
        opacity: faded ? 0.35 : 1,
      }}
    >
      {Array.from({ length: previewRows }, (_, r) =>
        Array.from({ length: previewCols }, (_, c) => (
          <div
            key={`${r}-${c}`}
            style={{
              width: cellPx,
              height: cellPx,
              backgroundColor: filled.has(`${r},${c}`) ? color : "transparent",
              borderRadius: 2,
            }}
          />
        ))
      )}
    </div>
  );
}

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

  /* ── Responsive cell size ───────────────────────────────────────────────── */
  const [CELL, setCELL] = useState(() =>
    typeof window === "undefined" ? 56 : Math.min(58, Math.floor((window.innerWidth - 40) / size))
  );
  useEffect(() => {
    const update = () =>
      setCELL(Math.min(58, Math.floor((window.innerWidth - 40) / size)));
    window.addEventListener("resize", update);
    return () => window.removeEventListener("resize", update);
  }, [size]);

  /* ── Lookup maps ────────────────────────────────────────────────────────── */
  const regionById = useMemo(() => new Map<number, Region>(regions.map(r => [r.id, r])), [regions]);

  // Canonical shape key per region
  const shapeKeys = useMemo(() => {
    const m = new Map<number, string>();
    for (const reg of regions) m.set(reg.id, shapeKey(reg.relCells));
    return m;
  }, [regions]);

  // Piece tray order — shuffled deterministically per date
  const trayPieces = useMemo(() => {
    const rng = createRng(date.split("-").reduce((a, n) => a * 1000 + parseInt(n), 0) + 7777);
    return shuffle([...regions], rng);
  }, [regions, date]);

  /* ── State ──────────────────────────────────────────────────────────────── */
  // placement: Map<slotRegionId, pieceRegionId>
  const [placement, setPlacement] = useState<Map<number, number>>(() => {
    if (typeof window !== "undefined") {
      try {
        const s = localStorage.getItem(STORAGE_KEY(date));
        if (s) return new Map(JSON.parse(s) as [number, number][]);
      } catch { /* */ }
    }
    return new Map();
  });

  const [selectedId, setSelectedId] = useState<number | null>(null);
  const [errorSlot, setErrorSlot] = useState<number | null>(null); // regionId to flash red
  const [won, setWon] = useState(false);
  const [elapsed, setElapsed] = useState(0);
  const [t0] = useState(() => Date.now());

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

  /* ── Persist & win detection ────────────────────────────────────────────── */
  useEffect(() => {
    localStorage.setItem(STORAGE_KEY(date), JSON.stringify([...placement]));
    if (!won && placement.size === regions.length) {
      setWon(true);
      onSolve?.(Math.floor((Date.now() - t0) / 1000));
    }
  }, [placement, date, won, regions.length, onSolve, t0]);

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

  const placedPieceIds = useMemo(() => new Set(placement.values()), [placement]);

  /* ── Interaction ────────────────────────────────────────────────────────── */
  const handleGridTap = useCallback((slotId: number) => {
    if (won) return;

    if (selectedId === null) {
      // No piece selected — remove piece from this slot (return to tray)
      if (placement.has(slotId)) {
        setPlacement(prev => {
          const m = new Map(prev);
          m.delete(slotId);
          return m;
        });
      }
      return;
    }

    // Piece is selected
    if (placement.get(slotId) === selectedId) {
      // Tap the slot that already has this piece → deselect
      setSelectedId(null);
      return;
    }

    // Shape validation
    const slotShape = shapeKeys.get(slotId)!;
    const pieceShape = shapeKeys.get(selectedId)!;

    if (slotShape !== pieceShape) {
      // Wrong shape → error flash
      setErrorSlot(slotId);
      setTimeout(() => setErrorSlot(null), 500);
      return;
    }

    // Valid — place piece (move from any previous slot, replace any existing piece in target)
    setPlacement(prev => {
      const m = new Map(prev);
      // Remove selected piece from wherever it was
      for (const [slot, piece] of m) {
        if (piece === selectedId) { m.delete(slot); break; }
      }
      m.set(slotId, selectedId);
      return m;
    });
    setSelectedId(null);
  }, [won, selectedId, placement, shapeKeys]);

  const handleTrayTap = useCallback((pieceId: number) => {
    if (won) return;
    setSelectedId(prev => prev === pieceId ? null : pieceId);
  }, [won]);

  const reset = () => {
    setPlacement(new Map());
    setSelectedId(null);
    setWon(false);
  };

  const undoLast = () => {
    if (placement.size === 0) return;
    const entries = [...placement];
    setPlacement(new Map(entries.slice(0, -1)));
  };

  /* ── Cell border style ──────────────────────────────────────────────────── */
  function getCellStyle(r: number, c: number, slotId: number) {
    const THICK = "2.5px solid #1f2937";
    const THIN  = "1px solid #e5e7eb";

    const top    = r === 0           || grid[r - 1][c] !== slotId ? THICK : THIN;
    const bottom = r === size - 1    || grid[r + 1][c] !== slotId ? THICK : THIN;
    const left   = c === 0           || grid[r][c - 1] !== slotId ? THICK : THIN;
    const right  = c === size - 1    || grid[r][c + 1] !== slotId ? THICK : THIN;

    return { borderTop: top, borderBottom: bottom, borderLeft: left, borderRight: right };
  }

  /* ── Render ─────────────────────────────────────────────────────────────── */
  return (
    <div className="flex flex-col items-center gap-5">
      {/* Header row */}
      <div className="flex items-center gap-8 text-sm text-gray-500 font-medium">
        <span>{placement.size} / {regions.length} pièces</span>
        <span className="tabular-nums">{fmt(elapsed)}</span>
      </div>

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

      {/* Grid */}
      <div
        style={{
          display: "grid",
          gridTemplateColumns: `repeat(${size}, ${CELL}px)`,
          gridTemplateRows: `repeat(${size}, ${CELL}px)`,
          userSelect: "none",
        }}
      >
        {Array.from({ length: size }, (_, r) =>
          Array.from({ length: size }, (_, c) => {
            const slotId = grid[r][c];
            const placedPieceId = placement.get(slotId);
            const placedRegion = placedPieceId !== undefined
              ? regionById.get(placedPieceId)
              : undefined;
            const isError = errorSlot === slotId;

            // Highlight: when a piece is selected, subtly mark slots with matching shape
            const selectedShape = selectedId !== null ? shapeKeys.get(selectedId) : null;
            const isMatchingSlot = selectedShape !== null
              && shapeKeys.get(slotId) === selectedShape
              && !placement.has(slotId);

            let bg = "#f9fafb";
            if (placedRegion) bg = placedRegion.color;
            else if (isError) bg = "#fee2e2";
            else if (isMatchingSlot) bg = "#eff6ff";

            const borders = getCellStyle(r, c, slotId);

            return (
              <div
                key={`${r}-${c}`}
                onClick={() => handleGridTap(slotId)}
                style={{
                  width: CELL,
                  height: CELL,
                  boxSizing: "border-box",
                  backgroundColor: bg,
                  ...borders,
                  cursor: won ? "default" : "pointer",
                  transition: "background-color 0.12s",
                  animation: isError ? "shake 0.35s ease" : undefined,
                }}
              />
            );
          })
        )}
      </div>

      {/* Selected piece label */}
      {selectedId !== null && !won ? (
        <p className="text-sm font-medium" style={{ color: regionById.get(selectedId)?.color }}>
          Pièce sélectionnée — touche la zone correspondante
        </p>
      ) : !won ? (
        <p className="text-xs text-gray-400">
          Sélectionne une pièce, puis place-la dans la zone qui lui correspond
        </p>
      ) : null}

      {/* Piece tray */}
      <div className="w-full overflow-x-auto pb-1">
        <div
          className="flex gap-3 px-2 py-1"
          style={{ width: "max-content", margin: "0 auto" }}
        >
          {trayPieces.map(piece => {
            const isPlaced = placedPieceIds.has(piece.id);
            const isSelected = selectedId === piece.id;
            const maxDim = Math.max(piece.previewRows, piece.previewCols);
            const cellPx = Math.min(14, Math.floor(52 / maxDim));

            return (
              <button
                key={piece.id}
                onClick={() => !isPlaced && handleTrayTap(piece.id)}
                aria-pressed={isSelected}
                style={{
                  padding: "10px",
                  borderRadius: 14,
                  border: isSelected
                    ? `2.5px solid ${piece.color}`
                    : "2px solid #e5e7eb",
                  background: isSelected
                    ? `${piece.color}18`
                    : isPlaced ? "#f5f5f5" : "white",
                  opacity: isPlaced ? 0.3 : 1,
                  transform: isSelected ? "scale(1.10) translateY(-4px)" : "scale(1)",
                  transition: "transform 0.15s, box-shadow 0.15s, opacity 0.2s, border-color 0.15s",
                  boxShadow: isSelected ? `0 4px 14px ${piece.color}50` : "none",
                  cursor: isPlaced ? "default" : "pointer",
                  pointerEvents: isPlaced ? "none" : "auto",
                  flexShrink: 0,
                  minWidth: 48,
                  minHeight: 48,
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                }}
              >
                <ShapePreview
                  relCells={piece.relCells}
                  previewRows={piece.previewRows}
                  previewCols={piece.previewCols}
                  color={piece.color}
                  cellPx={cellPx}
                  faded={isPlaced}
                />
              </button>
            );
          })}
        </div>
      </div>

      {/* Controls */}
      {!won && (
        <div className="flex gap-3">
          <button
            onClick={undoLast}
            disabled={placement.size === 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"
          >
            ↺ Annuler
          </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>
  );
}
