import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
import { ControllerStylePreview } from '../../../components/ControllerIcons'
import { useGameAction, useInput, type ControllerIconStyle } from '../../../input'
import { getGameMode } from '../../../gameRepository'
import {
  restoreIwt2CloudSlot,
  snapshotIwt2CloudSlot,
  updateIwt2CharacterSettings,
  type Iwt2Save,
} from '../save/iwt2Repository'
import { IWT2_BOSS_METADATA, type Iwt2BossId } from '../content/bosses'
import {
  abilitiesForHealer,
  IWT2_HEALER_METADATA,
  IWT2_HEALER_ORDER,
} from '../content/healerAbilities'
import {
  type Iwt2RoguelikeChoice,
  type Iwt2RoguelikeContentType,
  type Iwt2RoguelikeOpponentDebuffId,
  type Iwt2RoguelikeSelfBuffId,
  type Iwt2RoguelikeVariant,
} from '../content/roguelike'

type Iwt2NavAction = {
  key: string
  label: string
  detail?: string
  value?: string
  disabled?: boolean
  onConfirm: () => void
}

type Iwt2Mode = 'Raids'

type Iwt2ScreenShellProps = {
  title: string
  eyebrow?: string
  onBack: () => void
  children: ReactNode
}

type Iwt2ActionListProps = {
  actions: Iwt2NavAction[]
}

type Iwt2InfoRow = {
  key: string
  label: string
  detail: string
  value: string
}

type Iwt2NavPosition = {
  column: number
  row: number
}

type Iwt2UpgradeNavEntry =
  | { kind: 'upgradeBuff', index: number, row: number, column: number }
  | { kind: 'upgradeDebuff', index: number, row: number, column: number }
  | { kind: 'upgradeContinue', row: number, column: number, disabled?: boolean }
  | { kind: 'upgradeLeave', row: number, column: number }

function withBackAction(actions: Iwt2NavAction[], onBack: () => void): Iwt2NavAction[] {
  return [
    {
      key: 'back',
      label: 'Back',
      onConfirm: onBack,
    },
    ...actions,
  ]
}

function useIwt2ActionList(actions: Iwt2NavAction[]) {
  const [selectedIndex, setSelectedIndex] = useState(0)
  const activeIndex = clampToEnabled(actions, selectedIndex)

  useGameAction((action, device) => {
    if (device !== 'controller' || actions.length === 0) return
    if (action === 'navigateUp' || action === 'navigateLeft') {
      setSelectedIndex((current) => nextEnabledIndex(actions, current, -1))
    } else if (action === 'navigateDown' || action === 'navigateRight') {
      setSelectedIndex((current) => nextEnabledIndex(actions, current, 1))
    } else if (action === 'confirm') {
      if (!actions[activeIndex]?.disabled) actions[activeIndex]?.onConfirm()
    }
  })

  return { selectedIndex: activeIndex, setSelectedIndex }
}

const MODE_ACTIONS: Record<Iwt2Mode, Array<{
  bossId: Iwt2BossId
  label: string
  detail: string
  value: string
}>> = {
  Raids: [
    {
      bossId: 'bulldrome',
      label: 'Bulldrome Raid Assignment',
      detail: 'Shared arena entry with raid-role notes: tank opens aggro, party spreads for charge lanes, then collapses after ground slam.',
      value: 'Start',
    },
    {
      bossId: 'yian-kut-ku',
      label: 'Yian Kut Ku Raid Assignment',
      detail: 'Shared arena entry with raid-role notes: assign ranged pressure, clear bird waves, and keep fire puddles away from party frames.',
      value: 'Start',
    },
    {
      bossId: 'great-jaggi',
      label: 'Great Jaggi Raid Assignment',
      detail: 'Shared arena entry with raid-role notes: hold center, dodge pack lanes, and stabilize the paladin during howl windows.',
      value: 'Start',
    },
    {
      bossId: 'khezu',
      label: 'Khezu Raid Assignment',
      detail: 'Shared arena entry with raid-role notes: watch lightning markers, rotate through safe bands, and recover stunned allies fast.',
      value: 'Start',
    },
  ],
}

const CONTROLLER_ICON_OPTIONS: ControllerIconStyle[] = ['playstation', 'xbox', 'nintendo']

const CONTROLLER_ICON_LABELS: Record<ControllerIconStyle, string> = {
  xbox: 'Xbox',
  playstation: 'PlayStation',
  nintendo: 'Nintendo',
}

export function Iwt2ScreenShell({
  children,
  eyebrow = 'I Want To Heal 2',
  onBack,
  title,
}: Iwt2ScreenShellProps) {
  useGameAction((action, device) => {
    if (device === 'controller' && action === 'back') onBack()
  })

  return (
    <section className="content-screen iwt2-screen-shell" data-game-nav-active="true">
      <div className="screen-heading">
        <div>
          <p className="eyebrow">{eyebrow}</p>
          <h1>{title}</h1>
        </div>
        <button className="back-button" data-controller-nav="skip" onClick={onBack} type="button">
          Back
        </button>
      </div>
      {children}
    </section>
  )
}

