import { lazy, Suspense, useEffect, useMemo, useState } from 'react'
import {
  loadActionMechanicConfig,
  resetActionMechanicConfig,
  saveActionMechanicConfig,
  type ActionAiAvoidanceSource,
  type ActionAttackConfig,
  type ActionMechanicConfig,
} from '../actionBoss/actionEncounterConfig'
import type { EnemyKind } from '../actionBoss/actionCombatSimulation'
import {
  ACTION_DIFFICULTY_TIERS,
  addActionRosterCharacter,
  completeActionDungeonHunt,
  completeActionPvpRound,
  equipActionItem,
  getActionTalentPoints,
  getActionCoinCount,
  getActionDifficultyTier,
  getActionLevelProgress,
  getActiveActionCharacter,
  loadActionRoster,
  saveActionRoster,
  switchActionRosterCharacter,
  updateActionRosterCharacter,
  type ActionCharacter,
  type ActionCharacterRoster,
  type ActionDifficulty,
  type ActionDungeonId,
  type ActionGearSource,
  type ActionRunMode,
  type ActionRunReward,
} from '../actionMode'
import { ACTION_DUNGEON_ICONS } from '../actionAssets'
import {
  buyArenaUpgrade,
  createArenaMatchState,
  getArenaAvailableUpgrades,
  getArenaSpellName,
  getArenaSpellLoadout,
  resolveArenaRoundWinner,
  startNextArenaRound,
  type ArenaMatchState,
} from '../actionArena'
import { PauseSettingsMenu } from './CombatPauseMenu'
import { ActionCharacterRosterPanel } from './ActionCharacterRosterPanel'
import { ActionCharacterMenu, ActionInventoryMenu, ActionMenuOverlay, ActionTalentMenu } from './ActionEquipmentMenus'
import { BulldromeBossSlice } from './BulldromeBossSlice'
import type { SpellSlot } from '../actionCombatCore'
import {
  cancelArenaQueue,
  checkArenaQueue,
  joinArenaQueue,
  type AuthAccount,
} from '../actionApi'

type ActionModeScreenProps = {
  account?: AuthAccount
  authActionLabel?: string
  onCharacterSaved?: (character: ActionCharacter) => void
  onBack?: () => void
  onLogout?: () => void
  onMechanicsSaved?: (config: ActionMechanicConfig) => void
}

const ArenaBattleSlice = lazy(() => import('./ArenaBattleSlice').then((module) => ({ default: module.ArenaBattleSlice })))
const ActionCharacterCreator = lazy(() => import('./ActionCharacterCreation').then((module) => ({ default: module.ActionCharacterCreator })))

type ActionHubTab = 'dungeons' | 'raids' | 'pvp' | 'roguelike' | 'character' | 'inventory' | 'talents' | 'characters' | 'settings'
type ActionHubScreen = 'menu' | ActionHubTab
type PlayableActionDungeonId = ActionDungeonId
type ArenaShopCategory = `Slot ${SpellSlot}` | 'Misc'

const AI_AVOIDANCE_LABELS: Record<ActionAiAvoidanceSource, string> = {
  projectile: 'AI Avoid Projectile',
  persistentArea: 'AI Avoid Floor',
  circleTelegraph: 'AI Avoid Circle',
  lineTelegraph: 'AI Avoid Line',
}

const ARENA_SHOP_SLOT_CATEGORIES = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as const

function getArenaShopCategory(scope: string): ArenaShopCategory {
  const slot = Number(scope.replace('Slot ', ''))
  return ARENA_SHOP_SLOT_CATEGORIES.includes(slot as SpellSlot) ? `Slot ${slot as SpellSlot}` : 'Misc'
}

function getArenaShopCategoryLabel(category: ArenaShopCategory) {
  const slot = Number(category.replace('Slot ', ''))
  return ARENA_SHOP_SLOT_CATEGORIES.includes(slot as SpellSlot) ? getArenaSpellName(slot as SpellSlot) : 'General Buffs'
}

function getArenaUpgradeScopeLabel(scope: string) {
  if (scope === 'Every Slot') return 'Every Spell'
  return getArenaShopCategoryLabel(getArenaShopCategory(scope))
}

function getArenaUpgradeDescription(description: string) {
  return ARENA_SHOP_SLOT_CATEGORIES.reduce((text, slot) => {
    const name = getArenaSpellName(slot)
    return text
      .replaceAll(`Slot ${slot}`, name)
      .replaceAll(`slot ${slot}`, name)
  }, description)
}

const ACTION_HUB_ITEMS: Array<{
  id: ActionHubTab
  label: string
  glyph: string
  description: string
}> = [
  { id: 'dungeons', label: 'Dungeons', glyph: 'D', description: 'Run action dungeons and earn gear.' },
  { id: 'raids', label: 'Raids', glyph: 'R', description: 'Large action encounters.' },
  { id: 'pvp', label: 'PVP', glyph: 'P', description: 'Action healer competitions.' },
  { id: 'roguelike', label: 'Roguelike', glyph: 'L', description: 'Draft upgrades through action fights.' },
  { id: 'character', label: 'Character', glyph: 'C', description: 'View ClaudeCraft equipment and stats.' },
  { id: 'inventory', label: 'Inventory', glyph: 'B', description: 'Manage ClaudeCraft bags and loot.' },
  { id: 'talents', label: 'Talents', glyph: 'T', description: 'Spend ClaudeCraft talent points.' },
  { id: 'characters', label: 'Characters', glyph: '@', description: 'Create or switch local characters.' },
  { id: 'settings', label: 'Settings', glyph: 'S', description: 'Tune action mode controls.' },
]

