"use client";

import { useCallback, useEffect, useRef, useState } from "react";

const WIDTH = 1120;
const HEIGHT = 700;
const BRICK_COLUMNS = 8;
const ACCENTS = ["#2d9cdb", "#7fba00", "#ffb900", "#f25022", "#67e8f9"];

const LOGOS = [
  ["AI21", "ai21"],
  ["AMD", "amd"],
  ["American Innovators Network", "american-innovators-network"],
  ["AMP", "amp"],
  ["Andreessen Horowitz", "andreessen-horowitz"],
  ["Arcee AI", "arcee-ai"],
  ["Arena", "arena"],
  ["Baseten", "baseten"],
  ["Black Forest Labs", "black-forest-labs"],
  ["Block", "block"],
  ["Box", "box"],
  ["Cisco", "cisco"],
  ["Cloudflare", "cloudflare"],
  ["Cohere", "cohere"],
  ["CrowdStrike", "crowdstrike"],
  ["Dell Technologies", "dell-technologies"],
  ["DoorDash", "doordash"],
  ["Emergence Capital", "emergence-capital"],
  ["Fireworks AI", "fireworks-ai"],
  ["Genspark", "genspark"],
  ["GitHub", "github"],
  ["Google", "google"],
  ["Hugging Face", "hugging-face"],
  ["IBM", "ibm"],
  ["Inferact", "inferact"],
  ["Interconnects AI", "interconnects-ai"],
  ["The Linux Foundation", "linux-foundation"],
  ["Mariana Minerals", "mariana-minerals"],
  ["Meta", "meta"],
  ["Microsoft", "microsoft"],
  ["Mistral", "mistral"],
  ["Morph", "morph"],
  ["Mozilla", "mozilla"],
  ["Nebius", "nebius"],
  ["Nous Research", "nous-research"],
  ["NVIDIA", "nvidia"],
  ["Ollama", "ollama"],
  ["OpenAI", "openai"],
  ["OpenClaw", "openclaw"],
  ["Palantir", "palantir"],
  ["Palo Alto Networks", "palo-alto-networks"],
  ["Periodic Labs", "periodic-labs"],
  ["Perplexity", "perplexity"],
  ["Prime Intellect", "prime-intellect"],
  ["Reflection", "reflection"],
  ["Replit", "replit"],
  ["ServiceNow", "servicenow"],
  ["Telnyx", "telnyx"],
  ["Trajectory", "trajectory"],
  ["Y Combinator", "y-combinator"],
] as const;

type Status = "loading" | "ready" | "running" | "paused" | "won" | "lost";

type Brick = {
  x: number;
  y: number;
  width: number;
  height: number;
  alive: boolean;
  name: string;
  image: HTMLImageElement;
  accent: string;
};

type Particle = {
  x: number;
  y: number;
  vx: number;
  vy: number;
  life: number;
  color: string;
};

type Game = {
  status: Status;
  score: number;
  lives: number;
  destroyed: number;
  paddle: { x: number; y: number; width: number; height: number };
  ball: { x: number; y: number; vx: number; vy: number; radius: number };
  bricks: Brick[];
  particles: Particle[];
  trail: { x: number; y: number; life: number }[];
  logoImages: HTMLImageElement[];
  paddleImage: HTMLImageElement;
};

const loadImage = (src: string) =>
  new Promise<HTMLImageElement>((resolve, reject) => {
    const image = new Image();
    image.onload = () => resolve(image);
    image.onerror = () => reject(new Error(`Could not load ${src}`));
    image.src = src;
  });

const createBricks = (images: HTMLImageElement[]): Brick[] => {
  const marginX = 28;
  const gapX = 10;
  const gapY = 7;
  const width = (WIDTH - marginX * 2 - gapX * (BRICK_COLUMNS - 1)) / BRICK_COLUMNS;
  const height = 64;
  const startY = 32;

  return LOGOS.map(([name], index) => {
    const column = index % BRICK_COLUMNS;
    const row = Math.floor(index / BRICK_COLUMNS);
    return {
      x: marginX + column * (width + gapX),
      y: startY + row * (height + gapY),
      width,
      height,
      alive: true,
      name,
      image: images[index],
      accent: ACCENTS[row % ACCENTS.length],
    };
  });
};