export function Iwt2ActionList({ actions }: Iwt2ActionListProps) {
  const { selectedIndex, setSelectedIndex } = useIwt2ActionList(actions)
  const rowRefs = useRef<Array<HTMLButtonElement | null>>([])

  useEffect(() => {
    rowRefs.current[selectedIndex]?.scrollIntoView({
      block: 'nearest',
      inline: 'nearest',
    })
  }, [selectedIndex])

  return (
    <div className="iwt2-action-list">
      {actions.map((action, index) => (
        <button
          className={`iwt2-action-row ${selectedIndex === index ? 'game-selected selected' : ''}`}
          data-controller-nav="skip"
          data-game-selected={selectedIndex === index ? 'true' : undefined}
          disabled={action.disabled}
          key={action.key}
          onClick={action.onConfirm}
          onPointerDown={() => {
            if (!action.disabled) setSelectedIndex(index)
          }}
          ref={(element) => {
            rowRefs.current[index] = element
          }}
          type="button"
        >
          <span>
            <strong>{action.label}</strong>
            {action.detail && <small>{action.detail}</small>}
          </span>
          {action.value && <em>{action.value}</em>}
        </button>
      ))}
    </div>
  )
}

function Iwt2InfoList({ rows }: { rows: Iwt2InfoRow[] }) {
  return (
    <div className="iwt2-info-list">
      {rows.map((row) => (
        <div className="iwt2-info-row" key={row.key}>
          <span>
            <strong>{row.label}</strong>
            <small>{row.detail}</small>
          </span>
          <em>{row.value}</em>
        </div>
      ))}
    </div>
  )
}

export function Iwt2DungeonsScreen({
  onBack,
  onOpenBoss,
}: {
  onBack: () => void
  onOpenBoss: (bossId: Iwt2BossId) => void
}) {
  const actions = useMemo<Iwt2NavAction[]>(() => withBackAction([
    {
      key: 'bulldrome',
      label: 'Bulldrome Arena',
      detail: 'Level 1 field boss. Charge lanes, tank pressure, and periodic ground slam.',
      value: 'Ready',
      onConfirm: () => onOpenBoss('bulldrome'),
    },
    {
      key: 'yian-kut-ku',
      label: 'Yian Kut Ku Arena',
      detail: 'Bouncing fireballs, capped fire puddles, and bird waves at 90% and 40% health.',
      value: 'Ready',
      onConfirm: () => onOpenBoss('yian-kut-ku'),
    },
    {
      key: 'great-jaggi',
      label: 'Great Jaggi Arena',
      detail: 'Pack howl calls slanted add lanes that punish standing still or grouping badly.',
      value: 'Ready',
      onConfirm: () => onOpenBoss('great-jaggi'),
    },
    {
      key: 'khezu',
      label: 'Khezu Arena',
      detail: 'Thunder rings create safe bands while lightning circles lock onto weak allies.',
      value: 'Ready',
      onConfirm: () => onOpenBoss('khezu'),
    },
  ], onBack), [onBack, onOpenBoss])

  return (
    <Iwt2ScreenShell title="Dungeons" onBack={onBack}>
      <Iwt2ActionList actions={actions} />
    </Iwt2ScreenShell>
  )
}

export function Iwt2ModeScreen({
  mode,
  onBack,
  onOpenBoss,
}: {
  mode: Iwt2Mode
  onBack: () => void
  onOpenBoss: (bossId: Iwt2BossId) => void
}) {
  const actions = useMemo<Iwt2NavAction[]>(
    () => withBackAction(MODE_ACTIONS[mode].map((entry) => ({
      key: `${mode}-${entry.bossId}`,
      label: entry.label,
      detail: entry.detail,
      value: entry.value,
      onConfirm: () => onOpenBoss(entry.bossId),
    })), onBack),
    [mode, onBack, onOpenBoss],
  )

  return (
    <Iwt2ScreenShell title={mode} onBack={onBack}>
      <Iwt2ActionList actions={actions} />
    </Iwt2ScreenShell>
  )
}

