import { useEffect, useMemo, useRef, useState } from 'react'
import { useGameAction } from '../../input'
import { getGameMode } from '../../gameRepository'
import { startPvpQueueWithCpuFallback } from '../../pvpQueueLifecycle'
import { summarizeChoiceStacks } from '../../combat/roguelikeUpgrades'
import {
  useDualScreen,
  useDualScreenSetupPublisher,
  useDualScreenWorkshopPublisher,
  type DualScreenSetupState,
  type DualScreenWorkshopState,
} from '../../dualScreen'
import { BossArenaScreen } from './screens/BossArenaScreen'
import {
  Iwt2CloudSaveScreen,
  Iwt2CustomizeCharacterScreen,
  Iwt2DungeonsScreen,
  Iwt2GearUpgradeScreen,
  Iwt2HunterProfileScreen,
  Iwt2ModeScreen,
  Iwt2RoguelikeScreen,
  Iwt2RoguelikeUpgradeScreen,
  Iwt2SettingsScreen,
} from './screens/Iwt2ShellScreens'
import {
  loadIwt2Save,
  writeIwt2Save,
  type Iwt2Save,
} from './save/iwt2Repository'
import { IWT2_BOSS_METADATA, type Iwt2BossId } from './content/bosses'
import { iwt2BossCoinRewardFor } from './content/bossRewards'
import {
  findIwt2Difficulty,
  IWT2_DUNGEON_DIFFICULTIES,
  IWT2_RAID_DIFFICULTIES,
  type Iwt2Difficulty,
} from './content/difficulties'
import { abilitiesForHealer, IWT2_HEALER_METADATA } from './content/healerAbilities'
import {
  buildIwt2OpponentDebuffChoices,
  buildIwt2SelfBuffChoices,
  IWT2_REVIVE_PARTY_CHOICE,
  type Iwt2RoguelikeChoice,
  type Iwt2RoguelikeContentType,
  type Iwt2RoguelikeOpponentDebuffId,
  type Iwt2RoguelikeSelfBuffId,
  type Iwt2RoguelikeVariant,
} from './content/roguelike'
import {
  createIwt2WeightedRoguelikeBossPair,
  createUniformIwt2RoguelikeBossPair,
  enabledIwt2RoguelikeBossPool,
  IWT2_PVP_ROGUELIKE_BOSS_ROSTER_LIMIT_ENABLED,
  IWT2_PVP_ROGUELIKE_ENABLED_BOSS_IDS,
  IWT2_PVP_STADIUM_BOSS_ROSTER_LIMIT_ENABLED,
  IWT2_PVP_STADIUM_ENABLED_BOSS_IDS,
  IWT2_PVE_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED,
  IWT2_PVP_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED,
  IWT2_PVP_STADIUM_WEIGHTED_BOSS_PROGRESSION_ENABLED,
} from './content/roguelikeBossProgression'

type Iwt2Screen =
  | 'menu'
  | 'arena'
  | 'cloud-save'
  | 'dungeons'
  | 'raids'
  | 'roguelike'
  | 'roguelike-arena'
  | 'roguelike-upgrade'
  | 'gear-upgrade'
  | 'hunter-profile'
  | 'customize-character'
  | 'settings'

const IWT2_MENU_COLUMNS = 2
const IWT2_ROGUELIKE_CHOICE_COUNT = 3
const IWT2_PVP_FIRST_BUFF_EXTRA_TARGET_CHANCE = 0.65
const IWT2_ROGUELIKE_GREEN_COIN_BOSS_THRESHOLD = 5

type Iwt2RoguelikeRunState = {
  bossIds: Iwt2BossId[]
  bossesDefeated: number
  buffs: Iwt2RoguelikeSelfBuffId[]
  contentType: Iwt2RoguelikeContentType
  debuffs: Iwt2RoguelikeOpponentDebuffId[]
  debuffChoices: Array<Iwt2RoguelikeChoice<Iwt2RoguelikeOpponentDebuffId>>
  selfChoices: Array<Iwt2RoguelikeChoice<Iwt2RoguelikeSelfBuffId>>
  stage: number
  variant: Iwt2RoguelikeVariant
}