const makeGame = (
  logoImages: HTMLImageElement[],
  paddleImage: HTMLImageElement,
): Game => {
  const paddle = {
    x: WIDTH / 2 - 118,
    y: HEIGHT - 57,
    width: 236,
    height: 32,
  };

  return {
    status: "ready",
    score: 0,
    lives: 3,
    destroyed: 0,
    paddle,
    ball: {
      x: WIDTH / 2,
      y: paddle.y - 13,
      vx: 3.1,
      vy: -5.7,
      radius: 9,
    },
    bricks: createBricks(logoImages),
    particles: [],
    trail: [],
    logoImages,
    paddleImage,
  };
};

const roundRect = (
  context: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  radius: number,
) => {
  context.beginPath();
  context.roundRect(x, y, width, height, radius);
};

export function LogoBreakout() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const stageRef = useRef<HTMLDivElement>(null);
  const gameRef = useRef<Game | null>(null);
  const keysRef = useRef(new Set<string>());
  const soundRef = useRef<{ muted: boolean; context: AudioContext | null }>({
    muted: false,
    context: null,
  });
  const [status, setStatus] = useState<Status>("loading");
  const [score, setScore] = useState(0);
  const [lives, setLives] = useState(3);
  const [destroyed, setDestroyed] = useState(0);
  const [muted, setMuted] = useState(false);

  const tone = useCallback((frequency: number, duration = 0.055) => {
    if (soundRef.current.muted) return;

    const AudioContextClass =
      window.AudioContext ||
      (
        window as typeof window & {
          webkitAudioContext?: typeof AudioContext;
        }
      ).webkitAudioContext;
    if (!AudioContextClass) return;

    const audioContext =
      soundRef.current.context ?? new AudioContextClass();
    soundRef.current.context = audioContext;

    const oscillator = audioContext.createOscillator();
    const gain = audioContext.createGain();
    const now = audioContext.currentTime;
    oscillator.type = "sine";
    oscillator.frequency.setValueAtTime(frequency, now);
    gain.gain.setValueAtTime(0.025, now);
    gain.gain.exponentialRampToValueAtTime(0.0001, now + duration);
    oscillator.connect(gain);
    gain.connect(audioContext.destination);
    oscillator.start(now);
    oscillator.stop(now + duration);
  }, []);

  const syncUi = useCallback((game: Game) => {
    setStatus(game.status);
    setScore(game.score);
    setLives(game.lives);
    setDestroyed(game.destroyed);
  }, []);

  const launch = useCallback(() => {
    const game = gameRef.current;
    if (!game) return;

    if (game.status === "won" || game.status === "lost") {
      const fresh = makeGame(game.logoImages, game.paddleImage);
      fresh.status = "running";
      gameRef.current = fresh;
      syncUi(fresh);
      tone(520, 0.08);
      return;
    }

    if (game.status === "ready") {
      game.status = "running";
      setStatus("running");
      tone(520, 0.08);
    }
  }, [syncUi, tone]);

  const restart = useCallback(() => {
    const game = gameRef.current;
    if (!game) return;
    const fresh = makeGame(game.logoImages, game.paddleImage);
    gameRef.current = fresh;
    syncUi(fresh);
  }, [syncUi]);

  const togglePause = useCallback(() => {
    const game = gameRef.current;
    if (!game) return;
    if (game.status === "running") {
      game.status = "paused";
      setStatus("paused");
    } else if (game.status === "paused") {
      game.status = "running";
      setStatus("running");
    }
  }, []);

  useEffect(() => {
    let cancelled = false;

    const prepare = async () => {
      try {
        const [logoImages, paddleImage] = await Promise.all([
          Promise.all(
            LOGOS.map(([, slug]) => loadImage(`/logos/${slug}.png`)),
          ),
          loadImage("/anthropic-wordmark.png"),
        ]);

        if (cancelled) return;
        const game = makeGame(logoImages, paddleImage);
        gameRef.current = game;
        syncUi(game);
      } catch {
        if (!cancelled) setStatus("lost");
      }
    };

    prepare();
    return () => {
      cancelled = true;
    };
  }, [syncUi]);

  useEffect(() => {
    const keyDown = (event: KeyboardEvent) => {
      if (["ArrowLeft", "ArrowRight", " ", "p", "P"].includes(event.key)) {
        event.preventDefault();
      }
      keysRef.current.add(event.key);
      if (event.key === " ") launch();
      if (event.key === "p" || event.key === "P") togglePause();
    };
    const keyUp = (event: KeyboardEvent) => {
      keysRef.current.delete(event.key);
    };

    window.addEventListener("keydown", keyDown);
    window.addEventListener("keyup", keyUp);
    return () => {
      window.removeEventListener("keydown", keyDown);
      window.removeEventListener("keyup", keyUp);
    };
  }, [launch, togglePause]);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const context = canvas.getContext("2d");
    if (!context) return;

    let frame = 0;
    let previousTime = performance.now();

    const burst = (game: Game, brick: Brick) => {
      for (let index = 0; index < 13; index += 1) {
        const angle = Math.random() * Math.PI * 2;
        const speed = 1.5 + Math.random() * 3.5;
        game.particles.push({
          x: game.ball.x,
          y: game.ball.y,
          vx: Math.cos(angle) * speed,
          vy: Math.sin(angle) * speed,
          life: 1,
          color: brick.accent,
        });
      }
    };

    const loseLife = (game: Game) => {
      game.lives -= 1;
      game.trail = [];
      if (game.lives <= 0) {
        game.status = "lost";
        tone(120, 0.28);
      } else {
        game.status = "ready";
        game.ball.x = game.paddle.x + game.paddle.width / 2;
        game.ball.y = game.paddle.y - game.ball.radius - 2;
        game.ball.vx = 3.1;
        game.ball.vy = -5.7;
        tone(180, 0.18);
      }
      syncUi(game);
    };

    const hitBrick = (game: Game, brick: Brick) => {
      brick.alive = false;
      game.destroyed += 1;
      game.score += 100;
      burst(game, brick);
      tone(610 + (game.destroyed % 7) * 48);

      if (game.destroyed === LOGOS.length) {
        game.status = "won";
        tone(880, 0.32);
      }
      syncUi(game);
    };

    const update = (game: Game, delta: number) => {
      const paddleSpeed = 10 * delta;
      if (keysRef.current.has("ArrowLeft")) game.paddle.x -= paddleSpeed;
      if (keysRef.current.has("ArrowRight")) game.paddle.x += paddleSpeed;
      game.paddle.x = Math.max(
        14,
        Math.min(WIDTH - game.paddle.width - 14, game.paddle.x),
      );

      if (game.status === "ready") {
        game.ball.x = game.paddle.x + game.paddle.width / 2;
        game.ball.y = game.paddle.y - game.ball.radius - 2;
      }

      game.particles.forEach((particle) => {
        particle.x += particle.vx * delta;
        particle.y += particle.vy * delta;
        particle.vy += 0.09 * delta;
        particle.life -= 0.025 * delta;
      });
      game.particles = game.particles.filter((particle) => particle.life > 0);
      game.trail.forEach((point) => {
        point.life -= 0.08 * delta;
      });
      game.trail = game.trail.filter((point) => point.life > 0);

      if (game.status !== "running") return;

      const { ball, paddle } = game;
      game.trail.push({ x: ball.x, y: ball.y, life: 1 });
      if (game.trail.length > 15) game.trail.shift();

      ball.x += ball.vx * delta;
      ball.y += ball.vy * delta;

      if (ball.x - ball.radius <= 0) {
        ball.x = ball.radius;
        ball.vx = Math.abs(ball.vx);
        tone(260);
      } else if (ball.x + ball.radius >= WIDTH) {
        ball.x = WIDTH - ball.radius;
        ball.vx = -Math.abs(ball.vx);
        tone(260);
      }

      if (ball.y - ball.radius <= 0) {
        ball.y = ball.radius;
        ball.vy = Math.abs(ball.vy);
        tone(290);
      }

      if (
        ball.vy > 0 &&
        ball.y + ball.radius >= paddle.y &&
        ball.y - ball.radius <= paddle.y + paddle.height &&
        ball.x >= paddle.x &&
        ball.x <= paddle.x + paddle.width
      ) {
        ball.y = paddle.y - ball.radius;
        const offset =
          (ball.x - (paddle.x + paddle.width / 2)) / (paddle.width / 2);
        const speed = Math.min(9.2, Math.hypot(ball.vx, ball.vy) + 0.08);
        const angle = offset * 1.08;
        ball.vx = Math.sin(angle) * speed;
        ball.vy = -Math.max(4.6, Math.cos(angle) * speed);
        tone(420, 0.07);
      }

      for (const brick of game.bricks) {
        if (!brick.alive) continue;
        const closestX = Math.max(
          brick.x,
          Math.min(ball.x, brick.x + brick.width),
        );
        const closestY = Math.max(
          brick.y,
          Math.min(ball.y, brick.y + brick.height),
        );
        const distanceX = ball.x - closestX;
        const distanceY = ball.y - closestY;

        if (
          distanceX * distanceX + distanceY * distanceY <
          ball.radius * ball.radius
        ) {
          const brickCenterX = brick.x + brick.width / 2;
          const brickCenterY = brick.y + brick.height / 2;
          const normalizedX = Math.abs(ball.x - brickCenterX) / brick.width;
          const normalizedY = Math.abs(ball.y - brickCenterY) / brick.height;
          if (normalizedX > normalizedY) ball.vx *= -1;
          else ball.vy *= -1;
          hitBrick(game, brick);
          break;
        }
      }

      if (ball.y - ball.radius > HEIGHT) loseLife(game);
    };

    const draw = (game: Game) => {
      context.clearRect(0, 0, WIDTH, HEIGHT);
      context.fillStyle = "#ffffff";
      context.fillRect(0, 0, WIDTH, HEIGHT);

      for (const brick of game.bricks) {
        if (!brick.alive) continue;
        context.save();
        const cropX = brick.image.naturalWidth * 0.07;
        const cropY = brick.image.naturalHeight * 0.07;
        context.drawImage(
          brick.image,
          cropX,
          cropY,
          brick.image.naturalWidth - cropX * 2,
          brick.image.naturalHeight - cropY * 2,
          brick.x,
          brick.y,
          brick.width,
          brick.height,
        );
        context.restore();
      }

      for (const point of game.trail) {
        context.beginPath();
        context.arc(
          point.x,
          point.y,
          game.ball.radius * point.life * 0.65,
          0,
          Math.PI * 2,
        );
        context.fillStyle = `rgba(0,158,209,${point.life * 0.16})`;
        context.fill();
      }

      for (const particle of game.particles) {
        context.beginPath();
        context.arc(particle.x, particle.y, 3.2 * particle.life, 0, Math.PI * 2);
        context.globalAlpha = Math.max(0, particle.life);
        context.fillStyle = particle.color;
        context.fill();
      }
      context.globalAlpha = 1;

      context.save();
      context.shadowColor = "#009ed1";
      context.shadowBlur = 14;
      context.beginPath();
      context.arc(
        game.ball.x,
        game.ball.y,
        game.ball.radius,
        0,
        Math.PI * 2,
      );
      context.fillStyle = "#009ed1";
      context.fill();
      context.restore();

      const { paddle } = game;
      context.save();
      context.shadowColor = "rgba(0,0,0,.14)";
      context.shadowBlur = 12;
      roundRect(
        context,
        paddle.x,
        paddle.y,
        paddle.width,
        paddle.height,
        8,
      );
      context.fillStyle = "#f2f2f2";
      context.fill();
      context.shadowBlur = 0;
      context.drawImage(
        game.paddleImage,
        paddle.x + 25,
        paddle.y + 8,
        paddle.width - 50,
        paddle.height - 16,
      );
      context.restore();
    };

    const loop = (time: number) => {
      const delta = Math.min(1.65, (time - previousTime) / 16.667);
      previousTime = time;
      const game = gameRef.current;
      if (game) {
        update(game, delta);
        draw(game);
      } else {
        context.fillStyle = "#ffffff";
        context.fillRect(0, 0, WIDTH, HEIGHT);
      }
      frame = requestAnimationFrame(loop);
    };

    frame = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(frame);
  }, [syncUi, tone]);

  const movePaddle = (clientX: number) => {
    const game = gameRef.current;
    const stage = stageRef.current;
    if (!game || !stage) return;
    const bounds = stage.getBoundingClientRect();
    const x = ((clientX - bounds.left) / bounds.width) * WIDTH;
    game.paddle.x = Math.max(
      14,
      Math.min(WIDTH - game.paddle.width - 14, x - game.paddle.width / 2),
    );
  };

  const overlay = {
    loading: {
      kicker: "Initializing field",
      title: "Loading logos",
      copy: "Preparing all 50 signatory nodes.",
      action: "Please wait",
    },
    ready: {
      kicker: "",
      title: "",
      copy: "",
      action: "Start",
    },
    paused: {
      kicker: "Field suspended",
      title: "Paused",
      copy: "The signal is holding. Resume whenever you’re ready.",
      action: "Resume",
    },
    won: {
      kicker: "Mission complete",
      title: "Field cleared",
      copy: "All 50 signatory logos are down. The weights are open.",
      action: "Play again",
    },
    lost: {
      kicker: "Signal lost",
      title: "Try another run",
      copy: `${destroyed} of 50 logos cleared. Reconnect and finish the field.`,
      action: "Restart mission",
    },
    running: null,
  }[status];

  return (
    <section className="game-console" id="game" aria-label="Logo Breakout game">
      <div className="console-topbar">
        <div className="console-id">Field online</div>
        <div className="hud" aria-live="polite">
          <div className="hud-item">
            <span>Score</span>
            <strong>{String(score).padStart(5, "0")}</strong>
          </div>
          <div className="hud-item">
            <span>Lives</span>
            <strong>{"●".repeat(lives) || "—"}</strong>
          </div>
          <div className="hud-item">
            <span>Cleared</span>
            <strong>{destroyed}/50</strong>
          </div>
        </div>
        <div className="console-actions">
          <button
            className="icon-button"
            type="button"
            onClick={() => {
              const next = !muted;
              setMuted(next);
              soundRef.current.muted = next;
            }}
            aria-label={muted ? "Turn sound on" : "Turn sound off"}
            title={muted ? "Sound off" : "Sound on"}
          >
            {muted ? "×" : "♪"}
          </button>
          <button
            className="restart-button"
            type="button"
            onClick={restart}
            aria-label="Restart game"
          >
            Restart
          </button>
        </div>
      </div>

      <div
        className="game-stage"
        ref={stageRef}
        onPointerMove={(event) => movePaddle(event.clientX)}
        onPointerDown={(event) => {
          movePaddle(event.clientX);
          launch();
        }}
      >
        <canvas
          ref={canvasRef}
          width={WIDTH}
          height={HEIGHT}
          aria-label="Breakout playfield containing 50 company logo bricks"
        />
        {overlay && (
          <div className="game-overlay">
            {status === "ready" ? (
              <button
                className="launch-button start-button"
                type="button"
                onClick={(event) => {
                  event.stopPropagation();
                  launch();
                }}
              >
                Start
              </button>
            ) : (
              <div className="overlay-card">
                <p className="overlay-kicker">{overlay.kicker}</p>
                <h2>{overlay.title}</h2>
                <p>{overlay.copy}</p>
                <button
                  className="launch-button"
                  type="button"
                  onClick={(event) => {
                    event.stopPropagation();
                    if (status === "paused") togglePause();
                    else launch();
                  }}
                  disabled={status === "loading"}
                >
                  {overlay.action}
                </button>
              </div>
            )}
          </div>
        )}
      </div>

      <div className="console-bottom">
        <div className="control-list" aria-label="Game controls">
          <span>
            <kbd>←</kbd>
            <kbd>→</kbd> Move
          </span>
          <span>
            <kbd>Space</kbd> Launch
          </span>
          <span>
            <kbd>P</kbd> Pause
          </span>
        </div>
        <div className="field-counter">Anthropic / Control node</div>
      </div>

      <div className="sr-only">
        The logo bricks are: {LOGOS.map(([name]) => name).join(", ")}.
      </div>
    </section>
  );
}
