import { useEffect, useState } from 'react'
import './App.css'
import { CombatScreen } from './components/CombatScreen'
import { AuthScreen } from './components/AuthScreen'
import { CustomizeScreen } from './components/CustomizeScreen'
import { EquipmentScreen } from './components/EquipmentScreen'
import { PvPRoguelikeScreen } from './components/PvpRoguelikeScreen'
import { TalentScreen } from './components/TalentScreen'
import { SettingsScreen } from './components/SettingsScreen'
import {
  loadCpuPvpLeaderboard,
  type CpuPvpLeaderboardEntry,
  type PvpContentType,
} from './pvpRoguelike'
import {
  loadAuthSession,
  logoutAccount,
  type Account,
  type AuthSession,
  type CharacterProfile,
} from './profile'
import {
  getCloudSyncStatus,
  getGameMode,
  syncCloudSave,
  type GameMode,
} from './gameRepository'
import { focusFirstControl } from './input.tsx'

type Screen =
  | 'menu'
  | 'dungeons'
  | 'combat'
  | 'raids'
  | 'roguelike'
  | 'pvp'
  | 'customize'
  | 'equipment'
  | 'talents'
  | 'settings'

const MENU_ITEMS: Array<{
  screen: Screen
  label: string
  glyph: string
  description: string
}> = [
  { screen: 'dungeons', label: 'Dungeons', glyph: 'D', description: 'Guide a six-player party through dangerous encounters.' },
  { screen: 'raids', label: 'Raids', glyph: 'R', description: 'Guide an eighteen-player party through three-phase challenges.' },
  { screen: 'roguelike', label: 'Roguelike', glyph: 'L', description: 'Draft upgrades through escalating random encounters.' },
  { screen: 'pvp', label: 'PvP', glyph: 'P', description: 'Race another healer through roguelike encounters with buffs and sabotage.' },
  { screen: 'customize', label: 'Customize Character', glyph: 'C', description: 'Choose your class and prepare a six-ability loadout.' },
  { screen: 'settings', label: 'Settings', glyph: 'S', description: 'Remap PC and controller inputs.' },
]

const LAST_DIFFICULTY_KEY = 'i-want-to-heal:last-difficulty'
const SHOW_LEADERBOARDS = false

function activityInitials(name: string) {
  return name
    .split(/\s+/)
    .filter((word) => /^[A-Za-z0-9]/.test(word))
    .slice(0, 2)
    .map((word) => word[0].toUpperCase())
    .join('')
}

type RoguelikeUpgradeTiming = 'boss' | 'encounter'
type RoguelikeVariant = 'pve' | 'pvp'
type RoguelikeAbilityLabelMode = 'ability' | 'slot'

