"use client";

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

const COLS = 10;
const ROWS = 18;
const CELL = 42;
const BOARD_WIDTH = COLS * CELL;
const BOARD_HEIGHT = ROWS * CELL;
const TARGET_ROW = 11;
const TARGET_LAYERS = ROWS - TARGET_ROW;

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

const SHAPES = {
  I: [
    { x: 0, y: 0 },
    { x: 1, y: 0 },
    { x: 2, y: 0 },
    { x: 3, y: 0 },
  ],
  O: [
    { x: 0, y: 0 },
    { x: 1, y: 0 },
    { x: 0, y: 1 },
    { x: 1, y: 1 },
  ],
  T: [
    { x: 1, y: 0 },
    { x: 0, y: 1 },
    { x: 1, y: 1 },
    { x: 2, y: 1 },
  ],
  L: [
    { x: 0, y: 0 },
    { x: 0, y: 1 },
    { x: 1, y: 1 },
    { x: 2, y: 1 },
  ],
  J: [
    { x: 2, y: 0 },
    { x: 0, y: 1 },
    { x: 1, y: 1 },
    { x: 2, y: 1 },
  ],
  S: [
    { x: 1, y: 0 },
    { x: 2, y: 0 },
    { x: 0, y: 1 },
    { x: 1, y: 1 },
  ],
  Z: [
    { x: 0, y: 0 },
    { x: 1, y: 0 },
    { x: 1, y: 1 },
    { x: 2, y: 1 },
  ],
} as const;

type ShapeId = keyof typeof SHAPES;
type Status = "loading" | "ready" | "running" | "paused" | "won" | "lost";
type Point = { x: number; y: number };
type QueueEntry = { shapeId: ShapeId; logoIndex: number };
type BoardCell = { groupId: number; logoIndex: number };
type Piece = QueueEntry & {
  cells: Point[];
  x: number;
  y: number;
  groupId: number;
};
type Game = {
  status: Status;
  board: (BoardCell | null)[][];
  current: Piece | null;
  queue: QueueEntry[];
  shapeBag: ShapeId[];
  logoBag: number[];
  held: QueueEntry | null;
  holdUsed: boolean;
  pieces: number;
  completedLayers: number;
  nextGroupId: number;
  lastDrop: number;
  images: HTMLImageElement[];
};
type Action =
  | "start"
  | "restart"
  | "pause"
  | "left"
  | "right"
  | "down"
  | "rotate"
  | "drop"
  | "hold";

const shuffle = <T,>(values: T[]): T[] => {
  const result = [...values];
  for (let index = result.length - 1; index > 0; index -= 1) {
    const swapIndex = Math.floor(Math.random() * (index + 1));
    [result[index], result[swapIndex]] = [result[swapIndex], result[index]];
  }
  return result;
};

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 normalizeCells = (cells: Point[]): Point[] => {
  const minX = Math.min(...cells.map((cell) => cell.x));
  const minY = Math.min(...cells.map((cell) => cell.y));
  return cells.map((cell) => ({ x: cell.x - minX, y: cell.y - minY }));
};

const rotateCells = (shapeId: ShapeId, cells: Point[]): Point[] => {
  if (shapeId === "O") return cells;
  return normalizeCells(cells.map((cell) => ({ x: -cell.y, y: cell.x })));
};

const refillQueue = (game: Game) => {
  while (game.queue.length < 4) {
    if (!game.shapeBag.length) {
      game.shapeBag = shuffle(Object.keys(SHAPES) as ShapeId[]);
    }
    if (!game.logoBag.length) {
      game.logoBag = shuffle(LOGOS.map((_, index) => index));
    }
    game.queue.push({
      shapeId: game.shapeBag.shift() as ShapeId,
      logoIndex: game.logoBag.shift() as number,
    });
  }
};

