import type { EnemyKind } from './actionBoss/actionCombatSimulation'

export type ClaudeCraftRuntimeDungeonId =
  | 'hollow-crypt'
  | 'sunken-bastion'
  | 'gravewyrm-sanctum'
  | 'abandoned-crypt'
  | 'nythraxis-raid'

export type ClaudeCraftRuntimeSpawn = {
  hp: number
  kind?: EnemyKind
  mobId: string
  name: string
  radius?: number
  x: number
  z: number
}

export type ClaudeCraftRuntimeLayout = {
  entry: { x: number; z: number }
  scale: number
  source: ClaudeCraftRuntimeLayoutSource
  xMin: number
  xMax: number
  zMin: number
  zMax: number
}

export type ClaudeCraftRuntimeLayoutSource = {
  dais: { x: number; z: number; r: number }
  endWallHw?: number
  pillars: Array<{ x: number; z: number }>
  sideWallHd: number
  sideWallZ: number
  stubs: Array<{ x: number; z: number; hw: number; hd: number }>
  tombs: Array<{ x: number; z: number }>
  wallX?: number
}

export type ClaudeCraftRuntimeCollider =
  | { type: 'rect'; x: number; y: number; halfWidth: number; halfHeight: number }
  | { type: 'circle'; x: number; y: number; radius: number }

export type ClaudeCraftRuntimeDungeon = {
  finalBossName: string
  id: ClaudeCraftRuntimeDungeonId
  layout: ClaudeCraftRuntimeLayout
  name: string
  raid: boolean
  sourceDungeonId: string
  spawns: ClaudeCraftRuntimeSpawn[]
}

const CRYPT_LAYOUT: ClaudeCraftRuntimeLayout = {
  entry: { x: 0, z: 4 },
  scale: 5,
  source: {
    dais: { x: 0, z: 96, r: 9.5 },
    pillars: grid(10, 100, 15, [-14, 14]),
    sideWallHd: 66,
    sideWallZ: 47,
    stubs: [],
    tombs: grid(16, 92, 19, [-19, 19]),
  },
  xMin: -23,
  xMax: 23,
  zMin: -19,
  zMax: 112,
}

const SANCTUM_LAYOUT: ClaudeCraftRuntimeLayout = {
  entry: { x: 0, z: 4 },
  scale: 5,
  source: {
    dais: { x: 0, z: 146, r: 11.5 },
    pillars: [
      ...grid(10, 55, 15, [-14, 14]),
      ...grid(85, 100, 15, [-14, 14]),
      ...grid(125, 140, 15, [-14, 14]),
    ],
    sideWallHd: 89,
    sideWallZ: 69.5,
    stubs: [
      { x: -14, z: 67, hw: 9, hd: 5 },
      { x: 14, z: 67, hw: 9, hd: 5 },
      { x: -14, z: 115, hw: 9, hd: 3 },
      { x: 14, z: 115, hw: 9, hd: 3 },
    ],
    tombs: [],
  },
  xMin: -23,
  xMax: 23,
  zMin: -19,
  zMax: 158,
}

const NYTHRAXIS_LAYOUT: ClaudeCraftRuntimeLayout = {
  entry: { x: 0, z: 4 },
  scale: 3,
  source: {
    dais: { x: 0, z: 96, r: 13.5 },
    endWallHw: 231,
    pillars: [
      ...points([18, 38, 60, 82, 106], [-90, -45, 45, 90]),
    ],
    sideWallHd: 73,
    sideWallZ: 53.5,
    stubs: [],
    tombs: [
      { x: -210, z: 20 },
      { x: 210, z: 20 },
      { x: -210, z: 42 },
      { x: 210, z: 42 },
      { x: -210, z: 64 },
      { x: 210, z: 64 },
    ],
    wallX: 230,
  },
  xMin: -230,
  xMax: 230,
  zMin: -19,
  zMax: 126,
}