export function ActionModeScreen({
  account,
  authActionLabel = 'Logout',
  onBack,
  onCharacterSaved,
  onLogout,
  onMechanicsSaved,
}: ActionModeScreenProps) {
  const [roster, setRoster] = useState<ActionCharacterRoster>(() => loadActionRoster())
  const [activeDifficulty, setActiveDifficulty] = useState<ActionDifficulty | null>(null)
  const [activeDungeonId, setActiveDungeonId] = useState<PlayableActionDungeonId>('bulldrome')
  const [activeRunMode, setActiveRunMode] = useState<ActionRunMode>('hunt')
  const [activeScreen, setActiveScreen] = useState<ActionHubScreen>('menu')
  const [creatingCharacter, setCreatingCharacter] = useState(false)
  const [lastReward, setLastReward] = useState<ActionRunReward | null>(null)
  const [runKey, setRunKey] = useState(0)
  const [message, setMessage] = useState('')
  const character = useMemo(() => getActiveActionCharacter(roster), [roster])
  const progress = useMemo(() => character ? getActionLevelProgress(character) : null, [character])
  const talentPoints = useMemo(() => character ? getActionTalentPoints(character) : null, [character])
  const updateCharacter = (updater: (character: ActionCharacter) => ActionCharacter) => {
    setRoster((current) => {
      const activeCharacter = getActiveActionCharacter(current)
      if (!activeCharacter) return current
      return updateActionRosterCharacter(current, updater(activeCharacter))
    })
  }
  const equipCharacterItem = (itemId: string) => {
    updateCharacter((current) => {
      const nextCharacter = equipActionItem(current, itemId)
      setMessage(nextCharacter !== current ? 'Item equipped.' : 'Cannot equip that item.')
      return nextCharacter
    })
  }

  useEffect(() => {
    saveActionRoster(roster)
    if (character) onCharacterSaved?.(character)
  }, [character, onCharacterSaved, roster])

  if (!character || creatingCharacter) {
    return (
      <main className="game-shell action-mode-shell">
        <section className="content-screen action-mode-screen character-creation-screen">
          <Suspense fallback={<div className="boss-render-loading">Loading character creator...</div>}>
            <ActionCharacterCreator
              existingNames={roster.characters.map((candidate) => candidate.name)}
              onCancel={character ? () => setCreatingCharacter(false) : undefined}
              onCreate={(input) => {
                setRoster((current) => addActionRosterCharacter(current, input))
                setCreatingCharacter(false)
                setActiveScreen('menu')
                setMessage('Character created.')
              }}
            />
          </Suspense>
        </section>
      </main>
    )
  }

  if (activeDifficulty) {
    return (
      <div className="action-run-shell">
        <BulldromeBossSlice
          character={character}
          dungeonId={activeDungeonId}
          difficulty={activeDifficulty}
          runMode={activeRunMode}
          key={`${activeDungeonId}-${activeDifficulty}-${activeRunMode}-${runKey}`}
          onEquipItem={equipCharacterItem}
          onCharacterChange={(nextCharacter) => updateCharacter(() => nextCharacter)}
          onExit={() => {
            setLastReward(null)
            setActiveDifficulty(null)
            setActiveScreen('dungeons')
          }}
          onRunComplete={() => {
            if (lastReward && activeRunMode !== 'marathon') return
            const { character: nextCharacter, reward } = completeActionDungeonHunt(character, activeDungeonId, activeDifficulty)
            updateCharacter(() => nextCharacter)
            if (activeRunMode !== 'marathon') setLastReward(reward)
            setMessage('')
          }}
        />
        {lastReward && (
          <RunRewardModal
            dungeonId={activeDungeonId}
            difficulty={activeDifficulty}
            reward={lastReward}
            onGoAgain={() => {
              setLastReward(null)
              setRunKey((current) => current + 1)
            }}
            onMainMenu={() => {
              setLastReward(null)
              setActiveDifficulty(null)
              setActiveScreen('menu')
            }}
          />
        )}
      </div>
    )
  }

  return (
    <main className="game-shell action-mode-shell">
      <section className="content-screen action-mode-screen">
        {activeScreen !== 'menu' && activeScreen !== 'pvp' && activeScreen !== 'talents' && (
          <div className="action-screen-actions">
            <button className="back-button" onClick={() => setActiveScreen('menu')} type="button">
              Back
            </button>
            {onLogout && (
              <button className="back-button auth-mini-button" onClick={onLogout} type="button">
                {authActionLabel}
              </button>
            )}
          </div>
        )}

        {activeScreen === 'menu' && (
          <div className="action-main-menu">
            <nav className="action-hub-nav" aria-label="Action mode sections">
              {ACTION_HUB_ITEMS.map((item) => (
                <button
                  className="menu-card"
                  key={item.id}
                  onClick={() => setActiveScreen(item.id)}
                  type="button"
                >
                  <span>{item.glyph}</span>
                  <div>
                    <strong>{item.label}</strong>
                    <small>{item.description}</small>
                  </div>
                </button>
              ))}
            </nav>
            <aside className="action-menu-account">
              <div className="action-character-strip">
                <strong>{character.name}</strong>
                {account && <small>@{account.username}</small>}
                <small>{getClassLabel(character.classId)}</small>
                <small>Level {character.level}</small>
                {talentPoints && talentPoints.available > 0 && <small>{talentPoints.available} Talents</small>}
                <div className="header-xp" title={`${character.experience} action experience`}>
                  <span style={{ width: `${progress?.percent ?? 0}%` }} />
                </div>
              </div>
              {onLogout && (
                <button className="back-button auth-mini-button" onClick={onLogout} type="button">
                  {authActionLabel}
                </button>
              )}
              {onBack && (
                <button className="back-button auth-mini-button" onClick={onBack} type="button">
                  Back
                </button>
              )}
            </aside>
          </div>
        )}

        {activeScreen !== 'pvp' && message && <p className="action-mode-message">{message}</p>}

        {activeScreen === 'dungeons' && (
          <DungeonsPanel
            character={character}
            onStart={(dungeonId, difficulty) => {
              setActiveRunMode('hunt')
              setLastReward(null)
              setActiveDungeonId(dungeonId)
              setRunKey((current) => current + 1)
              setActiveDifficulty(difficulty)
            }}
            onStartMarathon={(dungeonId, difficulty) => {
              setActiveRunMode('marathon')
              setLastReward(null)
              setActiveDungeonId(dungeonId)
              setRunKey((current) => current + 1)
              setActiveDifficulty(difficulty)
            }}
          />
        )}

        {activeScreen === 'character' && (
          <ActionCharacterMenu
            character={character}
            onOpenInventory={() => setActiveScreen('inventory')}
            onOpenTalents={() => setActiveScreen('talents')}
          />
        )}

        {activeScreen === 'inventory' && (
          <ActionInventoryMenu
            character={character}
            onEquip={equipCharacterItem}
          />
        )}

        {activeScreen === 'talents' && (
          <ActionTalentMenu
            character={character}
            onBack={() => setActiveScreen('menu')}
            onCharacterChange={(nextCharacter) => updateCharacter(() => nextCharacter)}
          />
        )}

        {activeScreen === 'characters' && (
          <ActionCharacterRosterPanel
            activeCharacterId={roster.activeCharacterId}
            characters={roster.characters}
            onCreateNew={() => setCreatingCharacter(true)}
            onSwitch={(characterId) => {
              setRoster((current) => switchActionRosterCharacter(current, characterId))
              setMessage('Character switched.')
            }}
          />
        )}

        {activeScreen === 'pvp' && (
          <ArenasPanel
            account={account}
            character={character}
            onBack={() => setActiveScreen('menu')}
            onCharacterChange={(nextCharacter) => updateCharacter(() => nextCharacter)}
            onEquipItem={equipCharacterItem}
          />
        )}

        {activeScreen === 'settings' && (
          <ActionMechanicsAdmin onConfigSaved={onMechanicsSaved} />
        )}

        {activeScreen !== 'menu' && activeScreen !== 'dungeons' && activeScreen !== 'character' && activeScreen !== 'inventory' && activeScreen !== 'talents' && activeScreen !== 'characters' && activeScreen !== 'pvp' && activeScreen !== 'settings' && (
          <section className="action-placeholder-panel">
            <p className="eyebrow">{getHubTitle(activeScreen)}</p>
            <h2>Coming Soon</h2>
            <p>Content hooks can land here when this section is ready.</p>
          </section>
        )}
      </section>
    </main>
  )
}