const MENU_ITEMS: Array<{
  screen: Iwt2Screen
  title: string
  description: string
  glyph: string
}> = [
  {
    screen: 'dungeons',
    title: 'Dungeons',
    description: 'Queue into seven modular IWT2 boss arenas.',
    glyph: 'D',
  },
  {
    screen: 'raids',
    title: 'Raids',
    description: 'Open raid assignments built from active IWT2 boss mechanics.',
    glyph: 'R',
  },
  {
    screen: 'roguelike',
    title: 'Roguelike',
    description: 'Draft upgrades through escalating random encounters.',
    glyph: 'L',
  },
  {
    screen: 'roguelike',
    title: 'PvP',
    description: 'Race another healer through roguelike encounters with buffs and sabotage.',
    glyph: 'P',
  },
  {
    screen: 'gear-upgrade',
    title: 'Gear Upgrade',
    description: 'Spend boss coins on class gear slots and infusion abilities.',
    glyph: 'G',
  },
  {
    screen: 'hunter-profile',
    title: 'Hunter Profile',
    description: 'IWT2 level, inventory summary, and collection log.',
    glyph: 'H',
  },
  {
    screen: 'customize-character',
    title: 'Customize Character',
    description: 'Choose healer kit, armor palette, and IWT2 hunter callsign.',
    glyph: 'K',
  },
  {
    screen: 'cloud-save',
    title: 'Backup Slot',
    description: 'Choose local, online, or fresh IWT2 progress.',
    glyph: 'C',
  },
  {
    screen: 'settings',
    title: 'Settings',
    description: 'IWT2 targeting preference and controller icon style.',
    glyph: 'S',
  },
]