export function Iwt2RoguelikeScreen({
  contentType,
  onBack,
  onContentTypeChange,
  onStart,
  onVariantChange,
  queueMessage,
  variant,
}: {
  contentType: Iwt2RoguelikeContentType
  onBack: () => void
  onContentTypeChange: (contentType: Iwt2RoguelikeContentType) => void
  onStart: () => void
  onVariantChange: (variant: Iwt2RoguelikeVariant) => void
  queueMessage?: string
  variant: Iwt2RoguelikeVariant
}) {
  const gameMode = getGameMode()
  const [selectedIndex, setSelectedIndex] = useState(0)
  const actions = useMemo<Iwt2NavAction[]>(() => {
    const base = [
      {
        key: 'back',
        label: 'Back',
        onConfirm: onBack,
      },
      {
        key: 'variant-pve',
        label: 'PvE',
        onConfirm: () => onVariantChange('pve'),
      },
      {
        key: 'variant-pvp',
        label: 'PvP',
        onConfirm: () => onVariantChange('pvp'),
      },
    ] satisfies Iwt2NavAction[]
    if (variant === 'pve') {
      base.push(
        {
          key: 'pve-dungeon',
          label: 'Dungeon',
          onConfirm: () => onContentTypeChange('dungeon'),
        },
        {
          key: 'pve-raid',
          label: 'Raid',
          onConfirm: () => onContentTypeChange('raid'),
        },
        {
          key: 'pve-start',
          label: 'Start Run',
          onConfirm: onStart,
        },
      )
      return base
    }
    base.push(
      {
        key: 'pvp-dungeon',
        label: 'Dungeon',
        onConfirm: () => onContentTypeChange('dungeon'),
      },
      {
        key: 'pvp-raid',
        label: 'Raid',
        onConfirm: () => onContentTypeChange('raid'),
      },
      {
        key: 'pvp-stadium',
        label: 'Stadium',
        onConfirm: () => onContentTypeChange('stadium'),
      },
      {
        key: 'pvp-start',
        label: 'Start Match',
        onConfirm: onStart,
      },
    )
    return base
  }, [onBack, onContentTypeChange, onStart, onVariantChange, variant])
  const activeIndex = Math.min(selectedIndex, actions.length - 1)
  const selected = (key: string) => actions[activeIndex]?.key === key
  const open = (key: string) => actions.find((action) => action.key === key)?.onConfirm()
  const select = (key: string) => {
    const nextIndex = actions.findIndex((action) => action.key === key)
    if (nextIndex >= 0) setSelectedIndex(nextIndex)
  }
  const pveCardTitle = contentType === 'raid' ? 'Raid Roguelike' : 'Dungeon Roguelike'
  const pveCardCopy = contentType === 'raid'
    ? 'Ten-player party. Raid pools, lighter early scaling, and the same upgrade draft.'
    : 'Five-player party. Two random trash enemies and a boss with a lighter early ramp.'
  const pvpCardTitle = gameMode === 'offline' ? 'Offline CPU Match' : 'Queue Then CPU Fallback'
  const pvpCardCopy = contentType === 'stadium'
    ? 'Best-of-5 survival with dampening, equalized gear, and after-round buff buying.'
    : gameMode === 'offline'
      ? 'Offline mode always places you against a random CPU 1-5.'
      : 'Online mode searches briefly. If nobody is queued, a random CPU 1-5 takes the slot.'

  useGameAction((action, device) => {
    if (device !== 'controller') return
    if (action === 'back') onBack()
    else if (action.startsWith('navigate')) {
      setSelectedIndex((current) => moveSpatialSelection(actions, current, action))
    } else if (action === 'confirm') {
      actions[activeIndex]?.onConfirm()
    }
  })

  return (
    <section className="content-screen roguelike-screen" data-game-nav-active="true">
      <div className="screen-heading">
        <div>
          <p className="eyebrow">Endless Draft</p>
          <h1>Roguelike</h1>
        </div>
        <button
          className={`back-button ${selected('back') ? 'game-selected' : ''}`}
          data-controller-nav="skip"
          data-game-selected={selected('back') ? 'true' : undefined}
          onClick={onBack}
          onPointerDown={() => select('back')}
          type="button"
        >
          Back
        </button>
      </div>
      <div className="roguelike-variant-row">
        <button
          className={`text-button ${variant === 'pve' ? 'active' : ''} ${selected('variant-pve') ? 'game-selected' : ''}`}
          data-controller-nav="skip"
          data-game-selected={selected('variant-pve') ? 'true' : undefined}
          onClick={() => open('variant-pve')}
          onPointerDown={() => select('variant-pve')}
          type="button"
        >
          PvE
        </button>
        <button
          className={`text-button ${variant === 'pvp' ? 'active' : ''} ${selected('variant-pvp') ? 'game-selected' : ''}`}
          data-controller-nav="skip"
          data-game-selected={selected('variant-pvp') ? 'true' : undefined}
          onClick={() => open('variant-pvp')}
          onPointerDown={() => select('variant-pvp')}
          type="button"
        >
          PvP
        </button>
      </div>
      {variant === 'pve' && (
        <>
          <div className="roguelike-option-panel">
            <div>
              <p className="eyebrow">Run Type</p>
              <h2>PvE Roguelike</h2>
            </div>
            <div className="roguelike-timing-row">
              <button
                className={`text-button ${contentType === 'dungeon' ? 'active' : ''} ${selected('pve-dungeon') ? 'game-selected' : ''}`}
                data-controller-nav="skip"
                data-game-selected={selected('pve-dungeon') ? 'true' : undefined}
                onClick={() => open('pve-dungeon')}
                onPointerDown={() => select('pve-dungeon')}
                type="button"
              >
                Dungeon
              </button>
              <button
                className={`text-button ${contentType === 'raid' ? 'active' : ''} ${selected('pve-raid') ? 'game-selected' : ''}`}
                data-controller-nav="skip"
                data-game-selected={selected('pve-raid') ? 'true' : undefined}
                onClick={() => open('pve-raid')}
                onPointerDown={() => select('pve-raid')}
                type="button"
              >
                Raid
              </button>
            </div>
          </div>
          <div className="menu-card pvp-queue-panel">
            <span>{contentType === 'raid' ? 'R' : 'D'}</span>
            <div>
              <strong>{pveCardTitle}</strong>
              <small>{pveCardCopy}</small>
            </div>
            <button
              className={`text-button ${selected('pve-start') ? 'game-selected' : ''}`}
              data-controller-nav="skip"
              data-game-selected={selected('pve-start') ? 'true' : undefined}
              onClick={() => open('pve-start')}
              onPointerDown={() => select('pve-start')}
              type="button"
            >
              Start Run
            </button>
          </div>
        </>
      )}
      {variant === 'pvp' && (
        <>
          <div className="roguelike-option-panel">
            <div>
              <p className="eyebrow">Match Type</p>
              <h2>PvP Roguelike</h2>
            </div>
            <div className="roguelike-timing-row">
              <button
                className={`text-button ${contentType === 'dungeon' ? 'active' : ''} ${selected('pvp-dungeon') ? 'game-selected' : ''}`}
                data-controller-nav="skip"
                data-game-selected={selected('pvp-dungeon') ? 'true' : undefined}
                onClick={() => open('pvp-dungeon')}
                onPointerDown={() => select('pvp-dungeon')}
                type="button"
              >
                Dungeon
              </button>
              <button
                className={`text-button ${contentType === 'raid' ? 'active' : ''} ${selected('pvp-raid') ? 'game-selected' : ''}`}
                data-controller-nav="skip"
                data-game-selected={selected('pvp-raid') ? 'true' : undefined}
                onClick={() => open('pvp-raid')}
                onPointerDown={() => select('pvp-raid')}
                type="button"
              >
                Raid
              </button>
              <button
                className={`text-button ${contentType === 'stadium' ? 'active' : ''} ${selected('pvp-stadium') ? 'game-selected' : ''}`}
                data-controller-nav="skip"
                data-game-selected={selected('pvp-stadium') ? 'true' : undefined}
                onClick={() => open('pvp-stadium')}
                onPointerDown={() => select('pvp-stadium')}
                type="button"
              >
                Stadium
              </button>
            </div>
          </div>
          <div className="menu-card pvp-queue-panel">
            <span>{gameMode === 'offline' ? 'C' : 'Q'}</span>
            <div>
              <strong>{pvpCardTitle}</strong>
              <small>{queueMessage || pvpCardCopy}</small>
            </div>
            <button
              className={`text-button ${selected('pvp-start') ? 'game-selected' : ''}`}
              data-controller-nav="skip"
              data-game-selected={selected('pvp-start') ? 'true' : undefined}
              onClick={() => open('pvp-start')}
              onPointerDown={() => select('pvp-start')}
              type="button"
            >
              Start Match
            </button>
          </div>
        </>
      )}
    </section>
  )
}