function ArenasPanel({
  account,
  character,
  onBack,
  onCharacterChange,
  onEquipItem,
}: {
  account?: AuthAccount
  character: ActionCharacter
  onBack: () => void
  onCharacterChange: (character: ActionCharacter) => void
  onEquipItem: (itemId: string) => void
}) {
  const [match, setMatch] = useState<ArenaMatchState>(() => createArenaMatchState(character.name))
  const [shopReady, setShopReady] = useState(false)
  const [shopCategory, setShopCategory] = useState<ArenaShopCategory>('Slot 1')
  const [menuPaused, setMenuPaused] = useState(false)
  const [menuPausePanel, setMenuPausePanel] = useState<'settings' | 'character' | 'inventory' | 'talents'>('settings')
  const [queueMessage, setQueueMessage] = useState('')
  const [queueing, setQueueing] = useState(false)
  const availableUpgrades = useMemo(() => getArenaAvailableUpgrades(match), [match])
  const visibleUpgrades = useMemo(
    () => availableUpgrades.filter((upgrade) => {
      if (getArenaShopCategory(upgrade.scope) === shopCategory) return true
      return shopCategory !== 'Misc' && upgrade.scope === 'Every Slot'
    }),
    [availableUpgrades, shopCategory],
  )
  const ownedUpgrades = useMemo(
    () => availableUpgrades.filter((upgrade) => upgrade.owned),
    [availableUpgrades],
  )
  const spellLoadout = useMemo(() => getArenaSpellLoadout(), [])

  useEffect(() => {
    if (match.phase === 'fighting') return
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.repeat || event.key !== 'Escape') return
      event.preventDefault()
      setMenuPausePanel('settings')
      setMenuPaused((current) => !current)
    }
    let frameId = 0
    let startPressed = false
    const pollGamepadStart = () => {
      const pressed = Array.from(navigator.getGamepads?.() ?? [])
        .some((gamepad) => gamepad?.buttons[9]?.pressed)
      if (pressed && !startPressed) {
        setMenuPausePanel('settings')
        setMenuPaused((current) => !current)
      }
      startPressed = pressed
      frameId = window.requestAnimationFrame(pollGamepadStart)
    }
    window.addEventListener('keydown', handleKeyDown)
    frameId = window.requestAnimationFrame(pollGamepadStart)
    return () => {
      window.removeEventListener('keydown', handleKeyDown)
      window.cancelAnimationFrame(frameId)
    }
  }, [match.phase])

  async function findOnlineMatch() {
    if (!account || queueing) return
    setQueueing(true)
    setQueueMessage('Searching for arena opponent...')
    let ticketId = ''
    try {
      const joined = await joinArenaQueue()
      ticketId = joined.ticketId
      let result = joined
      for (let attempt = 0; attempt < 5 && result.status !== 'matched'; attempt += 1) {
        await new Promise((resolve) => window.setTimeout(resolve, 1000))
        result = await checkArenaQueue(ticketId)
      }
      if (result.status === 'matched' && result.match && result.side) {
        const opponentSide = result.side === 'a' ? 'b' : 'a'
        const opponentName = result.match.players[opponentSide].displayName
        setMatch({
          ...createArenaMatchState(character.name),
          opponentName,
          history: [`Matched against ${opponentName}.`],
        })
        setQueueMessage(`Matched against ${opponentName}.`)
        return
      }
      await cancelArenaQueue(ticketId).catch(() => null)
      setMatch({
        ...createArenaMatchState(character.name),
        opponentName: 'Rival Five',
        history: ['No online opponent found. CPU team loaded.'],
      })
      setQueueMessage('No online opponent found. CPU team loaded.')
    } catch (error) {
      if (ticketId) await cancelArenaQueue(ticketId).catch(() => null)
      setQueueMessage(error instanceof Error ? error.message : 'Arena queue failed.')
    } finally {
      setQueueing(false)
    }
  }

  function beginRound() {
    const next = startNextArenaRound(match)
    setShopReady(false)
    setMatch(next)
  }

  function resetMatch() {
    setMatch(createArenaMatchState(character.name))
    setQueueMessage('')
    setShopReady(false)
    setShopCategory('Slot 1')
  }

  function completeArenaRound(winner: 'player' | 'opponent', elapsedSeconds: number) {
    const nextMatch = resolveArenaRoundWinner(match, winner, elapsedSeconds)
    const playerWonRound = winner === 'player'
    const playerWonMatch = nextMatch.phase === 'complete' && nextMatch.playerWins > nextMatch.opponentWins
    const { character: nextCharacter, reward } = completeActionPvpRound(character, playerWonRound, playerWonMatch)
    const rewardParts = [
      `+${reward.roundExperience} XP`,
      ...(reward.matchBonusExperience > 0 ? [`+${reward.matchBonusExperience} match bonus`] : []),
      ...(reward.leveledUp ? ['Level up'] : []),
    ]

    onCharacterChange(nextCharacter)
    setMatch({
      ...nextMatch,
      history: [`${rewardParts.join(' · ')}.`, ...nextMatch.history].slice(0, 6),
    })
    setQueueMessage(rewardParts.join(' · '))
    setShopReady(false)
  }

  const matchComplete = match.phase === 'complete'
  const completedRounds = match.playerWins + match.opponentWins
  const leader = match.playerWins === match.opponentWins
    ? 'Tied'
    : match.playerWins > match.opponentWins
      ? character.name
      : match.opponentName
  const lastResult = match.history[0] || 'No rounds played yet.'

  if (match.phase === 'fighting') {
    return (
      <Suspense fallback={<div className="boss-render-loading">Loading arena...</div>}>
        <ArenaBattleSlice
          character={character}
          opponentName={match.opponentName}
          opponentUpgrades={match.opponentUpgrades}
          opponentWins={match.opponentWins}
          onCharacterChange={onCharacterChange}
          onEquipItem={onEquipItem}
          onExit={() => {
            setMatch((current) => ({ ...current, phase: 'upgrade' }))
            setShopReady(false)
          }}
          onRoundComplete={(winner, elapsedSeconds) => {
            completeArenaRound(winner, elapsedSeconds)
          }}
          playerName={match.playerName}
          playerUpgrades={match.playerUpgrades}
          playerWins={match.playerWins}
          round={match.round}
        />
      </Suspense>
    )
  }

  return (
    <section className="arena-panel pvp-roguelike-panel">
      <header className="pvp-compact-header">
        <div className="arena-scoreboard" aria-label="Arena match score">
          <span>{match.playerName}</span>
          <strong>{match.playerWins} - {match.opponentWins}</strong>
          <span>{match.opponentName}</span>
        </div>
      </header>

      {queueMessage && <p className="arena-queue-message">{queueMessage}</p>}

      {matchComplete ? (
        <section className="pvp-roguelike-result">
          <div className="pvp-result-emblem">{match.playerWins > match.opponentWins ? 'WIN' : 'LOSS'}</div>
          <div>
            <p className="eyebrow">Match Complete</p>
            <h3>{leader} wins the race</h3>
            <p>{lastResult}</p>
          </div>
          <button className="primary-button" onClick={resetMatch} type="button">New Match</button>
        </section>
      ) : match.phase === 'upgrade' ? (
        <section className="pvp-roguelike-shop">
          <header className="pvp-shop-header">
            <div>
              <p className="eyebrow">Buy Round</p>
              <h3>Round {completedRounds} Complete</h3>
              <span>{lastResult}</span>
            </div>
            <div className="pvp-shop-points" aria-label="Available points">
              <span>Available Points</span>
              <strong>{match.playerCredits}</strong>
            </div>
            <div className="arena-actions">
              <button
                className="back-button"
                disabled={shopReady}
                onClick={() => setShopReady(true)}
                type="button"
              >
                Finish Buying
              </button>
              <button
                className="primary-button"
                disabled={!shopReady}
                onClick={beginRound}
                type="button"
              >
                Next Round
              </button>
            </div>
          </header>

          <div className="pvp-shop-layout">
            <aside className="pvp-shop-spells">
              <div className="pvp-shop-summary">
                <div>
                  <span>Status</span>
                  <strong>{shopReady ? 'Ready' : 'Buying'}</strong>
                </div>
              </div>

              <nav className="pvp-shop-spell-grid" aria-label="Spell buff categories">
                {spellLoadout.map((spell) => {
                  const category = `Slot ${spell.slot}` as ArenaShopCategory
                  return (
                    <button
                      className={category === shopCategory ? 'active' : ''}
                      key={spell.slot}
                      onClick={() => setShopCategory(category)}
                      type="button"
                      title={`${spell.name} - ${spell.cost} mana`}
                    >
                      <span>{spell.slot}</span>
                      <strong>{spell.name}</strong>
                    </button>
                  )
                })}
              </nav>

              <button
                className={`pvp-shop-general ${shopCategory === 'Misc' ? 'active' : ''}`}
                onClick={() => setShopCategory('Misc')}
                type="button"
              >
                General Buffs
              </button>
            </aside>

            <section className="pvp-shop-main">
              <div className="pvp-shop-category-header">
                <div>
                  <p className="eyebrow">Buffs</p>
                  <h3>{getArenaShopCategoryLabel(shopCategory)}</h3>
                </div>
                <span>{visibleUpgrades.length} Buffs</span>
              </div>
              <div className="arena-upgrade-list pvp-shop-grid">
                {visibleUpgrades.length ? visibleUpgrades.map((upgrade) => (
                  <button
                    className={upgrade.owned ? 'owned' : ''}
                    disabled={shopReady || (!upgrade.owned && !upgrade.affordable)}
                    key={upgrade.id}
                    onClick={() => setMatch((current) => buyArenaUpgrade(current, upgrade.id))}
                    type="button"
                  >
                    <span>{getArenaUpgradeScopeLabel(upgrade.scope)}</span>
                    <strong>{upgrade.label}</strong>
                    <small>{getArenaUpgradeDescription(upgrade.description)}</small>
                    <em>{upgrade.owned ? 'Selected - Click to Remove' : upgrade.affordable ? `${upgrade.cost} Points` : `Need ${upgrade.cost} Points`}</em>
                  </button>
                )) : (
                  <div className="pvp-shop-empty">
                    <strong>No buffs yet</strong>
                    <span>Pick another spell or General Buffs.</span>
                  </div>
                )}
              </div>
            </section>

            <aside className="pvp-active-buffs">
              <div>
                <p className="eyebrow">Bought</p>
                <h3>{ownedUpgrades.length ? `${ownedUpgrades.length} Active` : 'None Yet'}</h3>
              </div>
              <div>
                {ownedUpgrades.length ? ownedUpgrades.map((upgrade) => (
                  <span key={upgrade.id}>{upgrade.label}</span>
                )) : <span>Spend points before readying.</span>}
              </div>
            </aside>
          </div>
        </section>
      ) : (
        <div className="pvp-roguelike-menu">
          <section className="pvp-mode-panel">
            <div className="pvp-row-heading">
              <div>
                <p className="eyebrow">Match Type</p>
                <h3>Boss Race</h3>
              </div>
            </div>
            <div className="pvp-mode-buttons">
              <button className="active" type="button">3D Roguelike</button>
              <button disabled type="button">2D Legacy</button>
            </div>
            <dl>
              <div>
                <dt>Rounds</dt>
                <dd>First to 3</dd>
              </div>
              <div>
                <dt>Dampening</dt>
                <dd>+1% every 5s</dd>
              </div>
              <div>
                <dt>Room</dt>
                <dd>Split Boss Arena</dd>
              </div>
            </dl>
          </section>

          <section className="pvp-queue-panel">
            <div className="pvp-queue-glyph">VS</div>
            <div>
              <p className="eyebrow">Queue</p>
              <h3>{match.playerName} vs {match.opponentName}</h3>
              <p>Race the other party through boss spawns. Buy buffs after each round.</p>
            </div>
            <div className="pvp-queue-actions">
              <button className="primary-button" onClick={beginRound} type="button">
                Start Round
              </button>
              {account && (
                <button
                  className="back-button"
                  disabled={queueing}
                  onClick={findOnlineMatch}
                  type="button"
                >
                  {queueing ? 'Queueing' : 'Find Match'}
                </button>
              )}
            </div>
          </section>

        </div>
      )}
      {menuPaused && (
        menuPausePanel === 'settings' ? (
          <PauseSettingsMenu
            exitLabel="Exit Game Mode"
            onExit={onBack}
            onOpenCharacter={() => setMenuPausePanel('character')}
            onOpenInventory={() => setMenuPausePanel('inventory')}
            onOpenTalents={() => setMenuPausePanel('talents')}
            onResume={() => setMenuPaused(false)}
          />
        ) : menuPausePanel === 'character' ? (
          <ActionMenuOverlay
            onBack={() => setMenuPausePanel('settings')}
            onClose={() => setMenuPaused(false)}
            title="Character"
          >
            <ActionCharacterMenu
              character={character}
              onOpenInventory={() => setMenuPausePanel('inventory')}
              onOpenTalents={() => setMenuPausePanel('talents')}
            />
          </ActionMenuOverlay>
        ) : menuPausePanel === 'talents' ? (
          <ActionMenuOverlay
            onBack={() => setMenuPausePanel('settings')}
            onClose={() => setMenuPaused(false)}
            title="Talents"
          >
            <ActionTalentMenu character={character} onCharacterChange={onCharacterChange} />
          </ActionMenuOverlay>
        ) : (
          <ActionMenuOverlay
            onBack={() => setMenuPausePanel('settings')}
            onClose={() => setMenuPaused(false)}
            title="Inventory"
          >
            <ActionInventoryMenu character={character} onEquip={onEquipItem} />
          </ActionMenuOverlay>
        )
      )}
    </section>
  )
}

