import type { Vec3 } from "@psx/shared";
import type { CharacterState } from "./CharacterModel.js";
import civilianFUrl from "../../../assets/characters/civilian-f.glb?url";
import civilianMUrl from "../../../assets/characters/civilian-m.glb?url";
import groundskeeperUrl from "../../../assets/characters/groundskeeper.glb?url";
import officerUrl from "../../../assets/characters/officer.glb?url";
import oliviaUrl from "../../../assets/characters/olivia.glb?url";
import researcherUrl from "../../../assets/characters/researcher.glb?url";
import survivorUrl from "../../../assets/characters/survivor.glb?url";

/**
 * Modular cast — each GLB is a recipe of body + parts from the character
 * creator (`pnpm cast` / `tools/src/creator/presets.ts`).
 *
 * Campaign tone: precinct investigation inside a PSX survival-horror shell.
 * You walk the building, question people, gather evidence, and open locked wings.
 */
export interface CharacterDef {
  id: string;
  name: string;
  url: string;
  /** Default world height the GLB was exported at. */
  height: number;
}

export const CHARACTER_CATALOGUE: ReadonlyMap<string, CharacterDef> = new Map(
  [
    { id: "survivor", name: "Detective", url: survivorUrl, height: 1.72 },
    { id: "olivia", name: "Det. Reyes", url: oliviaUrl, height: 1.63 },
    { id: "researcher", name: "Dr. Hale", url: researcherUrl, height: 1.66 },
    { id: "officer", name: "Sgt. Marrow", url: officerUrl, height: 1.82 },
    { id: "groundskeeper", name: "Ellis", url: groundskeeperUrl, height: 1.75 },
    { id: "civilian-f", name: "Nora Voss", url: civilianFUrl, height: 1.64 },
    { id: "civilian-m", name: "Garvey Kane", url: civilianMUrl, height: 1.76 },
  ].map((entry) => [entry.id, entry]),
);

export const PLAYER_CHARACTER_ID = "survivor";

/** Ambient cast placements — living NPCs (and crime-scene bodies) per room. */
export interface AmbientPlacement {
  characterId: string;
  roomId: string;
  position: Vec3;
  yaw: number;
  /** Defaults to idle — witnesses stand and wait to be questioned. */
  pose?: CharacterState;
  /** Optional examine / interview prompt wired as an interactable nearby. */
  examine?: { id: string; label: string; text: string };
}

/**
 * One dedicated room per NPC on Level 1 (Ashgrove Precinct).
 *
 *   Lobby ………… Ellis (custodian)
 *   Detective Office … Det. Reyes (Olivia)
 *   Interview Room … Nora Voss
 *   Witness Lounge … Garvey Kane
 *   Records ………… Dr. Hale
 *   Evidence Vault … Sgt. Marrow
 *   Player ………… survivor (Detective)
 */
export const AMBIENT_PLACEMENTS: readonly AmbientPlacement[] = [
  {
    characterId: "groundskeeper",
    roomId: "manor-foyer",
    position: { x: 3.2, y: 0, z: -1.8 },
    yaw: -Math.PI / 2,
    pose: "idle",
    examine: {
      id: "talk-ellis",
      label: "Ellis",
      text:
        'Ellis wipes grease off a ring of keys. "Night shift only. I mopped the lobby, then the upstairs hall went quiet. No shouting — just the lights flickering once. If you want the cellar, that key left the board years ago."',
    },
  },
  {
    characterId: "olivia",
    roomId: "precinct-office",
    position: { x: -2.4, y: 0, z: -1.6 },
    yaw: Math.PI * 0.25,
    pose: "idle",
    examine: {
      id: "talk-olivia",
      label: "Det. Reyes",
      text:
        'Reyes doesn\'t look up from the case board. "Victim was last seen leaving the records wing. Interview Voss and Kane first — their stories don\'t line up. Hale\'s notes are sealed until you turn the corridor valve. I\'ll hold the line here."',
    },
  },
  {
    characterId: "civilian-f",
    roomId: "precinct-interview",
    position: { x: 1.6, y: 0, z: -1.2 },
    yaw: Math.PI,
    pose: "idle",
    examine: {
      id: "talk-nora",
      label: "Nora Voss",
      text:
        '"I was in the lounge. Kane said he was getting coffee — he was gone too long. When he came back he smelled like the cellar, not the kitchen. That\'s all I\'m saying without counsel." Her hands shake, but her eyes stay fixed on the two-way glass.',
    },
  },
  {
    characterId: "civilian-m",
    roomId: "precinct-lounge",
    position: { x: -2.2, y: 0, z: 1.4 },
    yaw: Math.PI * 0.6,
    pose: "idle",
    examine: {
      id: "talk-garvey",
      label: "Garvey Kane",
      text:
        'Kane straightens his tie. "I never went downstairs. Nora is scared and inventing stairs. I sat here, I drank cold coffee, I heard the shutter in the corridor grind. Ask Marrow — if he ever comes up from evidence."',
    },
  },
  {
    characterId: "researcher",
    roomId: "manor-study",
    position: { x: -3.0, y: 0, z: -2.2 },
    yaw: Math.PI * 0.35,
    pose: "idle",
    examine: {
      id: "talk-hale",
      label: "Dr. Hale",
      text:
        'Dr. Hale keeps one glove on. "The sample from the vault reacts to the manor\'s lower air — not poison, not mould. Something that writes itself into the floorplan. The brass key on my desk opens the break-room wing. Don\'t go alone."',
    },
  },
  {
    characterId: "officer",
    roomId: "manor-cellar",
    position: { x: 2.2, y: 0, z: -2.4 },
    yaw: Math.PI * 0.85,
    pose: "slumped",
    examine: {
      id: "examine-officer",
      label: "Sgt. Marrow",
      text:
        "Uniform. Badge. Journal still in his hand. The only person who made it into evidence vault alone — and the only one who never left. His last note points back at Kane's coffee story.",
    },
  },
];

export function placementsForRoom(roomId: string): AmbientPlacement[] {
  return AMBIENT_PLACEMENTS.filter((placement) => placement.roomId === roomId);
}

/** Every ambient NPC that must have a dedicated Level-1 room. */
export const LEVEL1_NPC_ROOM_IDS: ReadonlyMap<string, string> = new Map(
  AMBIENT_PLACEMENTS.map((p) => [p.characterId, p.roomId]),
);