const canPlace = (
  game: Game,
  cells: Point[],
  offsetX: number,
  offsetY: number,
) =>
  cells.every((cell) => {
    const x = offsetX + cell.x;
    const y = offsetY + cell.y;
    return (
      x >= 0 &&
      x < COLS &&
      y >= 0 &&
      y < ROWS &&
      game.board[y][x] === null
    );
  });

const makePiece = (game: Game, entry: QueueEntry): Piece => {
  const cells = SHAPES[entry.shapeId].map((cell) => ({ ...cell }));
  const width = Math.max(...cells.map((cell) => cell.x)) + 1;
  return {
    ...entry,
    cells,
    x: Math.floor((COLS - width) / 2),
    y: 0,
    groupId: game.nextGroupId++,
  };
};

const spawnNext = (game: Game) => {
  refillQueue(game);
  const entry = game.queue.shift() as QueueEntry;
  refillQueue(game);
  game.current = makePiece(game, entry);
  game.lastDrop = performance.now();
  if (!canPlace(game, game.current.cells, game.current.x, game.current.y)) {
    game.status = "lost";
    game.current = null;
  }
};

const countCompletedLayers = (game: Game) => {
  let layers = 0;
  for (let row = ROWS - 1; row >= TARGET_ROW; row -= 1) {
    if (game.board[row].every(Boolean)) layers += 1;
  }
  return layers;
};

const lockPiece = (game: Game) => {
  if (!game.current) return;
  for (const cell of game.current.cells) {
    const x = game.current.x + cell.x;
    const y = game.current.y + cell.y;
    game.board[y][x] = {
      groupId: game.current.groupId,
      logoIndex: game.current.logoIndex,
    };
  }

  game.pieces += 1;
  game.completedLayers = countCompletedLayers(game);
  game.current = null;
  game.holdUsed = false;

  if (game.completedLayers === TARGET_LAYERS) {
    game.status = "won";
    return;
  }

  spawnNext(game);
};

const createGame = (images: HTMLImageElement[]): Game => {
  const game: Game = {
    status: "ready",
    board: Array.from({ length: ROWS }, () =>
      Array.from({ length: COLS }, () => null),
    ),
    current: null,
    queue: [],
    shapeBag: [],
    logoBag: [],
    held: null,
    holdUsed: false,
    pieces: 0,
    completedLayers: 0,
    nextGroupId: 1,
    lastDrop: performance.now(),
    images,
  };
  refillQueue(game);
  spawnNext(game);
  return game;
};

const absoluteCells = (piece: Piece, yOverride = piece.y) =>
  piece.cells.map((cell) => ({
    x: piece.x + cell.x,
    y: yOverride + cell.y,
  }));

const movePiece = (game: Game, x: number, y: number) => {
  if (!game.current) return false;
  if (
    !canPlace(
      game,
      game.current.cells,
      game.current.x + x,
      game.current.y + y,
    )
  ) {
    return false;
  }
  game.current.x += x;
  game.current.y += y;
  return true;
};

const tryRotate = (game: Game) => {
  if (!game.current) return false;
  const rotated = rotateCells(game.current.shapeId, game.current.cells);
  for (const kick of [0, -1, 1, -2, 2]) {
    if (canPlace(game, rotated, game.current.x + kick, game.current.y)) {
      game.current.cells = rotated;
      game.current.x += kick;
      return true;
    }
  }
  return false;
};

const ghostY = (game: Game) => {
  if (!game.current) return 0;
  let y = game.current.y;
  while (canPlace(game, game.current.cells, game.current.x, y + 1)) y += 1;
  return y;
};