export function IWantToHeal2App({
  onlineBackupsAvailable,
  onBackToGameSelect,
}: {
  onlineBackupsAvailable: boolean
  onBackToGameSelect: () => void
}) {
  const { enabled: dualScreenEnabled } = useDualScreen()
  const [screen, setScreen] = useState<Iwt2Screen>('menu')
  const [save, setSave] = useState<Iwt2Save>(loadIwt2Save)
  const [selectedIndex, setSelectedIndex] = useState(0)
  const [selectedBossId, setSelectedBossId] = useState<Iwt2BossId>('bulldrome')
  const [arenaModeLabel, setArenaModeLabel] = useState('Dungeon')
  const [arenaDifficulty, setArenaDifficulty] = useState<Iwt2Difficulty>(IWT2_DUNGEON_DIFFICULTIES[0])
  const [selectedDungeonDifficultySlug, setSelectedDungeonDifficultySlug] = useState(IWT2_DUNGEON_DIFFICULTIES[0].slug)
  const [selectedRaidDifficultySlug, setSelectedRaidDifficultySlug] = useState(IWT2_RAID_DIFFICULTIES[0].slug)
  const [roguelikeVariant, setRoguelikeVariant] = useState<Iwt2RoguelikeVariant>('pve')
  const [roguelikeContentType, setRoguelikeContentType] = useState<Iwt2RoguelikeContentType>('dungeon')
  const [roguelikeRun, setRoguelikeRun] = useState<Iwt2RoguelikeRunState | null>(null)
  const [pvpQueueMessage, setPvpQueueMessage] = useState('')
  const [pvpQueueing, setPvpQueueing] = useState(false)
  const cancelPvpQueueRef = useRef<(() => void) | null>(null)

  useEffect(() => {
    writeIwt2Save(save)
  }, [save])

  useEffect(() => () => {
    cancelPvpQueueRef.current?.()
  }, [])

  function cancelIwt2PvpQueue() {
    cancelPvpQueueRef.current?.()
    cancelPvpQueueRef.current = null
    setPvpQueueing(false)
    setPvpQueueMessage('')
  }

  const setupDualScreenState = useMemo<DualScreenSetupState | null>(
    () => buildIwt2SetupDualScreenState(screen, selectedBossId, save, currentSetupDifficulty(screen, selectedDungeonDifficultySlug, selectedRaidDifficultySlug)),
    [save, screen, selectedBossId, selectedDungeonDifficultySlug, selectedRaidDifficultySlug],
  )
  const workshopDualScreenState = useMemo<DualScreenWorkshopState | null>(
    () => buildIwt2WorkshopDualScreenState(screen, save),
    [save, screen],
  )

  useDualScreenSetupPublisher(setupDualScreenState, dualScreenEnabled)
  useDualScreenWorkshopPublisher(workshopDualScreenState, dualScreenEnabled)

  useGameAction((action, device) => {
    if (screen !== 'menu' || device !== 'controller') return
    if (action === 'back') {
      onBackToGameSelect()
      return
    }
    if (action === 'confirm') {
      openMenuItem(MENU_ITEMS[selectedIndex])
      return
    }
    if (action === 'navigateUp' || action === 'navigateLeft') {
      const offset = action === 'navigateUp' ? IWT2_MENU_COLUMNS : 1
      setSelectedIndex((current) => Math.max(0, current - offset))
    } else if (action === 'navigateDown' || action === 'navigateRight') {
      const offset = action === 'navigateDown' ? IWT2_MENU_COLUMNS : 1
      setSelectedIndex((current) => Math.min(MENU_ITEMS.length - 1, current + offset))
    }
  })

  function openMenuItem(item: (typeof MENU_ITEMS)[number]) {
    if (item.title === 'PvP') {
      setRoguelikeVariant('pvp')
      setScreen('roguelike')
      return
    }
    if (item.title === 'Roguelike') {
      setRoguelikeVariant('pve')
      setRoguelikeContentType((current) => current === 'stadium' ? 'dungeon' : current)
    }
    setScreen(item.screen)
  }

  if (screen === 'arena') {
    return (
      <BossArenaScreen
        bossId={selectedBossId}
        difficulty={arenaDifficulty}
        key={`arena-${selectedBossId}-${arenaModeLabel}-${arenaDifficulty.slug}`}
        modeLabel={arenaModeLabel}
        save={save}
        onBack={() => setScreen('menu')}
        onSaveUpdated={setSave}
      />
    )
  }

  if (screen === 'roguelike-arena' && roguelikeRun) {
    return (
      <BossArenaScreen
        bossIds={roguelikeRun.bossIds}
        bossId={selectedBossId}
        key={`roguelike-${roguelikeRun.variant}-${roguelikeRun.contentType}-${roguelikeRun.stage}-${roguelikeRun.bossIds.join('-')}`}
        roguelikeRun={{
          buffs: roguelikeRun.buffs,
          contentType: roguelikeRun.contentType,
          debuffs: roguelikeRun.debuffs,
          greenCoinThreshold: IWT2_ROGUELIKE_GREEN_COIN_BOSS_THRESHOLD,
          bossesDefeated: roguelikeRun.bossesDefeated,
          onVictory: () => {
            setRoguelikeRun((current) => current
              ? {
                  ...current,
                  bossesDefeated: current.bossesDefeated + current.bossIds.length,
                  ...buildRoguelikeChoices(save, current.variant),
                }
              : current)
            setScreen('roguelike-upgrade')
          },
          stage: roguelikeRun.stage,
          variant: roguelikeRun.variant,
        }}
        save={save}
        onBack={() => setScreen('roguelike')}
        onMainMenu={() => setScreen('menu')}
        onPvpRequeue={() => {
          setScreen('roguelike')
          startIwt2RoguelikeRun()
        }}
        onSaveUpdated={setSave}
      />
    )
  }

  if (screen === 'roguelike-upgrade' && roguelikeRun) {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
        <Iwt2RoguelikeUpgradeScreen
          activeBuffSummary={summarizeIwt2Buffs(save, roguelikeRun.buffs)}
          activeDebuffSummary={summarizeIwt2Debuffs(save, roguelikeRun.debuffs)}
          contentType={roguelikeRun.contentType}
          debuffChoices={roguelikeRun.debuffChoices}
          selfChoices={roguelikeRun.selfChoices}
          stage={roguelikeRun.stage}
          variant={roguelikeRun.variant}
          onBack={() => setScreen('roguelike')}
          onChoose={(buffId, debuffId) => {
            const nextRun = applyRoguelikeChoice(roguelikeRun, buffId, debuffId, save)
            setRoguelikeRun(nextRun)
            setSelectedBossId(nextRun.bossIds[0] ?? 'bulldrome')
            setScreen('roguelike-arena')
          }}
        />
      </main>
    )
  }

  if (screen === 'dungeons') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
        <Iwt2DungeonsScreen
          difficultySlug={selectedDungeonDifficultySlug}
          save={save}
          onBack={() => setScreen('menu')}
          onDifficultyChange={setSelectedDungeonDifficultySlug}
          onOpenBoss={(bossId, difficulty) => {
            setArenaModeLabel(`${difficulty.name} Dungeon`)
            setArenaDifficulty(difficulty)
            setSelectedBossId(bossId)
            setScreen('arena')
          }}
          onPreviewBoss={setSelectedBossId}
        />
      </main>
    )
  }

  if (screen === 'roguelike') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
        <Iwt2RoguelikeScreen
          contentType={roguelikeContentType}
          variant={roguelikeVariant}
          onBack={() => {
            cancelIwt2PvpQueue()
            setScreen('menu')
          }}
          onCancelQueue={cancelIwt2PvpQueue}
          onContentTypeChange={setRoguelikeContentType}
          onStart={() => {
            startIwt2RoguelikeRun()
          }}
          onVariantChange={(nextVariant) => {
            cancelIwt2PvpQueue()
            setRoguelikeVariant(nextVariant)
            if (nextVariant === 'pve' && roguelikeContentType === 'stadium') {
              setRoguelikeContentType('dungeon')
            }
          }}
          queueing={pvpQueueing}
          queueMessage={pvpQueueMessage}
        />
      </main>
    )
  }

  if (screen === 'raids') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
        <Iwt2ModeScreen
          difficultySlug={selectedRaidDifficultySlug}
          mode="Raids"
          save={save}
          onBack={() => setScreen('menu')}
          onDifficultyChange={setSelectedRaidDifficultySlug}
          onOpenBoss={(bossId, difficulty) => {
            setArenaModeLabel(`${difficulty.name} Raid`)
            setArenaDifficulty(difficulty)
            setSelectedBossId(bossId)
            setScreen('arena')
          }}
          onPreviewBoss={setSelectedBossId}
        />
      </main>
    )
  }

  if (screen === 'hunter-profile') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2HunterProfileScreen save={save} onBack={() => setScreen('menu')} />
      </main>
    )
  }

  if (screen === 'gear-upgrade') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header
          save={save}
          title="Gear Upgrade"
          onBack={() => setScreen('menu')}
          onBackToGameSelect={onBackToGameSelect}
        />
        <Iwt2GearUpgradeScreen
          save={save}
          onBack={() => setScreen('menu')}
          onSaveUpdated={setSave}
        />
      </main>
    )
  }

  if (screen === 'customize-character') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
        <Iwt2CustomizeCharacterScreen
          save={save}
          onBack={() => setScreen('menu')}
          onSaveUpdated={setSave}
        />
      </main>
    )
  }

  if (screen === 'cloud-save') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
        <Iwt2CloudSaveScreen
          save={save}
          onlineBackupsAvailable={onlineBackupsAvailable}
          onBack={() => setScreen('menu')}
          onSaveUpdated={setSave}
        />
      </main>
    )
  }

  if (screen === 'settings') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
        <Iwt2SettingsScreen onBack={() => setScreen('menu')} />
      </main>
    )
  }

  return (
    <main className="game-shell iwt2-shell">
      <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />

      {screen === 'menu' && (
        <section className="iwt2-menu-screen" data-game-nav-active="true">
          <div className="iwt2-menu-grid">
            {MENU_ITEMS.map((item, index) => (
              <button
                className={`iwt2-menu-card ${selectedIndex === index ? 'game-selected' : ''}`}
                aria-label={`${item.title}. ${item.description}`}
                data-controller-nav="skip"
                data-game-selected={selectedIndex === index ? 'true' : undefined}
                key={`${item.screen}-${item.title}`}
                onClick={() => openMenuItem(item)}
                onPointerDown={() => setSelectedIndex(index)}
                type="button"
              >
                <span>{item.glyph}</span>
                <div>
                  <strong>{item.title}</strong>
                  <small>{item.description}</small>
                </div>
              </button>
            ))}
          </div>
        </section>
      )}
    </main>
  )

  function startIwt2RoguelikeRun() {
    cancelIwt2PvpQueue()
    if (roguelikeVariant !== 'pvp') {
      beginIwt2RoguelikeArena(roguelikeVariant, roguelikeContentType)
      return
    }
    const startStage = 1
    setPvpQueueing(true)
    setPvpQueueMessage('Queuing for PvP...')
    cancelPvpQueueRef.current = startPvpQueueWithCpuFallback<unknown>({
      contentType: roguelikeContentType,
      startStage,
      gameMode: getGameMode(),
      liveMatchActive: () => false,
      onSearching: (message) => {
        setPvpQueueing(true)
        setPvpQueueMessage(message)
      },
      onCpuMatch: (_difficulty, message) => {
        cancelPvpQueueRef.current = null
        setPvpQueueing(false)
        setPvpQueueMessage(message)
        beginIwt2RoguelikeArena('pvp', roguelikeContentType)
      },
      onLiveMatch: (...liveMatchArgs) => {
        const message = liveMatchArgs[2]
        cancelPvpQueueRef.current = null
        setPvpQueueing(false)
        setPvpQueueMessage(message)
        beginIwt2RoguelikeArena('pvp', roguelikeContentType)
      },
      messages: {
        offline: (difficulty) => `Offline mode. CPU ${difficulty} enters IWT2 ${formatRoguelikeContentType(roguelikeContentType)}.`,
        searching: `Searching IWT2 ${formatRoguelikeContentType(roguelikeContentType)} queue for 5s.`,
        notFound: (difficulty) => `No IWT2 opponent found after 5s. CPU ${difficulty} steps in.`,
        unavailable: (difficulty) => `PvP server unavailable. CPU ${difficulty} steps in.`,
        liveFound: () => `Opponent found. Starting IWT2 ${formatRoguelikeContentType(roguelikeContentType)} race.`,
      },
    })
  }

  function beginIwt2RoguelikeArena(
    variant: Iwt2RoguelikeVariant,
    contentType: Iwt2RoguelikeContentType,
  ) {
    const nextRun = createRoguelikeRun(save, variant, contentType)
    setRoguelikeRun(nextRun)
    setSelectedBossId(nextRun.bossIds[0] ?? 'bulldrome')
    setScreen('roguelike-arena')
  }
}