function ActionMechanicsAdmin({ onConfigSaved }: { onConfigSaved?: (config: ActionMechanicConfig) => void }) {
  const [config, setConfig] = useState<ActionMechanicConfig>(() => loadActionMechanicConfig())
  const [enemyPage, setEnemyPage] = useState(0)
  const [attackPageByEnemy, setAttackPageByEnemy] = useState<Partial<Record<EnemyKind, number>>>({})

  function updateAttack(enemyKind: EnemyKind, attackId: string, patch: Partial<ActionAttackConfig>) {
    const next = {
      ...config,
      [enemyKind]: {
        ...config[enemyKind],
        attacks: config[enemyKind].attacks.map((attack) => (
          attack.id === attackId ? { ...attack, ...patch } : attack
        )),
      },
    }
    setConfig(next)
    saveActionMechanicConfig(next)
    onConfigSaved?.(next)
  }

  function resetConfig() {
    const next = resetActionMechanicConfig()
    setConfig(next)
    onConfigSaved?.(next)
    setEnemyPage(0)
    setAttackPageByEnemy({})
  }

  const enemyKinds = Object.keys(config) as EnemyKind[]
  const enemyKind = enemyKinds[Math.min(enemyPage, enemyKinds.length - 1)] ?? enemyKinds[0]
  const enemy = config[enemyKind]
  const enabledAttacks = enemy.attacks.filter((attack) => attack.enabled)
  const disabledAttacks = enemy.attacks.filter((attack) => !attack.enabled)
  const attackPage = Math.min(attackPageByEnemy[enemyKind] ?? 0, Math.max(0, enabledAttacks.length - 1))
  const activeAttack = enabledAttacks[attackPage] ?? null

  function setAttackPage(enemyKind: EnemyKind, page: number) {
    setAttackPageByEnemy((current) => ({ ...current, [enemyKind]: page }))
  }

  return (
    <section className="action-mechanics-admin">
      <header>
        <div>
          <p className="eyebrow">Admin</p>
          <h2>Mob And Boss Mechanics</h2>
        </div>
        <button className="back-button" onClick={resetConfig} type="button">Reset Defaults</button>
      </header>

      <article className="action-mechanic-card" key={enemy.kind}>
        <div className="action-mechanic-heading">
          <div>
            <p className="eyebrow">{enemy.role}</p>
            <h3>{enemy.label}</h3>
          </div>
          <span>{enemyPage + 1} / {enemyKinds.length}</span>
        </div>

        <div className="action-page-controls" aria-label="Mechanic pages">
          <button onClick={() => setEnemyPage((page) => Math.max(0, page - 1))} disabled={enemyPage === 0} type="button">
            Prev Mob
          </button>
          <button
            onClick={() => setEnemyPage((page) => Math.min(enemyKinds.length - 1, page + 1))}
            disabled={enemyPage >= enemyKinds.length - 1}
            type="button"
          >
            Next Mob
          </button>
        </div>

        {activeAttack ? (
          <div className="action-attack-list">
            <div className="action-page-controls" aria-label="Attack pages">
              <button onClick={() => setAttackPage(enemyKind, Math.max(0, attackPage - 1))} disabled={attackPage === 0} type="button">
                Prev Attack
              </button>
              <span>{attackPage + 1} / {enabledAttacks.length}</span>
              <button
                onClick={() => setAttackPage(enemyKind, Math.min(enabledAttacks.length - 1, attackPage + 1))}
                disabled={attackPage >= enabledAttacks.length - 1}
                type="button"
              >
                Next Attack
              </button>
            </div>
            <AttackEditor
              attack={activeAttack}
              key={activeAttack.id}
              onChange={(patch) => updateAttack(enemyKind, activeAttack.id, patch)}
            />
          </div>
        ) : (
          <p className="action-empty-note">No active attacks.</p>
        )}

        {disabledAttacks.length > 0 && (
          <div className="action-disabled-attacks">
            <strong>Add Attack</strong>
            {disabledAttacks.map((attack) => (
              <button
                key={attack.id}
                onClick={() => {
                  updateAttack(enemyKind, attack.id, { enabled: true })
                  setAttackPage(enemyKind, enabledAttacks.length)
                }}
                type="button"
              >
                {attack.label}
              </button>
            ))}
          </div>
        )}
      </article>
    </section>
  )
}

