"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";

const SIZE = 8;
const START = { x: 0, y: SIZE - 1 };
const EXIT = { x: SIZE - 1, y: 0 };
const SPARK_START = [
  { x: 2, y: 6 },
  { x: 5, y: 6 },
  { x: 1, y: 3 },
  { x: 6, y: 4 },
  { x: 3, y: 1 },
  { x: 6, y: 1 },
];
const WALLS = new Set(["2,7", "3,7", "4,7", "2,5", "2,4", "4,4", "5,4", "4,2", "5,2", "5,1"]);

type Point = { x: number; y: number };
type Phase = "ready" | "playing" | "won" | "lost";

const same = (a: Point, b: Point) => a.x === b.x && a.y === b.y;
const keyOf = (p: Point) => `${p.x},${p.y}`;
const inside = (p: Point) => p.x >= 0 && p.x < SIZE && p.y >= 0 && p.y < SIZE;

function freshHazards(): Point[] {
  return [
    { x: 4, y: 6 },
    { x: 6, y: 3 },
    { x: 3, y: 3 },
  ];
}

export default function SignalGarden() {
  const [phase, setPhase] = useState<Phase>("ready");
  const [player, setPlayer] = useState<Point>(START);
  const [sparks, setSparks] = useState<Point[]>(SPARK_START);
  const [hazards, setHazards] = useState<Point[]>(freshHazards);
  const [score, setScore] = useState(0);
  const [bestScore, setBestScore] = useState(0);
  const [streak, setStreak] = useState(0);
  const [timeLeft, setTimeLeft] = useState(90);
  const [message, setMessage] = useState("Route the courier through six sparks, then reach the gate.");
  const [shareStatus, setShareStatus] = useState("");
  const audioRef = useRef<AudioContext | null>(null);

  const collected = SPARK_START.length - sparks.length;
  const level = collected >= 4 ? 2 : 1;
  const progress = Math.round((collected / SPARK_START.length) * 100);

  // Audio is created only after a user gesture, so the game stays autoplay-safe.
  const ensureAudio = useCallback(() => {
    if (typeof window === "undefined") return null;
    if (!audioRef.current) {
      const AudioCtor = window.AudioContext ?? (window as Window & { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
      if (!AudioCtor) return null;
      audioRef.current = new AudioCtor();
    }
    if (audioRef.current.state === "suspended") void audioRef.current.resume();
    return audioRef.current;
  }, []);

  const playTone = useCallback((frequency: number, duration = 0.08, type: OscillatorType = "sine") => {
    const context = ensureAudio();
    if (!context) return;
    const oscillator = context.createOscillator();
    const gain = context.createGain();
    const now = context.currentTime;
    oscillator.type = type;
    oscillator.frequency.setValueAtTime(frequency, now);
    gain.gain.setValueAtTime(0.0001, now);
    gain.gain.exponentialRampToValueAtTime(0.055, now + 0.012);
    gain.gain.exponentialRampToValueAtTime(0.0001, now + duration);
    oscillator.connect(gain).connect(context.destination);
    oscillator.start(now);
    oscillator.stop(now + duration + 0.02);
  }, [ensureAudio]);

  useEffect(() => {
    const stored = Number(window.localStorage.getItem("signal-garden-best") ?? 0);
    if (Number.isFinite(stored)) setBestScore(stored);
  }, []);

  useEffect(() => {
    if (phase !== "won" && phase !== "lost") return;
    if (score <= bestScore) return;
    setBestScore(score);
    window.localStorage.setItem("signal-garden-best", String(score));
  }, [bestScore, phase, score]);

  const startGame = useCallback(() => {
    ensureAudio();
    playTone(392, 0.1, "triangle");
    window.setTimeout(() => playTone(587, 0.14, "triangle"), 85);
    setPhase("playing");
    setPlayer(START);
    setSparks(SPARK_START);
    setHazards(freshHazards());
    setScore(0);
    setStreak(0);
    setTimeLeft(90);
    setMessage("Collect the sparks. The gate opens only after the garden is charged.");
  }, [ensureAudio, playTone]);

  const shareGame = useCallback(async () => {
    const shareData = {
      title: "Signal Garden",
      text: "Can you charge the Signal Garden before the shadows catch you?",
      url: window.location.href,
    };
    try {
      if (navigator.share) {
        await navigator.share(shareData);
        setShareStatus("Shared — invite a friend to beat your score.");
        return;
      }
      await navigator.clipboard.writeText(window.location.href);
      setShareStatus("Link copied — send it to a friend.");
    } catch {
      setShareStatus("Copy the address bar link to share the garden.");
    }
  }, []);

  const finish = useCallback((next: Exclude<Phase, "ready" | "playing">, text: string) => {
    playTone(next === "won" ? 784 : 146, next === "won" ? 0.28 : 0.22, next === "won" ? "sine" : "sawtooth");
    setPhase(next);
    setMessage(text);
  }, [playTone]);

  const move = useCallback((dx: number, dy: number) => {
    if (phase !== "playing") return;
    const next = { x: player.x + dx, y: player.y + dy };
    if (!inside(next) || WALLS.has(keyOf(next))) return;
    if (hazards.some((hazard) => same(hazard, next))) {
      finish("lost", "A shadow crossed your route. Restart and keep the streak alive.");
      return;
    }
    setPlayer(next);
    const spark = sparks.find((item) => same(item, next));
    if (spark) {
      playTone(660 + (streak % 3) * 90, 0.1, "sine");
      const nextStreak = streak + 1;
      setSparks((current) => current.filter((item) => !same(item, next)));
      setStreak(nextStreak);
      setScore((current) => current + 10 + nextStreak * 5);
      setMessage(nextStreak >= 4 ? "Perfect rhythm. The garden is humming." : "Spark captured. Keep moving.");
    }
    if (same(next, EXIT) && sparks.length === 0) {
      setScore((current) => current + timeLeft * 2 + 50);
      finish("won", `Garden restored in ${90 - timeLeft}s. You made the signal sing.`);
    } else if (same(next, EXIT)) {
      setMessage("The gate is still dim. Charge every spark first.");
    }
  }, [finish, hazards, phase, player, playTone, sparks, streak, timeLeft]);

  useEffect(() => {
    const onKey = (event: KeyboardEvent) => {
      if (event.key.toLowerCase() === "r" && phase !== "ready") {
        event.preventDefault();
        startGame();
        return;
      }
      if (phase !== "playing") return;
      const controls: Record<string, Point> = {
        ArrowUp: { x: 0, y: -1 },
        w: { x: 0, y: -1 },
        ArrowDown: { x: 0, y: 1 },
        s: { x: 0, y: 1 },
        ArrowLeft: { x: -1, y: 0 },
        a: { x: -1, y: 0 },
        ArrowRight: { x: 1, y: 0 },
        d: { x: 1, y: 0 },
      };
      const direction = controls[event.key];
      if (direction) {
        event.preventDefault();
        move(direction.x, direction.y);
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [move, phase, startGame]);

  useEffect(() => {
    if (phase !== "playing") return;
    const clock = window.setInterval(() => {
      setTimeLeft((current) => {
        if (current <= 1) {
          finish("lost", "The signal faded before the gate opened. Try a tighter route.");
          return 0;
        }
        return current - 1;
      });
    }, 1000);
    return () => window.clearInterval(clock);
  }, [finish, phase]);

  useEffect(() => {
    if (phase !== "playing") return;
    const speed = level === 2 ? 520 : 720;
    const drift = window.setInterval(() => {
      setHazards((current) => current.map((hazard, index) => {
        const options = [
          { x: hazard.x + (index % 2 ? 1 : -1), y: hazard.y },
          { x: hazard.x, y: hazard.y + (index % 2 ? -1 : 1) },
          hazard,
        ].filter((candidate) => inside(candidate) && !WALLS.has(keyOf(candidate)));
        const next = options[(Date.now() + index) % options.length] ?? hazard;
        return next;
      }));
    }, speed);
    return () => window.clearInterval(drift);
  }, [level, phase]);

  useEffect(() => {
    if (phase !== "playing") return;
    if (hazards.some((hazard) => same(hazard, player))) {
      finish("lost", "A shadow crossed your route. Restart and keep the streak alive.");
    }
  }, [finish, hazards, phase, player]);

  const cells = useMemo(() => Array.from({ length: SIZE * SIZE }, (_, index) => ({ x: index % SIZE, y: Math.floor(index / SIZE) })), []);

  return (
    <main className="arcade-shell">
      <a className="arcade-back" href="/">← CashForge desk</a>
      <section className="arcade-hero">
        <div>
          <p className="arcade-eyebrow">OPENTASK ARCADE · SIGNAL GARDEN</p>
          <h1>Make the garden sing.</h1>
          <p className="arcade-lede">A short, original route-planning game. Gather every spark, dodge the roaming shadows, and open the gate before the signal fades.</p>
        </div>
        <div className="arcade-score-card" aria-label="Mission status">
          <span>Score</span><strong>{score.toString().padStart(4, "0")}</strong>
          <small>{phase === "playing" ? `${timeLeft}s remaining` : phase === "won" ? "signal restored" : phase === "lost" ? "signal lost" : "90s mission"}</small>
          <em>Best {bestScore.toString().padStart(4, "0")}</em>
        </div>
      </section>

      <section className="arcade-layout">
        <div className="garden-panel">
          <div className="garden-topline"><span>LEVEL {level}</span><span>{collected}/{SPARK_START.length} SPARKS</span><span>{progress}% CHARGED</span></div>
          <div className="garden-board" role="grid" aria-label="Signal Garden board">
            {cells.map((cell) => {
              const isWall = WALLS.has(keyOf(cell));
              const isPlayer = same(cell, player);
              const isSpark = sparks.some((spark) => same(spark, cell));
              const isHazard = hazards.some((hazard) => same(hazard, cell));
              const isExit = same(cell, EXIT);
              return <div className={["garden-cell", isWall && "wall", isPlayer && "courier", isSpark && "spark", isHazard && "shadow", isExit && "gate"].filter(Boolean).join(" ")} key={keyOf(cell)} role="gridcell" aria-label={isPlayer ? "courier" : isSpark ? "spark" : isExit ? "gate" : isWall ? "wall" : "garden tile"}>{isPlayer ? "✦" : isSpark ? "✧" : isHazard ? "◌" : isExit ? "◇" : ""}</div>;
            })}
          </div>
          <div className="arcade-message" aria-live="polite">{message}</div>
          <div className="arcade-controls" aria-label="Touch controls">
            <button onClick={() => move(0, -1)} aria-label="Move up">↑</button>
            <div><button onClick={() => move(-1, 0)} aria-label="Move left">←</button><button onClick={() => move(0, 1)} aria-label="Move down">↓</button><button onClick={() => move(1, 0)} aria-label="Move right">→</button></div>
          </div>
        </div>

        <aside className="arcade-side">
          <div className="arcade-instructions"><p className="arcade-eyebrow">MISSION BRIEF</p><h2>Charge the route.</h2><p>Move with <kbd>WASD</kbd> or arrow keys. Every spark raises your streak. The gate only opens when the board is fully charged.</p><ul><li>Collect all six sparks</li><li>Avoid moving shadows</li><li>Reach the diamond gate</li><li>Press <kbd>R</kbd> to restart</li></ul></div>
          <div className="arcade-actions">
            <button className="arcade-primary" onClick={startGame}>{phase === "playing" ? "Restart mission" : phase === "won" || phase === "lost" ? "Play again" : "Start mission"}</button>
            <button className="arcade-share" onClick={shareGame}>Share Signal Garden</button>
            <span>{phase === "ready" ? "No login. No backend. Just play." : phase === "won" ? `New route record: ${score.toString().padStart(4, "0")}` : phase === "lost" ? "Try a shorter route and protect your streak." : "Your best route is the one you remember."}</span>
            {shareStatus ? <small className="arcade-share-status" aria-live="polite">{shareStatus}</small> : null}
            <small className="arcade-local-note">Best score stays on this device only.</small>
          </div>
        </aside>
      </section>
      <footer className="arcade-footer"><span>Built for the OpenTask Arcade.</span><a href="/arcade/README.md">Play notes &amp; design brief</a></footer>
    </main>
  );
}