function formatRoguelikeContentType(contentType: Iwt2RoguelikeContentType) {
  if (contentType === 'raid') return 'Raid'
  if (contentType === 'stadium') return 'Stadium'
  return 'Dungeon'
}

function createRoguelikeRun(
  save: Iwt2Save,
  variant: Iwt2RoguelikeVariant,
  contentType: Iwt2RoguelikeContentType,
): Iwt2RoguelikeRunState {
  return {
    bossIds: createRoguelikeBossPair(variant, contentType, 1, 0),
    bossesDefeated: 0,
    buffs: [],
    contentType,
    debuffs: [],
    ...buildRoguelikeChoices(save, variant),
    stage: 1,
    variant,
  }
}

function buildRoguelikeChoices(save: Iwt2Save, variant: Iwt2RoguelikeVariant) {
  const abilities = abilitiesForHealer(save.character.healerStyle)
  const selfCatalog = [IWT2_REVIVE_PARTY_CHOICE, ...buildIwt2SelfBuffChoices(abilities)]
  const debuffCatalog = buildIwt2OpponentDebuffChoices(abilities)
  return {
    selfChoices: variant === 'pvp'
      ? chooseRunChoicesWithPreferredFirst(
          selfCatalog,
          IWT2_ROGUELIKE_CHOICE_COUNT,
          isExtraTargetBuff,
          IWT2_PVP_FIRST_BUFF_EXTRA_TARGET_CHANCE,
        )
      : chooseRunChoices(selfCatalog, IWT2_ROGUELIKE_CHOICE_COUNT),
    debuffChoices: variant === 'pvp'
      ? chooseRunChoices(debuffCatalog, IWT2_ROGUELIKE_CHOICE_COUNT)
      : [],
  }
}