function AttackEditor({
  attack,
  onChange,
}: {
  attack: ActionAttackConfig
  onChange: (patch: Partial<ActionAttackConfig>) => void
}) {
  const aiAvoidanceEntries = Object.entries(attack.aiAvoidance ?? {}) as Array<[
    ActionAiAvoidanceSource,
    NonNullable<ActionAttackConfig['aiAvoidance']>[ActionAiAvoidanceSource],
  ]>

  function updateAiAvoidance(
    source: ActionAiAvoidanceSource,
    patch: Partial<NonNullable<ActionAttackConfig['aiAvoidance']>[ActionAiAvoidanceSource]>,
  ) {
    const current = attack.aiAvoidance?.[source]
    if (!current) return
    onChange({
      aiAvoidance: {
        ...attack.aiAvoidance,
        [source]: {
          ...current,
          ...patch,
        },
      },
    })
  }

  return (
    <section className="action-attack-editor">
      <header>
        <div>
          <strong>{attack.label}</strong>
          <small>{attack.kind}</small>
        </div>
        <button onClick={() => onChange({ enabled: false })} type="button">Remove</button>
      </header>

      <div className="action-attack-fields">
        <label>
          Frequency
          <input
            min="0"
            onChange={(event) => onChange({ frequencySeconds: Number(event.target.value) })}
            step="0.05"
            type="number"
            value={attack.frequencySeconds}
          />
        </label>
        <label>
          Damage
          <input
            min="0"
            onChange={(event) => onChange({ damage: Number(event.target.value) })}
            step="1"
            type="number"
            value={attack.damage}
          />
        </label>
        {attack.windupSeconds !== undefined && (
          <label>
            Windup
            <input
              min="0"
              onChange={(event) => onChange({ windupSeconds: Number(event.target.value) })}
              step="0.05"
              type="number"
              value={attack.windupSeconds}
            />
          </label>
        )}
        {attack.recoverSeconds !== undefined && (
          <label>
            Recover
            <input
              min="0"
              onChange={(event) => onChange({ recoverSeconds: Number(event.target.value) })}
              step="0.05"
              type="number"
              value={attack.recoverSeconds}
            />
          </label>
        )}
        {attack.speed !== undefined && (
          <label>
            Speed
            <input
              min="0"
              onChange={(event) => onChange({ speed: Number(event.target.value) })}
              step="10"
              type="number"
              value={attack.speed}
            />
          </label>
        )}
        {attack.radius !== undefined && (
          <label>
            Radius
            <input
              min="0"
              onChange={(event) => onChange({ radius: Number(event.target.value) })}
              step="1"
              type="number"
              value={attack.radius}
            />
          </label>
        )}
        {attack.everyNthCharge !== undefined && (
          <label>
            Every Charges
            <input
              min="1"
              onChange={(event) => onChange({ everyNthCharge: Number(event.target.value) })}
              step="1"
              type="number"
              value={attack.everyNthCharge}
            />
          </label>
        )}
      </div>

      {aiAvoidanceEntries.length > 0 && (
        <div className="action-attack-fields">
          {aiAvoidanceEntries.map(([source, avoidance]) => {
            if (!avoidance) return null
            return (
              <div className="action-ai-avoidance-fields" key={source}>
                <label className="action-toggle-field">
                  <input
                    checked={avoidance.enabled}
                    onChange={(event) => updateAiAvoidance(source, { enabled: event.target.checked })}
                    type="checkbox"
                  />
                  {AI_AVOIDANCE_LABELS[source]}
                </label>
                <label>
                  Buffer
                  <input
                    min="0"
                    onChange={(event) => updateAiAvoidance(source, { buffer: Number(event.target.value) })}
                    step="1"
                    type="number"
                    value={avoidance.buffer}
                  />
                </label>
                <label>
                  Move Speed
                  <input
                    min="0"
                    onChange={(event) => updateAiAvoidance(source, { moveSpeed: Number(event.target.value) })}
                    step="5"
                    type="number"
                    value={avoidance.moveSpeed}
                  />
                </label>
                <label>
                  Avoid Distance
                  <input
                    min="1"
                    onChange={(event) => updateAiAvoidance(source, { avoidDistance: Number(event.target.value) })}
                    step="5"
                    type="number"
                    value={avoidance.avoidDistance}
                  />
                </label>
                {avoidance.lineWidthMultiplier !== undefined && (
                  <label>
                    Line Width
                    <input
                      min="0"
                      onChange={(event) => updateAiAvoidance(source, { lineWidthMultiplier: Number(event.target.value) })}
                      step="0.05"
                      type="number"
                      value={avoidance.lineWidthMultiplier}
                    />
                  </label>
                )}
              </div>
            )
          })}
        </div>
      )}
    </section>
  )
}