export const CLAUDECRAFT_RUNTIME_DUNGEONS: Record<ClaudeCraftRuntimeDungeonId, ClaudeCraftRuntimeDungeon> = {
  'hollow-crypt': {
    finalBossName: 'Morthen the Gravecaller',
    id: 'hollow-crypt',
    layout: CRYPT_LAYOUT,
    name: 'The Hollow Crypt',
    raid: false,
    sourceDungeonId: 'hollow_crypt',
    spawns: [
      spawn('crypt_shambler', 'Crypt Shambler', 190, -3, 18),
      spawn('crypt_shambler', 'Crypt Shambler', 190, 3, 19),
      spawn('crypt_shambler', 'Crypt Shambler', 190, -9, 38),
      spawn('hollow_acolyte', 'Hollow Acolyte', 170, -5, 39),
      spawn('crypt_shambler', 'Crypt Shambler', 190, 9, 54),
      spawn('hollow_acolyte', 'Hollow Acolyte', 170, 5, 55),
      spawn('bonechill_widow', 'Bonechill Widow', 200, -5, 68),
      spawn('bonechill_widow', 'Bonechill Widow', 200, -1, 70),
      spawn('sexton_marrow', 'Sexton Marrow', 326, -4, 82, 'claudecraft-mob', 24),
      spawn('hollow_acolyte', 'Hollow Acolyte', 170, 1, 83),
      spawn('morthen', 'Morthen the Gravecaller', 550, 0, 98, 'morthen-the-gravecaller', 32),
      spawn('crypt_shambler', 'Crypt Shambler', 190, -4, 96),
      spawn('crypt_shambler', 'Crypt Shambler', 190, 4, 96),
    ],
  },
  'sunken-bastion': {
    finalBossName: 'Vael the Mistcaller',
    id: 'sunken-bastion',
    layout: CRYPT_LAYOUT,
    name: 'The Sunken Bastion',
    raid: false,
    sourceDungeonId: 'sunken_bastion',
    spawns: [
      spawn('bastion_revenant', 'Bastion Revenant', 327, -3, 18),
      spawn('bastion_revenant', 'Bastion Revenant', 327, 3, 19),
      spawn('bastion_revenant', 'Bastion Revenant', 327, -9, 38),
      spawn('tidebound_acolyte', 'Tidebound Acolyte', 310, -5, 39),
      spawn('tidebound_acolyte', 'Tidebound Acolyte', 310, 9, 54),
      spawn('bastion_revenant', 'Bastion Revenant', 327, 5, 55),
      spawn('bastion_revenant', 'Bastion Revenant', 327, -5, 68),
      spawn('tidebound_acolyte', 'Tidebound Acolyte', 310, -1, 70),
      spawn('knight_commander_olen', 'Knight-Commander Olen', 458, -4, 82, 'claudecraft-mob', 25),
      spawn('bastion_revenant', 'Bastion Revenant', 327, 1, 83),
      spawn('vael_the_mistcaller', 'Vael the Mistcaller', 682, 0, 98, 'vael-the-mistcaller', 34),
      spawn('tidebound_acolyte', 'Tidebound Acolyte', 310, -4, 96),
      spawn('bastion_revenant', 'Bastion Revenant', 327, 4, 96),
    ],
  },
  'gravewyrm-sanctum': {
    finalBossName: 'Korzul the Gravewyrm',
    id: 'gravewyrm-sanctum',
    layout: SANCTUM_LAYOUT,
    name: 'Gravewyrm Sanctum',
    raid: false,
    sourceDungeonId: 'gravewyrm_sanctum',
    spawns: [
      spawn('sanctum_boneguard', 'Sanctum Boneguard', 501, -3, 16),
      spawn('sanctum_boneguard', 'Sanctum Boneguard', 501, 3, 17),
      spawn('sanctum_boneguard', 'Sanctum Boneguard', 501, -8, 30),
      spawn('sanctum_drakonid', 'Sanctum Drakonid', 548, -4, 31, 'claudecraft-mob', 24),
      spawn('sanctum_drakonid', 'Sanctum Drakonid', 548, 7, 44, 'claudecraft-mob', 24),
      spawn('sanctum_boneguard', 'Sanctum Boneguard', 501, 3, 45),
      spawn('sanctum_boneguard', 'Sanctum Boneguard', 501, -6, 58),
      spawn('sanctum_drakonid', 'Sanctum Drakonid', 548, -2, 59, 'claudecraft-mob', 24),
      spawn('korgath_the_bound', 'Korgath the Bound', 980, 0, 72, 'claudecraft-mob', 34),
      spawn('sanctum_drakonid', 'Sanctum Drakonid', 548, -7, 86, 'claudecraft-mob', 24),
      spawn('sanctum_boneguard', 'Sanctum Boneguard', 501, -3, 87),
      spawn('sanctum_boneguard', 'Sanctum Boneguard', 501, 6, 100),
      spawn('sanctum_drakonid', 'Sanctum Drakonid', 548, 2, 101, 'claudecraft-mob', 24),
      spawn('grand_necromancer_velkhar', 'Grand Necromancer Velkhar', 890, 0, 114, 'morthen-the-gravecaller', 30),
      spawn('sanctum_boneguard', 'Sanctum Boneguard', 501, -4, 112),
      spawn('sanctum_boneguard', 'Sanctum Boneguard', 501, 4, 112),
      spawn('sanctum_drakonid', 'Sanctum Drakonid', 548, -5, 130, 'claudecraft-mob', 24),
      spawn('sanctum_drakonid', 'Sanctum Drakonid', 548, -1, 132, 'claudecraft-mob', 24),
      spawn('korzul_the_gravewyrm', 'Korzul the Gravewyrm', 1380, 0, 146, 'korzul-the-gravewyrm', 42),
      spawn('sanctum_drakonid', 'Sanctum Drakonid', 548, -5, 144, 'claudecraft-mob', 24),
      spawn('sanctum_drakonid', 'Sanctum Drakonid', 548, 5, 144, 'claudecraft-mob', 24),
    ],
  },
  'abandoned-crypt': {
    finalBossName: 'Sealed Royal Door',
    id: 'abandoned-crypt',
    layout: CRYPT_LAYOUT,
    name: 'Abandoned Crypt',
    raid: false,
    sourceDungeonId: 'nythraxis_crypt',
    spawns: [],
  },
  'nythraxis-raid': {
    finalBossName: 'Nythraxis, Scourge of Thornpeak',
    id: 'nythraxis-raid',
    layout: NYTHRAXIS_LAYOUT,
    name: 'Nythraxis Raid Arena',
    raid: true,
    sourceDungeonId: 'nythraxis_boss_arena',
    spawns: [
      spawn('nythraxis_scourge_of_thornpeak', 'Nythraxis, Scourge of Thornpeak', 51239, 0, 96, 'korzul-the-gravewyrm', 54),
    ],
  },
}