function App() {
  const [screen, setScreen] = useState<Screen>('menu')
  const [account, setAccount] = useState<Account | null>(null)
  const [profile, setProfile] = useState<CharacterProfile | null>(null)
  const [authChecked, setAuthChecked] = useState(false)
  const [gameMode, setGameMode] = useState<GameMode>(getGameMode())
  const [serverMessage, setServerMessage] = useState('')
  const [selectedDifficultyId, setSelectedDifficultyId] = useState(() => {
    const saved = Number(window.localStorage.getItem(LAST_DIFFICULTY_KEY))
    return Number.isFinite(saved) && saved > 0 ? saved : 1
  })
  const [selectedDungeonId, setSelectedDungeonId] = useState(1)
  const [selectedRaidId, setSelectedRaidId] = useState(2)
  const [roguelikeKind, setRoguelikeKind] = useState<'dungeon' | 'raid'>('dungeon')
  const [roguelikeVariant, setRoguelikeVariant] = useState<RoguelikeVariant>('pve')
  const [roguelikeUpgradeTiming, setRoguelikeUpgradeTiming] = useState<RoguelikeUpgradeTiming>('encounter')
  const [roguelikeAbilityLabelMode, setRoguelikeAbilityLabelMode] = useState<RoguelikeAbilityLabelMode>('ability')
  const [pvpContentType, setPvpContentType] = useState<PvpContentType>('dungeon')
  const [selectedPart, setSelectedPart] = useState(1)
  const [combatContentId, setCombatContentId] = useState(1)
  const [leaderboardCategory, setLeaderboardCategory] = useState<'part_1' | 'part_2' | 'part_3' | 'full_run'>('part_1')
  const [showLoot, setShowLoot] = useState(false)
  const [lootSort, setLootSort] = useState<'sequence' | 'boss'>('sequence')
  const [showLeaderboard, setShowLeaderboard] = useState(false)
  const [error, setError] = useState('')
  const [syncingCloud, setSyncingCloud] = useState(false)
  const [syncMessage, setSyncMessage] = useState('')

  useEffect(() => {
    loadAuthSession()
      .then((session) => {
        setAccount(session.account)
        setProfile(session.profile)
      })
      .catch((reason: unknown) => {
        setServerMessage(
          reason instanceof Error
            ? `${reason.message} Offline play is still available.`
            : 'Unable to reach the server. Offline play is still available.',
        )
      })
      .finally(() => setAuthChecked(true))
  }, [])

  useEffect(() => {
    const handleModeChange = (event: Event) => {
      const nextMode = (event as CustomEvent<GameMode>).detail
      setGameMode(nextMode)
    }
    window.addEventListener('chronicle:mode-changed', handleModeChange as EventListener)
    return () => {
      window.removeEventListener('chronicle:mode-changed', handleModeChange as EventListener)
    }
  }, [])

  useEffect(() => {
    if (screen === 'combat') return
    window.requestAnimationFrame(() => {
      focusFirstControl()
    })
  }, [screen])

  useEffect(() => {
    if (!authChecked || !account || !profile || screen === 'combat') return
    window.requestAnimationFrame(() => {
      focusFirstControl()
    })
  }, [account, authChecked, profile, screen])

  useEffect(() => {
    window.localStorage.setItem(LAST_DIFFICULTY_KEY, String(selectedDifficultyId))
  }, [selectedDifficultyId])

  const [cpuLeaderboard, setCpuLeaderboard] = useState<CpuPvpLeaderboardEntry[]>([])

  useEffect(() => {
    setCpuLeaderboard(loadCpuPvpLeaderboard(pvpContentType))
  }, [pvpContentType, screen, roguelikeVariant])

  function acceptSession(session: AuthSession) {
    setAccount(session.account)
    setProfile(session.profile)
    setGameMode(getGameMode())
    setScreen('menu')
    setError('')
    setServerMessage('')
    window.requestAnimationFrame(() => {
      focusFirstControl()
    })
  }

  async function signOut() {
    try {
      await logoutAccount()
      setAccount(null)
      setProfile(null)
      setGameMode(getGameMode())
      setScreen('menu')
      setSyncMessage('')
    } catch (reason) {
      setError(reason instanceof Error ? reason.message : 'Unable to sign out.')
    }
  }

  async function syncSaveNow() {
    setSyncingCloud(true)
    setSyncMessage('')
    try {
      const updated = await syncCloudSave()
      setProfile(updated)
      setGameMode(getGameMode())
      setSyncMessage('Cloud save updated.')
    } catch (reason) {
      setSyncMessage(reason instanceof Error ? reason.message : 'Unable to sync cloud save.')
    } finally {
      setSyncingCloud(false)
    }
  }

  if (error) {
    return (
      <main className="game-shell">
        <section className="message-panel">
          <p className="eyebrow">Database Error</p>
          <h1>Character Unavailable</h1>
          <p>{error}</p>
        </section>
      </main>
    )
  }

  if (!authChecked) {
    return (
      <main className="game-shell">
        <section className="message-panel">
          <p className="eyebrow">Opening Chronicle</p>
          <h1>Loading...</h1>
        </section>
      </main>
    )
  }

  if (!account || !profile) {
    return (
      <AuthScreen
        onAuthenticated={acceptSession}
        serverMessage={serverMessage}
      />
    )
  }

  if (screen === 'combat') {
    const dungeon = combatContentId < 0
      ? profile.dungeons.find((candidate) => candidate.contentType === roguelikeKind) ?? profile.dungeons[0]
      : profile.dungeons.find((candidate) => candidate.id === combatContentId) ?? profile.dungeons[0]
    const difficulty = dungeon.difficulties.find(
      (candidate) => candidate.id === selectedDifficultyId,
    ) ?? dungeon.difficulties[0]
    const roguelikePool = profile.dungeons
      .filter((candidate) => candidate.contentType === roguelikeKind)
      .flatMap((candidate) => candidate.encounters)
    const startPart = selectedPart
    return (
      <CombatScreen
        difficulty={difficulty}
        dungeon={dungeon}
        profile={profile}
        roguelikeMode={combatContentId < 0 ? roguelikeKind : undefined}
        roguelikeUpgradeTiming={combatContentId < 0 ? roguelikeUpgradeTiming : undefined}
        roguelikeAbilityLabelMode={combatContentId < 0 ? roguelikeAbilityLabelMode : undefined}
        roguelikeEncounterPool={combatContentId < 0 ? roguelikePool : undefined}
        startPart={startPart}
        onExit={() => {
          setScreen(combatContentId < 0 ? 'roguelike' : dungeon.contentType === 'raid' ? 'raids' : 'dungeons')
        }}
        onProfileUpdated={setProfile}
      />
    )
  }

  if (screen === 'pvp') {
    const pvpPool = profile.dungeons
      .filter((candidate) => candidate.contentType === pvpContentType)
      .flatMap((candidate) => candidate.encounters)
    return (
      <PvPRoguelikeScreen
        contentType={pvpContentType}
        encounterPool={pvpPool}
        gameMode={gameMode}
        onExit={() => {
          setCpuLeaderboard(loadCpuPvpLeaderboard(pvpContentType))
          setRoguelikeVariant('pvp')
          setScreen('roguelike')
        }}
        onProfileUpdated={setProfile}
        profile={profile}
      />
    )
  }

  const levelStart = profile.character.currentLevelExperience
  const levelEnd = profile.character.nextLevelExperience
  const experienceIntoLevel = profile.character.experience - levelStart
  const experienceForLevel = Math.max(1, levelEnd - levelStart)
  const experiencePercent = profile.character.level >= profile.maxLevel
    ? 100
    : Math.min(100, (experienceIntoLevel / experienceForLevel) * 100)
  const dungeonOptions = profile.dungeons.filter((candidate) => candidate.contentType === 'dungeon')
  const raidOptions = profile.dungeons.filter((candidate) => candidate.contentType === 'raid')
  const dungeon = dungeonOptions.find((candidate) => candidate.id === selectedDungeonId)
    ?? dungeonOptions[0]!
  const raid = raidOptions.find((candidate) => candidate.id === selectedRaidId)
    ?? raidOptions[0]
  const activityOptions = screen === 'raids' ? raidOptions : dungeonOptions
  const tierOptions = activityOptions
    .flatMap((option) => option.difficulties)
    .filter((difficulty, index, all) => (
      all.findIndex((candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel) === index
    ))
    .sort((a, b) => a.droppedItemLevel - b.droppedItemLevel)
  const savedDifficulty = profile.dungeons
    .flatMap((option) => option.difficulties)
    .find((candidate) => candidate.id === selectedDifficultyId)
  const selectedTier = tierOptions.find((candidate) => (
    candidate.droppedItemLevel === savedDifficulty?.droppedItemLevel
    && profile.character.level >= candidate.unlockLevel
  ))
    ?? tierOptions.slice().reverse().find((candidate) => profile.character.level >= candidate.unlockLevel)
    ?? tierOptions[0]
  const selectedTierItemLevel = selectedTier?.droppedItemLevel ?? 0
  const tierActivityOptions = activityOptions.filter((option) =>
    option.difficulties.some((difficulty) => difficulty.droppedItemLevel === selectedTierItemLevel),
  )
  const selectedActivityId = screen === 'raids' && raid ? raid.id : dungeon.id
  const activity = tierActivityOptions.find((candidate) => candidate.id === selectedActivityId)
    ?? tierActivityOptions[0]
    ?? (screen === 'raids' && raid ? raid : dungeon)
  const selectedDifficulty = activity.difficulties.find(
    (candidate) => candidate.droppedItemLevel === selectedTierItemLevel,
  ) ?? activity.difficulties[0]
  const difficultyLocked = profile.character.level < selectedDifficulty.unlockLevel
  const completedSections = activity.contentType === 'raid'
    ? profile.completedRaidPhases
    : profile.completedDungeonParts
  const sectionName = activity.contentType === 'raid' ? 'Phase' : 'Part'
  const parts = [
    { part: 1, name: `${sectionName} 1`, encounterCount: 3, unlocked: true },
    { part: 2, name: `${sectionName} 2`, encounterCount: 3, unlocked: completedSections >= 1 },
    { part: 3, name: `${sectionName} 3`, encounterCount: 3, unlocked: completedSections >= 2 },
  ]
  const cloudSync = getCloudSyncStatus()
  const canShowCloudSync = account.id !== -1 && cloudSync.available
  const lootPreviewEncounters = [...activity.encounters]
    .filter((encounter) => encounter.isBoss)
    .sort((a, b) => lootSort === 'boss'
      ? a.enemyName.localeCompare(b.enemyName) || a.sequence - b.sequence
      : a.sequence - b.sequence)

  return (
    <main className="game-shell">
      <header className="topbar app-header">
        <button
          className="brand-button"
          onClick={() => setScreen('menu')}
          type="button"
        >
          <strong>Menu</strong>
        </button>
        <div className="character-summary">
          <strong>{profile.character.name}</strong>
          <small>Level {profile.character.level}</small>
          <small>Item Level {profile.gearStats.averageItemLevel.toFixed(1)}</small>
          <div className="header-xp" title={`${profile.character.experience} total experience`}>
            <span style={{ width: `${experiencePercent}%` }} />
          </div>
          <button className="logout-button" onClick={signOut} type="button">
            {gameMode === 'offline' ? 'Leave Offline' : `Sign Out ${account.username}`}
          </button>
        </div>
      </header>

      {screen === 'menu' && (
        <section className="menu-screen">
          <div className="main-menu-grid">
            {canShowCloudSync && (
              <div className="menu-card cloud-sync-card">
                <span>{cloudSync.dirty ? 'S' : 'C'}</span>
                <div>
                  <strong>Cloud Save</strong>
                  <small>
                    {cloudSync.dirty
                      ? 'Local progress waiting. Upload when you want to refresh the server copy.'
                      : 'Server copy matches this device.'}
                  </small>
                  {syncMessage && <small className="cloud-sync-message">{syncMessage}</small>}
                </div>
                <button
                  className="text-button"
                  disabled={syncingCloud || !cloudSync.dirty}
                  onClick={syncSaveNow}
                  type="button"
                >
                  {syncingCloud ? 'Syncing...' : cloudSync.dirty ? 'Sync Save To Server' : 'Already Synced'}
                </button>
              </div>
            )}
            {MENU_ITEMS.map((item) => (
              <button
                className="menu-card"
                key={item.screen}
                onClick={() => {
                  if (item.screen === 'pvp') {
                    setRoguelikeVariant('pvp')
                    setScreen('roguelike')
                    return
                  }
                  setScreen(item.screen)
                }}
                type="button"
              >
                <span>{item.glyph}</span>
                <div>
                  <strong>{item.label}</strong>
                  <small>{item.description}</small>
                </div>
              </button>
            ))}
          </div>
        </section>
      )}

      {screen === 'roguelike' && (
        <section className="content-screen">
          <ScreenHeading
            eyebrow="Endless Draft"
            title="Roguelike"
            onBack={() => setScreen('menu')}
          />
          <div className="roguelike-variant-row">
            <button
              className={`text-button ${roguelikeVariant === 'pve' ? 'active' : ''}`}
              onClick={() => setRoguelikeVariant('pve')}
              type="button"
            >
              PvE
            </button>
            <button
              className={`text-button ${roguelikeVariant === 'pvp' ? 'active' : ''}`}
              onClick={() => setRoguelikeVariant('pvp')}
              type="button"
            >
              PvP
            </button>
          </div>
          {roguelikeVariant === 'pve' && (
            <>
          <div className="roguelike-option-panel">
            <div>
              <p className="eyebrow">Upgrade Timing</p>
              <h2>Buff Drafts</h2>
            </div>
            <div className="roguelike-timing-row">
              <button
                className={`text-button ${roguelikeUpgradeTiming === 'encounter' ? 'active' : ''}`}
                onClick={() => setRoguelikeUpgradeTiming('encounter')}
                type="button"
              >
                Every Encounter
              </button>
              <button
                className={`text-button ${roguelikeUpgradeTiming === 'boss' ? 'active' : ''}`}
                onClick={() => setRoguelikeUpgradeTiming('boss')}
                type="button"
              >
                Boss Only
              </button>
            </div>
          </div>
          <div className="roguelike-option-panel">
            <div>
              <p className="eyebrow">Upgrade Labels</p>
              <h2>Display Mode</h2>
            </div>
            <div className="roguelike-timing-row">
              <button
                className={`text-button ${roguelikeAbilityLabelMode === 'ability' ? 'active' : ''}`}
                onClick={() => setRoguelikeAbilityLabelMode('ability')}
                type="button"
              >
                Ability Names
              </button>
              <button
                className={`text-button ${roguelikeAbilityLabelMode === 'slot' ? 'active' : ''}`}
                onClick={() => setRoguelikeAbilityLabelMode('slot')}
                type="button"
              >
                Slot Names
              </button>
            </div>
          </div>
          <div className="roguelike-mode-grid">
            <button
              className="menu-card"
              onClick={() => {
                const baseDungeon = dungeonOptions[0]
                setRoguelikeKind('dungeon')
                setCombatContentId(-1)
                setSelectedDifficultyId(baseDungeon.difficulties[0]?.id ?? 1)
                setSelectedPart(1)
                setScreen('combat')
              }}
              type="button"
            >
              <span>D</span>
              <strong>Dungeon Roguelike</strong>
              <small>Five-player party. Two random trash enemies and a boss with a lighter early ramp.</small>
            </button>
            <button
              className="menu-card"
              onClick={() => {
                const baseRaid = raidOptions[0]
                setRoguelikeKind('raid')
                setCombatContentId(-2)
                setSelectedDifficultyId(baseRaid?.difficulties[0]?.id ?? 101)
                setSelectedPart(1)
                setScreen('combat')
              }}
              type="button"
            >
              <span>R</span>
              <strong>Raid Roguelike</strong>
              <small>Ten-player party. Raid pools, lighter early scaling, and the same upgrade draft.</small>
            </button>
          </div>
            </>
          )}
          {roguelikeVariant === '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 ${pvpContentType === 'dungeon' ? 'active' : ''}`}
                    onClick={() => setPvpContentType('dungeon')}
                    type="button"
                  >
                    Dungeon
                  </button>
                  <button
                    className={`text-button ${pvpContentType === 'raid' ? 'active' : ''}`}
                    onClick={() => setPvpContentType('raid')}
                    type="button"
                  >
                    Raid
                  </button>
                </div>
              </div>
              <div className="menu-card pvp-queue-panel">
                <span>{gameMode === 'offline' ? 'C' : 'Q'}</span>
                <div>
                  <strong>{gameMode === 'offline' ? 'Offline CPU Match' : 'Queue Then CPU Fallback'}</strong>
                  <small>
                    {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.'}
                  </small>
                </div>
                <button
                  className="text-button"
                  onClick={() => setScreen('pvp')}
                  type="button"
                >
                  Start Match
                </button>
              </div>
              {SHOW_LEADERBOARDS && (
              <div className="leaderboard-section">
                <div className="equipment-heading toggle-heading">
                  <div>
                    <p className="eyebrow">CPU Leaderboard</p>
                    <h2>{pvpContentType === 'raid' ? 'Raid Clash' : 'Dungeon Clash'}</h2>
                  </div>
                </div>
                <div className="leaderboard-table">
                  <div className="leaderboard-header pvp-leaderboard-row">
                    <strong>Rank</strong>
                    <strong>Player</strong>
                    <strong>CPU</strong>
                    <strong>Clears</strong>
                    <strong>Result</strong>
                  </div>
                  {cpuLeaderboard.map((entry, index) => (
                    <div className="leaderboard-row pvp-leaderboard-row" key={`${entry.characterName}-${entry.completedAt}`}>
                      <strong>#{index + 1}</strong>
                      <span>{entry.characterName}</span>
                      <span>CPU {entry.cpuDifficulty}</span>
                      <span>{entry.encountersCleared}</span>
                      <span>{entry.result}</span>
                    </div>
                  ))}
                  {cpuLeaderboard.length === 0 && (
                    <div className="leaderboard-empty">
                      No CPU runs recorded yet for this mode.
                    </div>
                  )}
                </div>
              </div>
              )}
            </>
          )}
        </section>
      )}

      {(screen === 'dungeons' || screen === 'raids') && (
        <section className="content-screen">
          <ScreenHeading
            eyebrow="Adventure"
            title={activity.contentType === 'raid' ? 'Raids' : 'Dungeons'}
            onBack={() => setScreen('menu')}
          />
          <section className="run-setup-panel">
            <div className="run-setup-heading">
              <div>
                <p className="eyebrow">Step 1</p>
                <h2>Item Level</h2>
              </div>
              <small>{screen === 'raids' ? 'Raid' : 'Dungeon'} tiers unlock by character level.</small>
            </div>
            <div className="tier-grid">
              {tierOptions.map((difficulty) => {
                const locked = profile.character.level < difficulty.unlockLevel
                const selected = difficulty.droppedItemLevel === selectedDifficulty.droppedItemLevel
                return (
                  <button
                    className={`${selected ? 'selected' : ''} ${locked ? 'locked' : ''}`}
                    disabled={locked}
                    key={difficulty.id}
                    onClick={() => {
                      const nextActivity = activity.difficulties.some(
                        (candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel,
                      )
                        ? activity
                        : activityOptions.find((option) =>
                          option.difficulties.some((candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel),
                        )
                      if (nextActivity) {
                        if (screen === 'raids') setSelectedRaidId(nextActivity.id)
                        else setSelectedDungeonId(nextActivity.id)
                        const nextDifficulty = nextActivity.difficulties.find(
                          (candidate) => candidate.droppedItemLevel === difficulty.droppedItemLevel,
                        )
                        if (nextDifficulty) setSelectedDifficultyId(nextDifficulty.id)
                      }
                    }}
                    type="button"
                  >
                    <strong>iLvl {difficulty.droppedItemLevel}</strong>
                    <span>{locked ? `Level ${difficulty.unlockLevel}` : difficulty.name}</span>
                  </button>
                )
              })}
            </div>
          </section>

          <section className="run-setup-panel">
            <div className="run-setup-heading">
              <div>
                <p className="eyebrow">Step 2</p>
                <h2>{screen === 'raids' ? 'Pick Raid' : 'Pick Dungeon'}</h2>
              </div>
              <small>{selectedDifficulty.name} rewards iLvl {selectedDifficulty.droppedItemLevel} components.</small>
            </div>
            <div className="activity-card-grid">
              {tierActivityOptions.map((candidate) => {
                const difficulty = candidate.difficulties.find(
                  (option) => option.droppedItemLevel === selectedDifficulty.droppedItemLevel,
                ) ?? candidate.difficulties[0]
                const locked = profile.character.level < difficulty.unlockLevel
                const selected = candidate.id === activity.id
                return (
                  <button
                    className={`activity-card ${selected ? 'selected' : ''} ${locked ? 'locked' : ''}`}
                    disabled={locked}
                    key={candidate.id}
                    onClick={() => {
                      if (screen === 'raids') setSelectedRaidId(candidate.id)
                      else setSelectedDungeonId(candidate.id)
                      setSelectedDifficultyId(difficulty.id)
                    }}
                    type="button"
                  >
                    <span className={`dungeon-art ${candidate.contentType === 'raid' ? 'raid-art' : ''}`}>
                      {activityInitials(candidate.name)}
                    </span>
                    <strong>{candidate.name}</strong>
                    <small>{candidate.locationName}</small>
                    <i>
                      Level {candidate.recommendedLevel} | {candidate.partySize} Players
                    </i>
                  </button>
                )
              })}
            </div>
          </section>

          <article className="run-summary-card">
            <div className={`dungeon-art ${activity.contentType === 'raid' ? 'raid-art' : ''}`}>
              {activityInitials(activity.name)}
            </div>
            <div className="run-summary-copy">
              <p className="eyebrow">Step 3</p>
              <h2>{activity.name}</h2>
              <p>{activity.description}</p>
              <div className="tag-row">
                <span>Level {activity.recommendedLevel}</span>
                <span>{activity.partySize} Players</span>
                <span>{selectedDifficulty.name}</span>
                <span>iLvl {selectedDifficulty.droppedItemLevel}</span>
                <span>{Math.round(activity.experienceReward * selectedDifficulty.experienceMultiplier)} XP</span>
              </div>
            </div>
            <div className="part-picker">
              {parts.map((p) => (
                <button
                  key={p.part}
                  className={`primary-button ${selectedPart === p.part ? 'selected-part' : ''} ${!p.unlocked ? 'locked' : ''}`}
                  disabled={difficultyLocked || !p.unlocked}
                  onClick={() => {
                    setSelectedPart(p.part)
                    setCombatContentId(activity.id)
                    setSelectedDifficultyId(selectedDifficulty.id)
                    setScreen('combat')
                  }}
                  type="button"
                >
                  {p.name}
                </button>
              ))}
            </div>
          </article>
          <div className="difficulty-section compact-difficulty-section">
            <div className={`difficulty-summary ${difficultyLocked ? 'locked' : ''}`}>
              <div>
                <strong>{selectedDifficulty.name}</strong>
                <small>{difficultyLocked ? `Unlocks at level ${selectedDifficulty.unlockLevel}` : selectedDifficulty.description}</small>
              </div>
              <dl>
                <div><dt>Health</dt><dd>{selectedDifficulty.healthMultiplier.toFixed(2)}x</dd></div>
                <div><dt>Damage</dt><dd>{selectedDifficulty.damageMultiplier.toFixed(2)}x</dd></div>
                <div><dt>XP</dt><dd>{selectedDifficulty.experienceMultiplier.toFixed(1)}x</dd></div>
                <div><dt>Components</dt><dd>iLvl {selectedDifficulty.droppedItemLevel}</dd></div>
              </dl>
            </div>
          </div>
          <div className="loot-preview-section">
            <div className="equipment-heading toggle-heading">
              <div>
                <p className="eyebrow">Encounter Rewards</p>
                <h2>{selectedDifficulty.name} Loot Tables</h2>
              </div>
              <button
                className="text-button"
                onClick={() => setShowLoot((current) => !current)}
                type="button"
              >
                {showLoot ? 'Hide Loot' : 'View Loot'}
              </button>
            </div>
            {showLoot && (
              <>
                <div className="loot-toolbar">
                  <label>
                    <span>Sort</span>
                    <select value={lootSort} onChange={(event) => setLootSort(event.target.value as 'sequence' | 'boss')}>
                      <option value="sequence">Encounter order</option>
                      <option value="boss">Boss name</option>
                    </select>
                  </label>
                </div>
                <p className="section-note">
                  Bosses drop 1-3 boss coins from one loot roll
                  {activity.completionItemLevel
                    ? ` - full clear guarantees iLvl ${activity.completionItemLevel}`
                    : ''}
                </p>
                <div className="loot-preview-grid">
                  {lootPreviewEncounters.map((encounter) => {
                    const loot = encounter.lootTables.filter(
                      (entry) => entry.difficultyId === selectedDifficulty.id,
                    )
                    return (
                      <article key={encounter.id}>
                        <div className="loot-encounter-title">
                          {encounter.isBoss ? (
                            <img className="loot-boss-icon" src={encounter.imageUrl} alt={`${encounter.enemyName} icon`} />
                          ) : (
                            <span>{encounter.sequence}</span>
                          )}
                          <div>
                            <strong>{encounter.enemyName}</strong>
                            <small>{loot.length > 0 ? '1 loot roll, 1-3 coins' : 'No coin table'}</small>
                          </div>
                        </div>
                        <div className="loot-items">
                          {loot.map((item) => (
                            <div className={`rarity-${item.rarity}`} key={item.id}>
                              <span>{item.glyph}</span>
                              <div>
                                <strong>{item.name}</strong>
                                <small>{item.slot} - iLvl {item.itemLevel}</small>
                              </div>
                              <i>{item.dropWeight}% weight</i>
                            </div>
                          ))}
                        </div>
                      </article>
                    )
                  })}
                </div>
              </>
            )}
          </div>
          {SHOW_LEADERBOARDS && (
          <div className="leaderboard-section">
            <div className="equipment-heading toggle-heading">
              <div>
                <p className="eyebrow">Efficiency Rankings</p>
                <h2>{selectedDifficulty.name} Leaderboard</h2>
              </div>
              <button
                className="text-button"
                onClick={() => setShowLeaderboard((current) => !current)}
                type="button"
              >
                {showLeaderboard ? 'Hide Leaderboard' : 'View Leaderboard'}
              </button>
            </div>
            {showLeaderboard && (
              <>
                <p className="section-note">
                  {gameMode === 'offline'
                    ? 'Offline runs are not submitted'
                    : canShowCloudSync
                      ? 'Manual save sync updates your cloud profile.'
                      : 'Lowest resource spent ranks first'}
                </p>
                <div className="leaderboard-tabs">
                  {([
                    { key: 'part_1', label: `${sectionName} 1` },
                    { key: 'part_2', label: `${sectionName} 2` },
                    { key: 'part_3', label: `${sectionName} 3` },
                    { key: 'full_run', label: 'Full Run' },
                  ] as const).map((tab) => (
                    <button
                      key={tab.key}
                      className={`leaderboard-tab ${leaderboardCategory === tab.key ? 'active' : ''}`}
                      onClick={() => setLeaderboardCategory(tab.key)}
                      type="button"
                    >
                      {tab.label}
                    </button>
                  ))}
                </div>
                <div className="leaderboard-table">
                  <div className="leaderboard-header">
                    <span>Rank</span>
                    <span>Healer</span>
                    <span>Class</span>
                    <span>Level</span>
                    <span>Item Level</span>
                    <span>Resource</span>
                    <span>Time</span>
                  </div>
                  {activity.leaderboards[leaderboardCategory]
                    .filter((entry) => entry.difficultyId === selectedDifficulty.id)
                    .map((entry) => (
                      <div className="leaderboard-row" key={`${entry.rank}-${entry.completedAt}`}>
                        <strong>#{entry.rank}</strong>
                        <span>{entry.characterName}</span>
                        <span>{entry.className}</span>
                        <span>{entry.characterLevel}</span>
                        <span>{entry.averageItemLevel.toFixed(1)}</span>
                        <strong>{entry.resourceSpent}</strong>
                        <span>{entry.durationSeconds}s</span>
                      </div>
                    ))}
                  {activity.leaderboards[leaderboardCategory].filter(
                    (entry) => entry.difficultyId === selectedDifficulty.id,
                  ).length === 0 && (
                    <div className="leaderboard-empty">
                      {gameMode === 'offline'
                        ? 'Connect with an online character to compete in rankings.'
                        : canShowCloudSync
                          ? 'No leaderboard entries yet.'
                          : 'Complete this difficulty to claim the first ranking.'}
                    </div>
                  )}
                </div>
              </>
            )}
          </div>
          )}
        </section>
      )}

      {screen === 'customize' && (
        <CustomizeScreen
          profile={profile}
          onBack={() => setScreen('menu')}
          onSaved={setProfile}
        />
      )}

      {screen === 'talents' && (
        <TalentScreen
          profile={profile}
          onBack={() => setScreen('menu')}
          onUpdated={setProfile}
        />
      )}

      {screen === 'equipment' && (
        <EquipmentScreen
          profile={profile}
          onBack={() => setScreen('menu')}
          onUpdated={setProfile}
        />
      )}

      {screen === 'settings' && (
        <SettingsScreen onBack={() => setScreen('menu')} />
      )}

    </main>
  )
}

function ScreenHeading({
  eyebrow,
  title,
  onBack,
}: {
  eyebrow: string
  title: string
  onBack: () => void
}) {
  return (
    <div className="screen-heading">
      <div>
        <p className="eyebrow">{eyebrow}</p>
        <h1>{title}</h1>
      </div>
      <button className="back-button" onClick={onBack} type="button">Back</button>
    </div>
  )
}

export default App