function DungeonsPanel({
  character,
  onStart,
  onStartMarathon,
}: {
  character: ActionCharacter
  onStart: (dungeonId: PlayableActionDungeonId, difficulty: ActionDifficulty) => void
  onStartMarathon: (dungeonId: PlayableActionDungeonId, difficulty: ActionDifficulty) => void
}) {
  const [selectedDungeonId, setSelectedDungeonId] = useState<PlayableActionDungeonId>('bulldrome')
  const [selectedDifficulty, setSelectedDifficulty] = useState<ActionDifficulty>('ilvl-1')
  const selectedDungeon = ACTION_DUNGEONS.find((dungeon) => dungeon.id === selectedDungeonId) ?? ACTION_DUNGEONS[0]
  const selectedTier = getActionDifficultyTier(selectedDifficulty)
  const selectedCoinCount = getActionCoinCount(character, selectedDungeon.source, selectedTier.coinColor)

  return (
    <div className="action-dungeon-board">
      <section className="action-dungeon-list" aria-label="Action dungeons">
        {ACTION_DUNGEONS.map((dungeon) => {
          const selected = dungeon.id === selectedDungeonId
          const locked = dungeon.locked
          return (
            <button
              className={`action-dungeon-card ${selected ? 'selected' : ''} ${locked ? 'locked' : ''}`}
              disabled={locked}
              key={dungeon.id}
              onClick={() => {
                if (!locked) setSelectedDungeonId(dungeon.id as PlayableActionDungeonId)
              }}
              type="button"
            >
              <span className="action-dungeon-glyph">
                <img alt="" src={dungeon.icon} />
                <span>{dungeon.glyph}</span>
              </span>
              <span>
                <small>{dungeon.eyebrow}</small>
                <strong>{dungeon.name}</strong>
                <i>{dungeon.summary}</i>
              </span>
            </button>
          )
        })}
      </section>

      <aside className="action-dungeon-setup">
        <section className="action-dungeon-selected">
          <p className="eyebrow">Selected Run</p>
          <h2>{selectedDungeon.name}</h2>
          <p>{selectedDungeon.description}</p>
          <div className="tag-row">
            <span>{selectedTier.label}</span>
            <span>{selectedTier.healthMultiplier}x HP</span>
            <span>{selectedTier.damageMultiplier}x damage</span>
            <span>{selectedTier.coinLabel}</span>
          </div>
          <dl>
            <div><dt>Tier Coins</dt><dd>{selectedCoinCount}</dd></div>
            <div><dt>Gear Drop</dt><dd>Shared action loot</dd></div>
            <div><dt>XP</dt><dd>{selectedTier.experience}</dd></div>
          </dl>
        </section>

        <section className="action-dungeon-tier">
          <div>
            <p className="eyebrow">Item Level</p>
            <h2>Tier</h2>
          </div>
          <div className="action-tier-grid">
            {ACTION_DIFFICULTY_TIERS.map((tier) => (
              <button
                className={`${selectedDifficulty === tier.id ? 'selected' : ''} coin-${tier.coinColor}`}
                key={tier.id}
                onClick={() => setSelectedDifficulty(tier.id)}
                type="button"
              >
                <strong>{tier.label}</strong>
                <span>{tier.coinLabel}</span>
              </button>
            ))}
          </div>
        </section>

        <section className="action-dungeon-start">
          <div>
            <p className="eyebrow">Start</p>
            <h2>Run</h2>
          </div>
          <div className="action-dungeon-actions">
            <button className="primary-button" onClick={() => onStart(selectedDungeonId, selectedDifficulty)} type="button">
              Start Hunt
            </button>
            <button className="primary-button" onClick={() => onStartMarathon(selectedDungeonId, selectedDifficulty)} type="button">
              Start Marathon
            </button>
          </div>
          <p>Marathon respawns the boss after a 5 second break.</p>
        </section>
      </aside>
    </div>
  )
}