export function isClaudeCraftRuntimeDungeon(dungeonId: string): dungeonId is ClaudeCraftRuntimeDungeonId {
  return dungeonId in CLAUDECRAFT_RUNTIME_DUNGEONS
}

export function getClaudeCraftRuntimeDungeon(dungeonId: ClaudeCraftRuntimeDungeonId) {
  return CLAUDECRAFT_RUNTIME_DUNGEONS[dungeonId]
}

export function mapClaudeCraftPoint(dungeonId: ClaudeCraftRuntimeDungeonId, point: { x: number; z: number }) {
  const dungeon = getClaudeCraftRuntimeDungeon(dungeonId)
  const padding = getClaudeCraftRuntimePadding()
  const contentWidth = (dungeon.layout.xMax - dungeon.layout.xMin) * dungeon.layout.scale + padding * 2
  const contentHeight = (dungeon.layout.zMax - dungeon.layout.zMin) * dungeon.layout.scale + padding * 2
  const arena = getClaudeCraftRuntimeArena(dungeonId)
  const xOffset = Math.max(0, (arena.width - contentWidth) / 2)
  const yOffset = Math.max(0, (arena.height - contentHeight) / 2)
  return {
    x: xOffset + padding + (point.x - dungeon.layout.xMin) * dungeon.layout.scale,
    y: yOffset + padding + (point.z - dungeon.layout.zMin) * dungeon.layout.scale,
  }
}