function summarizeIwt2Buffs(save: Iwt2Save, buffs: Iwt2RoguelikeSelfBuffId[]) {
  if (buffs.length === 0) return ''
  const abilities = abilitiesForHealer(save.character.healerStyle)
  return summarizeChoiceStacks(
    buffs,
    [IWT2_REVIVE_PARTY_CHOICE, ...buildIwt2SelfBuffChoices(abilities)],
    'None',
  )
}

function summarizeIwt2Debuffs(save: Iwt2Save, debuffs: Iwt2RoguelikeOpponentDebuffId[]) {
  if (debuffs.length === 0) return ''
  const abilities = abilitiesForHealer(save.character.healerStyle)
  return summarizeChoiceStacks(
    debuffs,
    buildIwt2OpponentDebuffChoices(abilities),
    'None',
  )
}

function applyRoguelikeChoice(
  run: Iwt2RoguelikeRunState,
  buffId: Iwt2RoguelikeSelfBuffId,
  debuffId: Iwt2RoguelikeOpponentDebuffId | undefined,
  save: Iwt2Save,
): Iwt2RoguelikeRunState {
  const nextDebuffs = debuffId ? [...run.debuffs, debuffId] : run.debuffs
  const nextBase = buffId === IWT2_REVIVE_PARTY_CHOICE.id
    ? {
        buffs: run.buffs,
        debuffs: nextDebuffs.slice(1),
      }
    : {
        buffs: [...run.buffs, buffId],
        debuffs: nextDebuffs,
      }
  return {
    ...run,
    ...nextBase,
    bossIds: createRoguelikeBossPair(run.variant, run.contentType, run.stage + 1, run.bossesDefeated),
    ...buildRoguelikeChoices(save, run.variant),
    stage: run.stage + 1,
  }
}