const ACTION_DUNGEONS: Array<{
  id: ActionDungeonId
  eyebrow: string
  glyph: string
  icon: string
  name: string
  summary: string
  description: string
  source: ActionGearSource
  coinLabel: string
  locked?: boolean
}> = [
  {
    id: 'bulldrome',
    eyebrow: 'Dungeon 1',
    glyph: 'B',
    icon: ACTION_DUNGEON_ICONS.bulldrome,
    name: 'Bulldrome Hunting Grounds',
    summary: 'Charge lanes and ground slam telegraphs.',
    description: 'Bulldrome is a single boss hunt built on the shared action combat rules.',
    source: 'bulldrome',
    coinLabel: 'White Bulldrome Coins',
  },
  {
    id: 'yian-kut-ku',
    eyebrow: 'Dungeon 2',
    glyph: 'Y',
    icon: ACTION_DUNGEON_ICONS['yian-kut-ku'],
    name: 'Yian Kut-Ku Roost',
    summary: 'Bouncing fireballs and fire patches.',
    description: 'Yian Kut-Ku is a single boss hunt with bouncing fireball mechanics.',
    source: 'yian-kut-ku',
    coinLabel: 'White Yian Kut-Ku Coins',
  },
  {
    id: 'cyber-dragon',
    eyebrow: 'Dungeon 3',
    glyph: 'C',
    icon: ACTION_DUNGEON_ICONS['cyber-dragon'],
    name: 'Cyber Dragon Core',
    summary: 'Chasing saw blades and circular charge lanes.',
    description: 'Cyber Dragon spawns slow pursuit blades that bleed targets and charges after circling the room.',
    source: 'cyber-dragon',
    coinLabel: 'Cyber Dragon Coins',
  },
]