const drawLogoGroup = (
  context: CanvasRenderingContext2D,
  cells: Point[],
  image: HTMLImageElement,
  alpha = 1,
) => {
  if (!cells.length) return;

  const minX = Math.min(...cells.map((cell) => cell.x));
  const maxX = Math.max(...cells.map((cell) => cell.x));
  const minY = Math.min(...cells.map((cell) => cell.y));
  const maxY = Math.max(...cells.map((cell) => cell.y));
  const x = minX * CELL;
  const y = minY * CELL;
  const width = (maxX - minX + 1) * CELL;
  const height = (maxY - minY + 1) * CELL;

  context.save();
  context.globalAlpha = alpha;
  context.fillStyle = "#efefec";
  for (const cell of cells) {
    context.fillRect(cell.x * CELL, cell.y * CELL, CELL, CELL);
  }

  context.beginPath();
  for (const cell of cells) {
    context.rect(cell.x * CELL, cell.y * CELL, CELL, CELL);
  }
  context.clip();
  context.globalCompositeOperation = "multiply";

  const maxWidth = Math.max(1, width - 12);
  const maxHeight = Math.max(1, height - 12);
  const scale = Math.min(
    maxWidth / image.naturalWidth,
    maxHeight / image.naturalHeight,
  );
  const drawWidth = image.naturalWidth * scale;
  const drawHeight = image.naturalHeight * scale;
  context.drawImage(
    image,
    x + (width - drawWidth) / 2,
    y + (height - drawHeight) / 2,
    drawWidth,
    drawHeight,
  );
  context.restore();
};

