import { E, N, S, W, type Dir } from "./pieces";
import type { LevelSpec, PieceWeights } from "./sim";

const W0: PieceWeights = { straight: 26, elbow: 58, cross: 8, reservoir: 0, oneway: 0 };
const W1: PieceWeights = { straight: 22, elbow: 54, cross: 16, reservoir: 0, oneway: 0 };
const W2: PieceWeights = { straight: 20, elbow: 50, cross: 16, reservoir: 10, oneway: 0 };
const W3: PieceWeights = { straight: 18, elbow: 46, cross: 16, reservoir: 10, oneway: 10 };
const W4: PieceWeights = { straight: 16, elbow: 42, cross: 18, reservoir: 12, oneway: 12 };

function L(
  partial: Omit<LevelSpec, "cols" | "rows" | "wrap" | "weights"> & {
    cols?: number;
    rows?: number;
    wrap?: boolean;
    weights?: PieceWeights;
  },
): LevelSpec {
  return {
    cols: 10,
    rows: 7,
    wrap: false,
    weights: W1,
    ...partial,
  };
}

export const CAMPAIGN: LevelSpec[] = [
  L({
    id: 1,
    name: "FIRST LIGHT",
    subtitle: "Lay a path. The flooz is patient — for now.",
    min: 5,
    delay: 16,
    speed: 0.28,
    start: { r: 3, c: 1, out: E },
    weights: W0,
  }),
  L({
    id: 2,
    name: "ELBOW ROOM",
    subtitle: "Bends cannot be rotated. Read the queue.",
    min: 7,
    delay: 14,
    speed: 0.32,
    start: { r: 1, c: 0, out: E },
    weights: { straight: 16, elbow: 70, cross: 8, reservoir: 0, oneway: 0 },
    map: [
      "..........",
      "..........",
      "....#.....",
      "..........",
      "....#.....",
      "..........",
      "..........",
    ],
  }),
  L({
    id: 3,
    name: "CROSSROADS",
    subtitle: "A four-way is two tunnels. Cross yourself for gold.",
    min: 9,
    delay: 13,
    speed: 0.36,
    start: { r: 3, c: 0, out: E },
    weights: { straight: 18, elbow: 48, cross: 28, reservoir: 0, oneway: 0 },
  }),
  L({
    id: 4,
    name: "SHIFT WORK",
    subtitle: "Bonus round. Slide tiles into a longer vein.",
    min: 8,
    delay: 22,
    speed: 0.4,
    start: { r: 3, c: 0, out: E },
    bonusSlide: true,
    password: "AQUA",
    weights: W1,
  }),
  L({
    id: 5,
    name: "RIVETS",
    subtitle: "Sealed plates cannot be replaced.",
    min: 8,
    delay: 12,
    speed: 0.4,
    start: { r: 0, c: 0, out: S },
    map: [
      "..........",
      "..##..##..",
      "..........",
      "..##..##..",
      "..........",
      "..##..##..",
      "..........",
    ],
  }),
  L({
    id: 6,
    name: "THE DRAIN",
    subtitle: "Reach the drain — but not before the meter fills.",
    min: 10,
    delay: 11,
    speed: 0.44,
    start: { r: 3, c: 0, out: E },
    end: { r: 3, c: 9, inn: W },
    map: [
      "..........",
      "...#..#...",
      "..........",
      "..........",
      "..........",
      "...#..#...",
      "..........",
    ],
  }),
  L({
    id: 7,
    name: "CISTERN",
    subtitle: "Reservoirs buy time. Fill them for a bonus.",
    min: 11,
    delay: 11,
    speed: 0.48,
    start: { r: 5, c: 0, out: E },
    weights: W2,
    map: [
      "..........",
      "....R.....",
      "..........",
      "..........",
      "..........",
      "..........",
      "..........",
    ],
  }),
  L({
    id: 8,
    name: "LOCKSTEP",
    subtitle: "Bonus round. A tighter scramble.",
    min: 10,
    delay: 20,
    speed: 0.46,
    start: { r: 2, c: 0, out: E },
    bonusSlide: true,
    password: "IRON",
    weights: W1,
  }),
  L({
    id: 9,
    name: "ONE WAY",
    subtitle: "Arrows are law. Against the current is a spill.",
    min: 11,
    delay: 10,
    speed: 0.52,
    start: { r: 3, c: 0, out: E },
    weights: W3,
    map: [
      "..........",
      "....#.....",
      "..........",
      "..........",
      "..........",
      "....#.....",
      "..........",
    ],
  }),
  L({
    id: 10,
    name: "MOBIUS",
    subtitle: "The grid wraps. Edges are doors.",
    min: 12,
    delay: 10,
    speed: 0.55,
    wrap: true,
    start: { r: 3, c: 4, out: E },
    weights: W3,
  }),
  L({
    id: 11,
    name: "GAUNTLET",
    subtitle: "Drain, rivets, and a short fuse.",
    min: 12,
    delay: 9,
    speed: 0.6,
    start: { r: 0, c: 1, out: S },
    end: { r: 6, c: 8, inn: N },
    weights: W3,
    map: [
      "..........",
      ".#.#.#.#..",
      "..........",
      ".#.#.#.#..",
      "..........",
      ".#.#.#.#..",
      "..........",
    ],
  }),
  L({
    id: 12,
    name: "KNOTS",
    subtitle: "Bonus round. Thread the longest line.",
    min: 12,
    delay: 18,
    speed: 0.5,
    start: { r: 3, c: 0, out: E },
    bonusSlide: true,
    password: "VEIN",
    weights: W2,
  }),
  L({
    id: 13,
    name: "PRESSURE",
    subtitle: "The flooz no longer waits.",
    min: 14,
    delay: 7,
    speed: 0.72,
    start: { r: 1, c: 0, out: E },
    weights: W4,
    map: [
      "..........",
      "..........",
      "..######..",
      "..........",
      "..######..",
      "..........",
      "..........",
    ],
  }),
  L({
    id: 14,
    name: "LABYRINTH",
    subtitle: "Wrap + maze. Keep a spare elbow.",
    min: 14,
    delay: 8,
    speed: 0.7,
    wrap: true,
    start: { r: 0, c: 0, out: E },
    weights: W4,
    map: [
      "..........",
      ".##.##.##.",
      "..........",
      "##......##",
      "..........",
      ".##.##.##.",
      "..........",
    ],
  }),
  L({
    id: 15,
    name: "GOLDLINE",
    subtitle: "Pre-laid bonus pipe. Route through every gilt section.",
    min: 15,
    delay: 8,
    speed: 0.78,
    start: { r: 3, c: 0, out: E },
    end: { r: 3, c: 9, inn: W },
    weights: W4,
    map: [
      "..........",
      "..g....g..",
      "..........",
      "....++....",
      "..........",
      "..g....g..",
      "..........",
    ],
  }),
  L({
    id: 16,
    name: "HEART OF THE WORKS",
    subtitle: "Everything you learned, at once.",
    min: 18,
    delay: 6.5,
    speed: 0.88,
    wrap: true,
    start: { r: 3, c: 2, out: E },
    end: { r: 3, c: 9, inn: W },
    weights: W4,
    map: [
      "..#....#..",
      "#...R....#",
      "....#.....",
      "..X.......",
      "....#.....",
      "#....r...#",
      "..#....#..",
    ],
  }),
  L({
    id: 17,
    name: "AFTERGLOW",
    subtitle: "Final bonus. Make it sing.",
    min: 14,
    delay: 16,
    speed: 0.55,
    start: { r: 3, c: 0, out: E },
    bonusSlide: true,
    password: "GOLD",
    weights: W3,
  }),
];

export const PASSWORDS: Record<string, number> = {
  AQUA: 5,
  IRON: 9,
  VEIN: 13,
  GOLD: 17,
};

export function levelById(id: number): LevelSpec | undefined {
  return CAMPAIGN.find((l) => l.id === id);
}

export function isBonus(spec: LevelSpec): boolean {
  return !!spec.bonusSlide;
}

export const DIR_FROM_CHAR: Record<string, Dir> = {
  "^": N,
  ">": E,
  v: S,
  "<": W,
};