export function Iwt2RoguelikeUpgradeScreen({
  activeBuffSummary,
  activeDebuffSummary,
  contentType,
  debuffChoices,
  onBack,
  onChoose,
  selfChoices,
  stage,
  variant,
}: {
  activeBuffSummary?: string
  activeDebuffSummary?: string
  contentType: Iwt2RoguelikeContentType
  debuffChoices: Array<Iwt2RoguelikeChoice<Iwt2RoguelikeOpponentDebuffId>>
  onBack: () => void
  onChoose: (buffId: Iwt2RoguelikeSelfBuffId, debuffId?: Iwt2RoguelikeOpponentDebuffId) => void
  selfChoices: Array<Iwt2RoguelikeChoice<Iwt2RoguelikeSelfBuffId>>
  stage: number
  variant: Iwt2RoguelikeVariant
}) {
  const [selectedBuffId, setSelectedBuffId] = useState<Iwt2RoguelikeSelfBuffId | undefined>()
  const [selectedDebuffId, setSelectedDebuffId] = useState<Iwt2RoguelikeOpponentDebuffId | undefined>()
  const [selectedIndex, setSelectedIndex] = useState(0)
  const activeBuffId = selfChoices.some((choice) => choice.id === selectedBuffId) ? selectedBuffId : undefined
  const activeDebuffId = debuffChoices.some((choice) => choice.id === selectedDebuffId) ? selectedDebuffId : undefined

  const entries = useMemo<Iwt2UpgradeNavEntry[]>(() => {
    if (variant !== 'pvp') {
      return [
        ...selfChoices.map((_, index) => ({
          kind: 'upgradeBuff' as const,
          index,
          row: index,
          column: 0,
        })),
        {
          kind: 'upgradeLeave' as const,
          row: selfChoices.length,
          column: 0,
        },
      ]
    }
    const nextEntries: Iwt2UpgradeNavEntry[] = [
      ...selfChoices.map((_, index) => ({
        kind: 'upgradeBuff' as const,
        index,
        row: index,
        column: 0,
      })),
      ...debuffChoices.map((_, index) => ({
        kind: 'upgradeDebuff' as const,
        index,
        row: index,
        column: 1,
      })),
      {
        kind: 'upgradeContinue' as const,
        row: Math.max(selfChoices.length, debuffChoices.length),
        column: 1,
        disabled: !activeBuffId || !activeDebuffId,
      },
    ]
    return nextEntries
  }, [activeBuffId, activeDebuffId, debuffChoices, selfChoices, variant])

  const activeEntry = activeUpgradeEntry(entries, selectedIndex)
  const pvpUpgrade = variant === 'pvp'
  const buffHeader = pvpUpgrade ? 'Self Buff' : 'Run Buff'
  const headerEyebrow = pvpUpgrade && contentType === 'stadium' ? 'Stadium Buy Round' : 'Choose Edge'
  const title = pvpUpgrade
    ? contentType === 'stadium' ? `Round ${stage} Complete` : `Stage ${stage} Boss Cleared`
    : `Roguelike Stage ${stage} Complete`
  const continueLabel = contentType === 'stadium' ? 'Finished Buying' : 'Continue'

  function entrySelected(kind: Iwt2UpgradeNavEntry['kind'], index?: number) {
    if (!activeEntry || activeEntry.kind !== kind) return false
    if ('index' in activeEntry || index !== undefined) return 'index' in activeEntry && activeEntry.index === index
    return true
  }

  function setCursor(kind: Iwt2UpgradeNavEntry['kind'], index?: number) {
    const nextIndex = entries.findIndex((entry) => {
      if (entry.kind !== kind) return false
      if ('index' in entry || index !== undefined) return 'index' in entry && entry.index === index
      return true
    })
    if (nextIndex >= 0) setSelectedIndex(nextIndex)
  }

  function openEntry(entry: Iwt2UpgradeNavEntry | undefined) {
    if (!entry || upgradeEntryDisabled(entry)) return
    if (entry.kind === 'upgradeBuff') {
      const choice = selfChoices[entry.index]
      if (!choice) return
      if (pvpUpgrade) setSelectedBuffId(choice.id)
      else onChoose(choice.id)
    } else if (entry.kind === 'upgradeDebuff') {
      const choice = debuffChoices[entry.index]
      if (choice) setSelectedDebuffId(choice.id)
    } else if (entry.kind === 'upgradeContinue') {
      if (activeBuffId && activeDebuffId) onChoose(activeBuffId, activeDebuffId)
    } else if (entry.kind === 'upgradeLeave') {
      onBack()
    }
  }

  useGameAction((action, device) => {
    if (device !== 'controller') return
    if (action === 'confirm') {
      openEntry(activeEntry)
      return
    }
    if (action.startsWith('navigate')) {
      setSelectedIndex((current) => moveUpgradeSelection(entries, current, action))
      return
    }
    if (action === 'back' || action === 'pause') {
      onBack()
    }
  })

  return (
    <div className="result-screen" data-game-nav-active="true">
      <div className={`pvp-upgrade-dialog ${pvpUpgrade ? '' : 'pve-upgrade-dialog'}`}>
        {pvpUpgrade ? (
          <div className="pvp-upgrade-header">
            <div>
              <p className="eyebrow">{headerEyebrow}</p>
              <h2>{title}</h2>
            </div>
          </div>
        ) : (
          <>
            <p className="eyebrow">{title}</p>
            <h2>Choose Upgrade</h2>
            <p>Pick one upgrade before the next fight.</p>
          </>
        )}
        <div className="pvp-choice-columns">
          <div>
            <strong>{buffHeader}</strong>
            <div className="upgrade-choice-grid">
              {selfChoices.map((choice, index) => (
                <button
                  className={`${activeBuffId === choice.id && pvpUpgrade ? 'selected-upgrade' : ''} ${entrySelected('upgradeBuff', index) ? 'game-selected' : ''}`}
                  data-controller-nav="skip"
                  data-game-selected={entrySelected('upgradeBuff', index) ? 'true' : undefined}
                  key={choice.id}
                  onClick={() => {
                    if (pvpUpgrade) setSelectedBuffId(choice.id)
                    else onChoose(choice.id)
                  }}
                  onPointerDown={() => setCursor('upgradeBuff', index)}
                  type="button"
                >
                  <strong>{choice.name}</strong>
                  <small>{choice.description}</small>
                </button>
              ))}
            </div>
          </div>
          {pvpUpgrade && (
            <div>
              <strong>Opponent Debuff</strong>
              <div className="upgrade-choice-grid">
                {debuffChoices.map((choice, index) => (
                  <button
                    className={`${activeDebuffId === choice.id ? 'selected-upgrade' : ''} ${entrySelected('upgradeDebuff', index) ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    data-game-selected={entrySelected('upgradeDebuff', index) ? 'true' : undefined}
                    key={choice.id}
                    onClick={() => setSelectedDebuffId(choice.id)}
                    onPointerDown={() => setCursor('upgradeDebuff', index)}
                    type="button"
                  >
                    <strong>{choice.name}</strong>
                    <small>{choice.description}</small>
                  </button>
                ))}
              </div>
            </div>
          )}
        </div>
        {!pvpUpgrade && activeBuffSummary && (
          <p className="roguelike-upgrade-list">
            Active: {activeBuffSummary}
          </p>
        )}
        {pvpUpgrade && (activeBuffSummary || activeDebuffSummary) && (
          <p className="roguelike-upgrade-list">
            Buffs: {activeBuffSummary || 'None'} | Debuffs: {activeDebuffSummary || 'None'}
          </p>
        )}
        {pvpUpgrade ? (
          <button
            className={`secondary-result-button ${entrySelected('upgradeContinue') ? 'game-selected' : ''}`}
            data-controller-nav="skip"
            data-game-selected={entrySelected('upgradeContinue') ? 'true' : undefined}
            disabled={!activeBuffId || !activeDebuffId}
            onClick={() => {
              if (activeBuffId && activeDebuffId) onChoose(activeBuffId, activeDebuffId)
            }}
            onPointerDown={() => setCursor('upgradeContinue')}
            type="button"
          >
            {continueLabel}
          </button>
        ) : (
          <button
            className={`secondary-result-button ${entrySelected('upgradeLeave') ? 'game-selected' : ''}`}
            data-controller-nav="skip"
            data-game-selected={entrySelected('upgradeLeave') ? 'true' : undefined}
            onClick={onBack}
            onPointerDown={() => setCursor('upgradeLeave')}
            type="button"
          >
            Leave Roguelike
          </button>
        )}
      </div>
    </div>
  )
}

export function Iwt2HunterProfileScreen({
  onBack,
  save,
}: {
  onBack: () => void
  save: Iwt2Save
}) {
  const bossRows = (Object.keys(IWT2_BOSS_METADATA) as Iwt2BossId[]).map((bossId) => ({
    bossId,
    drops: save.collectionLog.dropsFound[bossDropIdForProfile(bossId)] ?? 0,
    kills: save.collectionLog.bossKills[bossId] ?? 0,
    name: IWT2_BOSS_METADATA[bossId].name,
  }))
  const actions = useMemo<Iwt2NavAction[]>(() => withBackAction([
    {
      key: 'name',
      label: 'Name',
      value: save.character.name,
      onConfirm: () => {},
    },
    {
      key: 'level',
      label: 'Level',
      value: String(save.character.level),
      onConfirm: () => {},
    },
    {
      key: 'experience',
      label: 'Experience',
      value: `${save.character.experience} XP`,
      onConfirm: () => {},
    },
    {
      key: 'class',
      label: 'Healing Class',
      value: IWT2_HEALER_METADATA[save.character.healerStyle].name,
      onConfirm: () => {},
    },
    ...bossRows.map((row) => ({
      key: `boss-${row.bossId}`,
      label: row.name,
      value: `${row.kills} kills / ${row.drops} drops`,
      onConfirm: () => {},
    })),
    {
      key: 'inventory',
      label: 'IWT2 Inventory',
      value: `${save.inventory.length} slots`,
      onConfirm: () => {},
    },
  ], onBack), [bossRows, onBack, save.character.experience, save.character.healerStyle, save.character.level, save.character.name, save.inventory.length])

  return (
    <Iwt2ScreenShell title="Hunter Profile" onBack={onBack}>
      <Iwt2ActionList actions={actions} />
    </Iwt2ScreenShell>
  )
}

function bossDropIdForProfile(bossId: Iwt2BossId): string {
  if (bossId === 'yian-kut-ku') return 'yian-kut-ku-scale'
  if (bossId === 'great-jaggi') return 'great-jaggi-hide'
  if (bossId === 'khezu') return 'khezu-pearl'
  return 'raw-bulldrome-coin'
}

export function Iwt2CustomizeCharacterScreen({
  onBack,
  onSaveUpdated,
  save,
}: {
  onBack: () => void
  onSaveUpdated: (save: Iwt2Save) => void
  save: Iwt2Save
}) {
  const activeAbilities = abilitiesForHealer(save.character.healerStyle)
  const abilityRows = activeAbilities.map((ability) => ({
    key: ability.id,
    label: ability.name,
    detail: `${ability.manaCost} resource. ${ability.cooldownSeconds}s cooldown.`,
    value: ability.icon,
  }))
  const actions = useMemo<Iwt2NavAction[]>(() => withBackAction([
    ...IWT2_HEALER_ORDER.map((healerId) => {
      const healer = IWT2_HEALER_METADATA[healerId]
      return {
        key: `class-${healerId}`,
        label: healer.name,
        detail: healer.description,
        value: save.character.healerStyle === healerId ? 'Selected' : healer.icon,
        onConfirm: () => onSaveUpdated(updateIwt2CharacterSettings(save, {
          healerStyle: healerId,
        })),
      }
    }),
  ], onBack), [onBack, onSaveUpdated, save])

  return (
    <Iwt2ScreenShell eyebrow="Character Workshop" title="Customize Character" onBack={onBack}>
      <div className="iwt2-customize-layout">
        <div>
          <p className="eyebrow">Healing Class</p>
          <Iwt2ActionList actions={actions} />
        </div>
        <div className="iwt2-loadout-preview">
          <div>
            <p className="eyebrow">Active Loadout</p>
            <h2>Ability Bar</h2>
            <span>Select a class, then start a run.</span>
          </div>
          <Iwt2InfoList rows={abilityRows} />
        </div>
      </div>
    </Iwt2ScreenShell>
  )
}

export function Iwt2CloudSaveScreen({
  onBack,
  onSaveUpdated,
  save,
}: {
  onBack: () => void
  onSaveUpdated: (save: Iwt2Save) => void
  save: Iwt2Save
}) {
  const savedAt = new Date(save.updatedAt).toLocaleString()
  const cloudSavedAt = save.cloudSlot
    ? new Date(save.cloudSlot.savedAt).toLocaleString()
    : 'No snapshot'
  const actions = useMemo<Iwt2NavAction[]>(() => withBackAction([
    {
      key: 'snapshot',
      label: 'Update Local Backup Slot',
      detail: 'Copies current IWT2 character, inventory, and collection log into the isolated backup slot.',
      value: 'Save',
      onConfirm: () => onSaveUpdated(snapshotIwt2CloudSlot(save)),
    },
    {
      key: 'restore',
      label: 'Restore Local Backup Slot',
      detail: cloudSavedAt,
      value: save.cloudSlot ? 'Restore' : 'Empty',
      disabled: !save.cloudSlot,
      onConfirm: () => onSaveUpdated(restoreIwt2CloudSlot(save)),
    },
    {
      key: 'last-save',
      label: 'Last Local Save',
      detail: savedAt,
      value: 'Local',
      onConfirm: () => {},
      disabled: true,
    },
  ], onBack), [cloudSavedAt, onBack, onSaveUpdated, save, savedAt])

  return (
    <Iwt2ScreenShell title="Backup Slot" onBack={onBack}>
      <Iwt2ActionList actions={actions} />
    </Iwt2ScreenShell>
  )
}

function clampToEnabled(actions: Iwt2NavAction[], index: number) {
  if (actions.length === 0) return 0
  const bounded = Math.min(Math.max(0, index), actions.length - 1)
  if (!actions[bounded]?.disabled) return bounded
  const firstEnabled = actions.findIndex((action) => !action.disabled)
  return firstEnabled >= 0 ? firstEnabled : 0
}

function nextEnabledIndex(actions: Iwt2NavAction[], current: number, direction: -1 | 1) {
  if (actions.length === 0) return 0
  let index = current
  for (let steps = 0; steps < actions.length; steps += 1) {
    index = Math.min(actions.length - 1, Math.max(0, index + direction))
    if (!actions[index]?.disabled) return index
    if (index === 0 || index === actions.length - 1) break
  }
  return clampToEnabled(actions, current)
}

function activeUpgradeEntry(entries: Iwt2UpgradeNavEntry[], index: number) {
  const bounded = Math.min(index, entries.length - 1)
  const active = entries[bounded]
  if (active && !upgradeEntryDisabled(active)) return active
  return entries.find((entry) => !upgradeEntryDisabled(entry))
}

function upgradeEntryDisabled(entry: Iwt2UpgradeNavEntry) {
  return entry.kind === 'upgradeContinue' && Boolean(entry.disabled)
}

function moveUpgradeSelection(entries: Iwt2UpgradeNavEntry[], current: number, action: string) {
  const enabledEntries = entries
    .map((entry, index) => ({ entry, index }))
    .filter(({ entry }) => !upgradeEntryDisabled(entry))
  if (enabledEntries.length === 0) return 0
  const bounded = Math.min(current, entries.length - 1)
  const candidate = entries[bounded]
  const active = candidate && !upgradeEntryDisabled(candidate)
    ? candidate
    : enabledEntries[0]?.entry
  if (!active) return bounded
  const candidates = enabledEntries.filter(({ entry }) => {
    if (entry === active) return false
    if (action === 'navigateLeft') return entry.row === active.row && entry.column < active.column
    if (action === 'navigateRight') return entry.row === active.row && entry.column > active.column
    if (action === 'navigateUp') return entry.row < active.row
    return entry.row > active.row
  })
  if (candidates.length === 0) return entries.findIndex((entry) => entry === active)
  candidates.sort((a, b) => {
    const aPrimary = Math.abs(a.entry.row - active.row) + Math.abs(a.entry.column - active.column)
    const bPrimary = Math.abs(b.entry.row - active.row) + Math.abs(b.entry.column - active.column)
    return aPrimary - bPrimary || a.index - b.index
  })
  return candidates[0]?.index ?? bounded
}

function moveSpatialSelection(actions: Iwt2NavAction[], current: number, action: string) {
  if (actions.length === 0) return 0
  const bounded = Math.min(current, actions.length - 1)
  const active = actions[bounded]
  if (!active) return bounded
  const activePosition = roguelikeActionPosition(active.key)
  const candidates = actions
    .map((entry, index) => ({ entry, index, position: roguelikeActionPosition(entry.key) }))
    .filter(({ index, position }) => {
      if (index === bounded) return false
      if (action === 'navigateLeft') return position.row === activePosition.row && position.column < activePosition.column
      if (action === 'navigateRight') return position.row === activePosition.row && position.column > activePosition.column
      if (action === 'navigateUp') return position.row < activePosition.row
      return position.row > activePosition.row
    })
  if (candidates.length === 0) return bounded
  candidates.sort((a, b) => {
    const aPrimary = Math.abs(a.position.row - activePosition.row)
      + Math.abs(a.position.column - activePosition.column)
    const bPrimary = Math.abs(b.position.row - activePosition.row)
      + Math.abs(b.position.column - activePosition.column)
    const aSecondary = action === 'navigateLeft' || action === 'navigateRight'
      ? Math.abs(a.position.column - activePosition.column)
      : Math.abs(a.position.row - activePosition.row)
    const bSecondary = action === 'navigateLeft' || action === 'navigateRight'
      ? Math.abs(b.position.column - activePosition.column)
      : Math.abs(b.position.row - activePosition.row)
    return aPrimary - bPrimary || aSecondary - bSecondary || a.index - b.index
  })
  return candidates[0]?.index ?? bounded
}

function roguelikeActionPosition(key: string): Iwt2NavPosition {
  if (key === 'back') return { row: 0, column: 0 }
  if (key === 'variant-pve') return { row: 1, column: 0 }
  if (key === 'variant-pvp') return { row: 1, column: 1 }
  if (key === 'pve-dungeon' || key === 'pvp-dungeon') return { row: 2, column: 0 }
  if (key === 'pve-raid' || key === 'pvp-raid') return { row: 2, column: 1 }
  if (key === 'pvp-stadium') return { row: 2, column: 2 }
  if (key === 'pvp-start') return { row: 3, column: 1 }
  return { row: 3, column: 0 }
}

export function Iwt2SettingsScreen({ onBack }: { onBack: () => void }) {
  const {
    controllerIconStyle,
    directPartyTargeting,
    setControllerIconStyle,
    setDirectPartyTargeting,
  } = useInput()
  const actions = useMemo<Iwt2NavAction[]>(() => withBackAction([
    {
      key: 'direct-targeting',
      label: 'Target Party Members With Bindings',
      detail: 'When enabled, party frames show direct target button icons instead of D-pad cycling.',
      value: directPartyTargeting ? 'On' : 'Off',
      onConfirm: () => setDirectPartyTargeting(!directPartyTargeting),
    },
    ...CONTROLLER_ICON_OPTIONS.map((style) => ({
      key: `icons-${style}`,
      label: `${CONTROLLER_ICON_LABELS[style]} Button Icons`,
      detail: 'Changes controller glyphs shown on IWT2 frames and spell controls.',
      value: controllerIconStyle === style ? 'Selected' : '',
      onConfirm: () => setControllerIconStyle(style),
    })),
  ], onBack), [
    controllerIconStyle,
    directPartyTargeting,
    onBack,
    setControllerIconStyle,
    setDirectPartyTargeting,
  ])

  return (
    <Iwt2ScreenShell title="Settings" onBack={onBack}>
      <div className="iwt2-settings-panel">
        <Iwt2ActionList actions={actions} />
        <div className="iwt2-controller-preview" aria-label="Current controller icon style">
          <span>{CONTROLLER_ICON_LABELS[controllerIconStyle]}</span>
          <ControllerStylePreview iconStyle={controllerIconStyle} />
        </div>
      </div>
    </Iwt2ScreenShell>
  )
}