export function getClaudeCraftRuntimeArena(dungeonId: ClaudeCraftRuntimeDungeonId) {
  const dungeon = getClaudeCraftRuntimeDungeon(dungeonId)
  const padding = getClaudeCraftRuntimePadding()
  return {
    width: Math.max(960, (dungeon.layout.xMax - dungeon.layout.xMin) * dungeon.layout.scale + padding * 2),
    height: Math.max(820, (dungeon.layout.zMax - dungeon.layout.zMin) * dungeon.layout.scale + padding * 2),
    padding,
  }
}

export function getClaudeCraftRuntimeColliders(dungeonId: ClaudeCraftRuntimeDungeonId): ClaudeCraftRuntimeCollider[] {
  const dungeon = getClaudeCraftRuntimeDungeon(dungeonId)
  const layout = dungeon.layout.source
  const colliders: ClaudeCraftRuntimeCollider[] = []
  const wallX = layout.wallX ?? 23
  const endWallHw = layout.endWallHw ?? 24
  const wallHw = 1

  for (const side of [-1, 1]) {
    colliders.push(rect(dungeonId, side * wallX, layout.sideWallZ, wallHw, layout.sideWallHd))
  }
  colliders.push(rect(dungeonId, 0, dungeon.layout.zMax, endWallHw, wallHw))
  colliders.push(rect(dungeonId, 0, dungeon.layout.zMin, endWallHw, wallHw))
  for (const stub of layout.stubs) colliders.push(rect(dungeonId, stub.x, stub.z, stub.hw, stub.hd))
  for (const pillar of layout.pillars) colliders.push(circle(dungeonId, pillar.x, pillar.z, 1))
  for (const tomb of layout.tombs) colliders.push(rect(dungeonId, tomb.x, tomb.z, 1.1, 2.1))
  return colliders
}

function getClaudeCraftRuntimePadding() {
  return 72
}

function rect(
  dungeonId: ClaudeCraftRuntimeDungeonId,
  x: number,
  z: number,
  halfWidth: number,
  halfHeight: number,
): ClaudeCraftRuntimeCollider {
  const dungeon = getClaudeCraftRuntimeDungeon(dungeonId)
  const point = mapClaudeCraftPoint(dungeonId, { x, z })
  return {
    type: 'rect',
    x: point.x,
    y: point.y,
    halfWidth: halfWidth * dungeon.layout.scale,
    halfHeight: halfHeight * dungeon.layout.scale,
  }
}

function circle(dungeonId: ClaudeCraftRuntimeDungeonId, x: number, z: number, radius: number): ClaudeCraftRuntimeCollider {
  const dungeon = getClaudeCraftRuntimeDungeon(dungeonId)
  const point = mapClaudeCraftPoint(dungeonId, { x, z })
  return {
    type: 'circle',
    x: point.x,
    y: point.y,
    radius: radius * dungeon.layout.scale,
  }
}

function spawn(
  mobId: string,
  name: string,
  hp: number,
  x: number,
  z: number,
  kind: EnemyKind = 'claudecraft-mob',
  radius = 20,
): ClaudeCraftRuntimeSpawn {
  return { hp, kind, mobId, name, radius, x, z }
}

function grid(zFrom: number, zTo: number, zStep: number, xs: readonly number[]) {
  const out: Array<{ x: number; z: number }> = []
  for (let z = zFrom; z <= zTo; z += zStep) {
    for (const x of xs) out.push({ x, z })
  }
  return out
}

function points(zs: readonly number[], xs: readonly number[]) {
  const out: Array<{ x: number; z: number }> = []
  for (const z of zs) {
    for (const x of xs) out.push({ x, z })
  }
  return out
}