function createRoguelikeBossPair(
  variant: Iwt2RoguelikeVariant,
  contentType: Iwt2RoguelikeContentType,
  stage: number,
  bossesDefeated: number,
): Iwt2BossId[] {
  const weightedProgressionEnabled = variant === 'pve'
    ? IWT2_PVE_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED
    : contentType === 'stadium'
      ? IWT2_PVP_STADIUM_WEIGHTED_BOSS_PROGRESSION_ENABLED
      : IWT2_PVP_ROGUELIKE_WEIGHTED_BOSS_PROGRESSION_ENABLED
  const bossPool = roguelikeBossPoolFor(variant, contentType)
  const count = bossesDefeated >= IWT2_ROGUELIKE_GREEN_COIN_BOSS_THRESHOLD ? 3 : 2

  if (weightedProgressionEnabled) {
    return createIwt2WeightedRoguelikeBossPair(stage, Math.random, { bossPool, count })
  }
  return createUniformIwt2RoguelikeBossPair(Math.random, { bossPool, count })
}

function roguelikeBossPoolFor(
  variant: Iwt2RoguelikeVariant,
  contentType: Iwt2RoguelikeContentType,
): Iwt2BossId[] | undefined {
  if (variant !== 'pvp') return undefined
  if (contentType === 'stadium') {
    return IWT2_PVP_STADIUM_BOSS_ROSTER_LIMIT_ENABLED
      ? enabledIwt2RoguelikeBossPool(IWT2_PVP_STADIUM_ENABLED_BOSS_IDS)
      : undefined
  }
  return IWT2_PVP_ROGUELIKE_BOSS_ROSTER_LIMIT_ENABLED
    ? enabledIwt2RoguelikeBossPool(IWT2_PVP_ROGUELIKE_ENABLED_BOSS_IDS)
    : undefined
}

function chooseRunChoices<T>(items: readonly T[], count: number): T[] {
  const pool = [...items]
  const choices: T[] = []
  while (pool.length > 0 && choices.length < count) {
    const index = Math.floor(Math.random() * pool.length)
    const [choice] = pool.splice(index, 1)
    if (choice) choices.push(choice)
  }
  return choices
}

function chooseRunChoicesWithPreferredFirst<T>(
  items: readonly T[],
  count: number,
  preferred: (item: T) => boolean,
  preferredChance: number,
): T[] {
  if (count <= 0) return []
  const pool = [...items]
  const choices: T[] = []
  const preferredPool = pool.filter(preferred)
  if (preferredPool.length > 0 && Math.random() < preferredChance) {
    const preferredChoice = preferredPool[Math.floor(Math.random() * preferredPool.length)]
    const preferredIndex = pool.indexOf(preferredChoice)
    if (preferredIndex >= 0) {
      const [choice] = pool.splice(preferredIndex, 1)
      if (choice) choices.push(choice)
    }
  }
  while (pool.length > 0 && choices.length < count) {
    const index = Math.floor(Math.random() * pool.length)
    const [choice] = pool.splice(index, 1)
    if (choice) choices.push(choice)
  }
  return choices
}