function RunRewardModal({
  dungeonId,
  difficulty,
  onGoAgain,
  onMainMenu,
  reward,
}: {
  dungeonId: PlayableActionDungeonId
  difficulty: ActionDifficulty
  onGoAgain: () => void
  onMainMenu: () => void
  reward: ActionRunReward
}) {
  return (
    <div className="action-run-reward-backdrop" role="dialog" aria-modal="true" aria-labelledby="action-run-reward-title">
      <section className="action-run-reward-modal">
        <p className="eyebrow">Dungeon Complete</p>
        <h1 id="action-run-reward-title">
          {getRunRewardTitle(dungeonId, difficulty)}
        </h1>
        <div className="action-run-reward-summary">
          <div>
            <dt>XP</dt>
            <dd>{reward.experience}</dd>
          </div>
          <div>
            <dt>{reward.coinName}</dt>
            <dd>{reward.coins}</dd>
          </div>
          <div>
            <dt>Level</dt>
            <dd>{reward.leveledUp ? 'Up' : 'No Change'}</dd>
          </div>
        </div>
        <div className="action-run-loot-list">
          <strong>Loot</strong>
          {reward.items.length === 0 ? (
            <p>No gear dropped.</p>
          ) : (
            reward.items.map((item, index) => (
              <span key={`${item.id}-${index}`}>{item.name} · {item.quality ?? 'common'} · {item.slot}</span>
            ))
          )}
        </div>
        <div className="action-run-reward-actions">
          <button className="primary-button" onClick={onGoAgain} type="button">Go Again</button>
          <button className="back-button" onClick={onMainMenu} type="button">Main Menu</button>
        </div>
      </section>
    </div>
  )
}

function getHubTitle(screen: ActionHubScreen) {
  if (screen === 'menu') return 'Action Mode'
  return ACTION_HUB_ITEMS.find((item) => item.id === screen)?.label ?? 'Action Mode'
}

function getClassLabel(classId: ActionCharacter['classId']) {
  if (classId === 'paladin') return 'Paladin'
  if (classId === 'shaman') return 'Shaman'
  if (classId === 'druid') return 'Druid'
  if (classId === 'priest') return 'Priest'
  return classId
}

function getRunRewardTitle(dungeonId: PlayableActionDungeonId, difficulty: ActionDifficulty) {
  const dungeonName = ACTION_DUNGEONS.find((dungeon) => dungeon.id === dungeonId)?.name ?? 'Dungeon'
  return `${getActionDifficultyTier(difficulty).label} ${dungeonName}`
}