export function LogoAlliance() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const gameRef = useRef<Game | null>(null);
  const soundRef = useRef<{ muted: boolean; context: AudioContext | null }>({
    muted: false,
    context: null,
  });
  const [status, setStatus] = useState<Status>("loading");
  const [pieces, setPieces] = useState(0);
  const [layers, setLayers] = 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.018, 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);
    setPieces(game.pieces);
    setLayers(game.completedLayers);
  }, []);

  const dispatch = useCallback(
    (action: Action) => {
      let game = gameRef.current;
      if (!game) return;

      if (action === "restart") {
        game = createGame(game.images);
        gameRef.current = game;
        syncUi(game);
        return;
      }

      if (action === "start") {
        if (game.status === "won" || game.status === "lost") {
          game = createGame(game.images);
          game.status = "running";
          gameRef.current = game;
          syncUi(game);
          tone(520, 0.08);
          return;
        }
        if (game.status === "ready") {
          game.status = "running";
          game.lastDrop = performance.now();
          syncUi(game);
          tone(520, 0.08);
        }
        return;
      }

      if (action === "pause") {
        if (game.status === "running") game.status = "paused";
        else if (game.status === "paused") {
          game.status = "running";
          game.lastDrop = performance.now();
        }
        syncUi(game);
        return;
      }

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

      if (action === "left" || action === "right") {
        if (movePiece(game, action === "left" ? -1 : 1, 0)) tone(250, 0.025);
        return;
      }

      if (action === "rotate") {
        if (tryRotate(game)) tone(340, 0.04);
        return;
      }

      if (action === "down") {
        if (!movePiece(game, 0, 1)) {
          const before = game.completedLayers;
          lockPiece(game);
          tone(game.status === "won" ? 880 : game.completedLayers > before ? 720 : 420, 0.09);
          syncUi(game);
        }
        game.lastDrop = performance.now();
        return;
      }

      if (action === "drop") {
        const landingY = ghostY(game);
        game.current.y = landingY;
        const before = game.completedLayers;
        lockPiece(game);
        tone(game.status === "won" ? 880 : game.completedLayers > before ? 720 : 420, 0.1);
        syncUi(game);
        return;
      }

      if (action === "hold" && !game.holdUsed) {
        const currentEntry = {
          shapeId: game.current.shapeId,
          logoIndex: game.current.logoIndex,
        };
        if (game.held) {
          const heldEntry = game.held;
          game.held = currentEntry;
          game.current = makePiece(game, heldEntry);
          if (!canPlace(game, game.current.cells, game.current.x, game.current.y)) {
            game.status = "lost";
            game.current = null;
          }
        } else {
          game.held = currentEntry;
          game.current = null;
          spawnNext(game);
        }
        game.holdUsed = true;
        tone(300, 0.06);
        syncUi(game);
      }
    },
    [syncUi, tone],
  );

  useEffect(() => {
    let cancelled = false;
    Promise.all(LOGOS.map(([, , src]) => loadImage(src)))
      .then((images) => {
        if (cancelled) return;
        const game = createGame(images);
        gameRef.current = game;
        syncUi(game);
      })
      .catch(() => {
        if (!cancelled) setStatus("lost");
      });
    return () => {
      cancelled = true;
    };
  }, [syncUi]);

  useEffect(() => {
    const keyDown = (event: KeyboardEvent) => {
      const actionByKey: Record<string, Action | undefined> = {
        ArrowLeft: "left",
        ArrowRight: "right",
        ArrowDown: "down",
        ArrowUp: "rotate",
        " ": status === "ready" ? "start" : "drop",
        c: "hold",
        C: "hold",
        p: "pause",
        P: "pause",
      };
      const action = actionByKey[event.key];
      if (!action) return;
      event.preventDefault();
      dispatch(action);
    };
    window.addEventListener("keydown", keyDown);
    return () => window.removeEventListener("keydown", keyDown);
  }, [dispatch, status]);

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

    let frame = 0;

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

      context.fillStyle = "rgba(0, 158, 209, 0.028)";
      context.fillRect(
        0,
        TARGET_ROW * CELL,
        BOARD_WIDTH,
        TARGET_LAYERS * CELL,
      );

      const groups = new Map<
        number,
        { cells: Point[]; logoIndex: number }
      >();
      game.board.forEach((row, y) => {
        row.forEach((cell, x) => {
          if (!cell) return;
          const group = groups.get(cell.groupId) ?? {
            cells: [],
            logoIndex: cell.logoIndex,
          };
          group.cells.push({ x, y });
          groups.set(cell.groupId, group);
        });
      });

      groups.forEach((group) => {
        drawLogoGroup(
          context,
          group.cells,
          game.images[group.logoIndex],
        );
      });

      for (let row = TARGET_ROW; row < ROWS; row += 1) {
        if (!game.board[row].every(Boolean)) continue;
        context.fillStyle = "rgba(0, 158, 209, 0.11)";
        context.fillRect(0, row * CELL + 2, BOARD_WIDTH, CELL - 4);
      }

      if (game.current) {
        const landingY = ghostY(game);
        if (landingY !== game.current.y) {
          const ghostCells = absoluteCells(game.current, landingY);
          context.save();
          context.fillStyle = "rgba(17, 17, 17, 0.055)";
          for (const cell of ghostCells) {
            context.fillRect(
              cell.x * CELL + 4,
              cell.y * CELL + 4,
              CELL - 8,
              CELL - 8,
            );
          }
          context.restore();
        }

        drawLogoGroup(
          context,
          absoluteCells(game.current),
          game.images[game.current.logoIndex],
        );
      }

      const lineY = TARGET_ROW * CELL;
      context.save();
      context.strokeStyle = "#009ed1";
      context.lineWidth = 4;
      context.beginPath();
      context.moveTo(0, lineY);
      context.lineTo(BOARD_WIDTH, lineY);
      context.stroke();
      context.fillStyle = "#009ed1";
      context.font = "700 11px ui-monospace, monospace";
      context.letterSpacing = "1.5px";
      context.fillText("ALLIANCE", 10, lineY - 10);
      context.restore();
    };

    const loop = (time: number) => {
      const game = gameRef.current;
      if (game) {
        if (game.status === "running" && game.current) {
          const interval = Math.max(250, 720 - game.pieces * 8);
          if (time - game.lastDrop >= interval) {
            if (!movePiece(game, 0, 1)) {
              const before = game.completedLayers;
              lockPiece(game);
              tone(
                game.status === "won"
                  ? 880
                  : game.completedLayers > before
                    ? 720
                    : 420,
                0.09,
              );
              syncUi(game);
            }
            game.lastDrop = time;
          }
        }
        draw(game);
      } else {
        context.fillStyle = "#ffffff";
        context.fillRect(0, 0, BOARD_WIDTH, BOARD_HEIGHT);
      }
      frame = requestAnimationFrame(loop);
    };

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

  const overlay =
    status === "paused"
      ? {
          kicker: "Alliance suspended",
          title: "Paused",
          copy: "The structure is holding.",
          action: "Resume",
        }
      : status === "won"
        ? {
            kicker: "Foundation complete",
            title: "Alliance!",
            copy: `${pieces} logo pieces formed seven permanent layers.`,
            action: "Build again",
          }
        : status === "lost"
          ? {
              kicker: "Structure blocked",
              title: "Try again",
              copy: `${layers} of ${TARGET_LAYERS} alliance layers completed.`,
              action: "Restart",
            }
          : status === "loading"
            ? {
                kicker: "Preparing the alliance",
                title: "Loading logos",
                copy: "Assembling all 51 participants.",
                action: "Please wait",
              }
            : null;

  return (
    <section className="alliance-game" aria-label="Alliance logo Tetris game">
      <header className="alliance-header">
        <div className="alliance-title">
          <span aria-hidden="true" />
          Alliance
        </div>
        <div className="alliance-hud" aria-live="polite">
          <div>
            <span>Layers</span>
            <strong>
              {layers}/{TARGET_LAYERS}
            </strong>
          </div>
          <div>
            <span>Pieces</span>
            <strong>{String(pieces).padStart(2, "0")}</strong>
          </div>
        </div>
        <div className="alliance-actions">
          <button
            type="button"
            onClick={() => dispatch("pause")}
            disabled={status !== "running" && status !== "paused"}
          >
            {status === "paused" ? "Resume" : "Pause"}
          </button>
          <button type="button" onClick={() => dispatch("restart")}>
            Restart
          </button>
        </div>
      </header>

      <div className="alliance-layout">
        <div className="board-shell">
          <canvas
            ref={canvasRef}
            width={BOARD_WIDTH}
            height={BOARD_HEIGHT}
            aria-label="Ten-column Tetris board with an Alliance target line"
          />
          {status === "ready" && (
            <div className="alliance-overlay">
              <button
                className="primary-action start-action"
                type="button"
                onClick={() => dispatch("start")}
              >
                Start
              </button>
            </div>
          )}
          {overlay && (
            <div className="alliance-overlay">
              <div className="status-card">
                <p>{overlay.kicker}</p>
                <h2>{overlay.title}</h2>
                <span>{overlay.copy}</span>
                <button
                  className="primary-action"
                  type="button"
                  disabled={status === "loading"}
                  onClick={() =>
                    dispatch(status === "paused" ? "pause" : "start")
                  }
                >
                  {overlay.action}
                </button>
              </div>
            </div>
          )}
        </div>

      </div>

      <div className="touch-controls" aria-label="Touch controls">
        <button type="button" onClick={() => dispatch("hold")}>
          Hold
        </button>
        <button type="button" onClick={() => dispatch("left")}>
          ←
        </button>
        <button type="button" onClick={() => dispatch("rotate")}>
          ↻
        </button>
        <button type="button" onClick={() => dispatch("right")}>
          →
        </button>
        <button type="button" onClick={() => dispatch("down")}>
          ↓
        </button>
        <button type="button" onClick={() => dispatch("drop")}>
          Drop
        </button>
      </div>

      <button
        className="sound-toggle"
        type="button"
        onClick={() => {
          const nextMuted = !muted;
          setMuted(nextMuted);
          soundRef.current.muted = nextMuted;
        }}
        aria-label={muted ? "Turn sound on" : "Turn sound off"}
      >
        {muted ? "Sound off" : "Sound on"}
      </button>
    </section>
  );
}