function isExtraTargetBuff(choice: Iwt2RoguelikeChoice<Iwt2RoguelikeSelfBuffId>) {
  return choice.id.endsWith('-extra-target')
}

function Iwt2Header({
  onBack,
  onBackToGameSelect,
  save,
  title,
}: {
  onBack?: () => void
  onBackToGameSelect: () => void
  save: Iwt2Save
  title?: string
}) {
  return (
    <header className="topbar app-header">
      <button className="brand-button" onClick={onBackToGameSelect} type="button">
        <strong>Games</strong>
      </button>
      {title && <strong className="iwt2-header-title">{title}</strong>}
      <div className="character-summary">
        <strong>{save.character.name}</strong>
        <small>IWT2 Level {save.character.level}</small>
        <small>{save.character.experience} XP</small>
      </div>
      {onBack && (
        <button className="back-button iwt2-header-back" data-controller-nav="skip" onClick={onBack} type="button">
          Back
        </button>
      )}
    </header>
  )
}

function buildIwt2SetupDualScreenState(
  screen: Iwt2Screen,
  selectedBossId: Iwt2BossId,
  save: Iwt2Save,
  difficulty: Iwt2Difficulty,
): DualScreenSetupState | null {
  if (screen !== 'dungeons' && screen !== 'raids') return null
  const boss = IWT2_BOSS_METADATA[selectedBossId]
  const raid = screen === 'raids'
  const coinReward = iwt2BossCoinRewardFor(selectedBossId, difficulty.slug)
  return {
    contentType: raid ? 'raid' : 'dungeon',
    description: raid
      ? `${boss.name} raid assignment. Tank holds aggro while party moves around modular boss mechanics. Reward: ${coinReward.name}.`
      : `${boss.name} arena. Heal the party through melee pressure, telegraphs, hazards, and stun recovery. Reward: ${coinReward.name}.`,
    difficultyName: difficulty.name,
    experience: Math.round(125 * difficulty.experienceMultiplier),
    initials: boss.icon,
    itemLevel: difficulty.droppedItemLevel,
    lockedReason: undefined,
    stats: {
      damage: `${difficulty.damageMultiplier.toFixed(2)}x`,
      health: `${difficulty.healthMultiplier.toFixed(2)}x`,
      loot: coinReward.name,
      xp: `${difficulty.experienceMultiplier.toFixed(1)}x`,
    },
    subtitle: `${raid ? 'Raid' : 'Dungeon'} | 6 Players | ${IWT2_HEALER_METADATA[save.character.healerStyle].name}`,
    title: raid ? `${boss.name} Raid` : `${boss.name} Arena`,
  }
}

function currentSetupDifficulty(
  screen: Iwt2Screen,
  selectedDungeonDifficultySlug: string,
  selectedRaidDifficultySlug: string,
): Iwt2Difficulty {
  if (screen === 'raids') {
    return findIwt2Difficulty(IWT2_RAID_DIFFICULTIES, selectedRaidDifficultySlug)
  }
  return findIwt2Difficulty(IWT2_DUNGEON_DIFFICULTIES, selectedDungeonDifficultySlug)
}

function buildIwt2WorkshopDualScreenState(
  screen: Iwt2Screen,
  save: Iwt2Save,
): DualScreenWorkshopState | null {
  if (screen === 'customize-character') {
    const healer = IWT2_HEALER_METADATA[save.character.healerStyle]
    return {
      items: abilitiesForHealer(save.character.healerStyle).map((ability) => ({
        glyph: ability.icon,
        meta: `${ability.manaCost} Mana | ${ability.cooldownSeconds}s cooldown`,
        status: `Slot ${ability.slot}`,
        title: ability.name,
      })),
      mode: 'class',
      subtitle: healer.description,
      summary: healer.name,
      title: 'Customize Character',
    }
  }

  return null
}
