import { useEffect, useMemo, useRef, useState } from 'react'
import { useGameAction } from '../../input'
import { getGameMode } from '../../gameRepository'
import { startPvpQueueWithCpuFallback } from '../../pvpQueueLifecycle'
import { summarizeChoiceStacks } from '../../combat/roguelikeUpgrades'
import {
  useDualScreen,
  useDualScreenSetupPublisher,
  useDualScreenWorkshopPublisher,
  type DualScreenSetupState,
  type DualScreenWorkshopState,
} from '../../dualScreen'
import { BossArenaScreen } from './screens/BossArenaScreen'
import {
  Iwt2CloudSaveScreen,
  Iwt2CustomizeCharacterScreen,
  Iwt2DungeonsScreen,
  Iwt2HunterProfileScreen,
  Iwt2ModeScreen,
  Iwt2RoguelikeScreen,
  Iwt2RoguelikeUpgradeScreen,
  Iwt2SettingsScreen,
} from './screens/Iwt2ShellScreens'
import {
  loadIwt2Save,
  writeIwt2Save,
  type Iwt2Save,
} from './save/iwt2Repository'
import { IWT2_BOSS_METADATA, type Iwt2BossId } from './content/bosses'
import { abilitiesForHealer, IWT2_HEALER_METADATA } from './content/healerAbilities'
import {
  buildIwt2OpponentDebuffChoices,
  buildIwt2SelfBuffChoices,
  IWT2_REVIVE_PARTY_CHOICE,
  type Iwt2RoguelikeChoice,
  type Iwt2RoguelikeContentType,
  type Iwt2RoguelikeOpponentDebuffId,
  type Iwt2RoguelikeSelfBuffId,
  type Iwt2RoguelikeVariant,
} from './content/roguelike'

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

const IWT2_MENU_COLUMNS = 4
const IWT2_ROGUELIKE_CHOICE_COUNT = 3

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

const MENU_ITEMS: Array<{
  screen: Iwt2Screen
  title: string
  description: string
  glyph: string
}> = [
  {
    screen: 'cloud-save',
    title: 'Backup Slot',
    description: 'Save or restore the isolated IWT2 progress slot.',
    glyph: 'C',
  },
  {
    screen: 'dungeons',
    title: 'Dungeons',
    description: 'Queue into Bulldrome, Yian Kut Ku, Great Jaggi, and Khezu boss arenas.',
    glyph: 'D',
  },
  {
    screen: 'raids',
    title: 'Raids',
    description: 'Open raid assignments built from active IWT2 boss mechanics.',
    glyph: 'R',
  },
  {
    screen: 'roguelike',
    title: 'Roguelike',
    description: 'Draft upgrades through escalating random encounters.',
    glyph: 'L',
  },
  {
    screen: 'roguelike',
    title: 'PvP',
    description: 'Race another healer through roguelike encounters with buffs and sabotage.',
    glyph: 'P',
  },
  {
    screen: 'hunter-profile',
    title: 'Hunter Profile',
    description: 'IWT2 level, inventory summary, and collection log.',
    glyph: 'H',
  },
  {
    screen: 'customize-character',
    title: 'Customize Character',
    description: 'Choose healer kit, armor palette, and IWT2 hunter callsign.',
    glyph: 'K',
  },
  {
    screen: 'settings',
    title: 'Settings',
    description: 'IWT2 targeting preference and controller icon style.',
    glyph: 'S',
  },
]

export function IWantToHeal2App({ onBackToGameSelect }: { onBackToGameSelect: () => void }) {
  const { enabled: dualScreenEnabled } = useDualScreen()
  const [screen, setScreen] = useState<Iwt2Screen>('menu')
  const [save, setSave] = useState<Iwt2Save>(loadIwt2Save)
  const [selectedIndex, setSelectedIndex] = useState(0)
  const [selectedBossId, setSelectedBossId] = useState<Iwt2BossId>('bulldrome')
  const [arenaModeLabel, setArenaModeLabel] = useState('Dungeon')
  const [roguelikeVariant, setRoguelikeVariant] = useState<Iwt2RoguelikeVariant>('pve')
  const [roguelikeContentType, setRoguelikeContentType] = useState<Iwt2RoguelikeContentType>('dungeon')
  const [roguelikeRun, setRoguelikeRun] = useState<Iwt2RoguelikeRunState | null>(null)
  const [pvpQueueMessage, setPvpQueueMessage] = useState('')
  const cancelPvpQueueRef = useRef<(() => void) | null>(null)

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

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

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

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

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

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

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

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

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

  if (screen === 'dungeons') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
        <Iwt2DungeonsScreen
          onBack={() => setScreen('menu')}
          onOpenBoss={(bossId) => {
            setArenaModeLabel('Dungeon')
            setSelectedBossId(bossId)
            setScreen('arena')
          }}
        />
      </main>
    )
  }

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

  if (screen === 'raids') {
    return (
      <main className="game-shell iwt2-shell">
        <Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
        <Iwt2ModeScreen
          mode="Raids"
          onBack={() => setScreen('menu')}
          onOpenBoss={(bossId) => {
            setArenaModeLabel('Raid')
            setSelectedBossId(bossId)
            setScreen('arena')
          }}
        />
      </main>
    )
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

function createRandomRoguelikeBossPair(): Iwt2BossId[] {
  const pool = Object.keys(IWT2_BOSS_METADATA) as Iwt2BossId[]
  return chooseRunChoices(pool, 2)
}

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

function Iwt2Header({
  onBackToGameSelect,
  save,
}: {
  onBackToGameSelect: () => void
  save: Iwt2Save
}) {
  return (
    <header className="topbar app-header">
      <button className="brand-button" onClick={onBackToGameSelect} type="button">
        <strong>Games</strong>
      </button>
      <div className="character-summary">
        <strong>{save.character.name}</strong>
        <small>IWT2 Level {save.character.level}</small>
        <small>{save.character.experience} XP</small>
      </div>
    </header>
  )
}

function buildIwt2SetupDualScreenState(
  screen: Iwt2Screen,
  selectedBossId: Iwt2BossId,
  save: Iwt2Save,
): DualScreenSetupState | null {
  if (screen !== 'dungeons' && screen !== 'raids') return null
  const boss = IWT2_BOSS_METADATA[selectedBossId]
  const raid = screen === 'raids'
  return {
    contentType: raid ? 'raid' : 'dungeon',
    description: raid
      ? `${boss.name} raid assignment. Tank holds aggro while party moves around modular boss mechanics.`
      : `${boss.name} arena. Heal the party through melee pressure, telegraphs, hazards, and stun recovery.`,
    difficultyName: `IWT2 Level ${save.character.level}`,
    experience: 125,
    initials: boss.icon,
    itemLevel: save.character.level,
    stats: {
      damage: `${boss.meleeDamage}`,
      health: `${boss.maxHealth}`,
      loot: 'IWT2',
      xp: '125',
    },
    subtitle: `${raid ? 'Raid' : 'Dungeon'} | 6 Players | ${IWT2_HEALER_METADATA[save.character.healerStyle].name}`,
    title: raid ? `${boss.name} Raid` : `${boss.name} Arena`,
  }
}

function buildIwt2WorkshopDualScreenState(
  screen: Iwt2Screen,
  save: Iwt2Save,
): DualScreenWorkshopState | null {
  if (screen === 'hunter-profile') {
    return {
      items: [
        {
          glyph: 'H',
          meta: `${save.character.experience} XP`,
          status: `Level ${save.character.level}`,
          title: save.character.name,
        },
        {
          glyph: IWT2_HEALER_METADATA[save.character.healerStyle].icon,
          meta: IWT2_HEALER_METADATA[save.character.healerStyle].description,
          status: 'Class',
          title: IWT2_HEALER_METADATA[save.character.healerStyle].name,
        },
        ...(Object.keys(IWT2_BOSS_METADATA) as Iwt2BossId[]).map((bossId) => ({
          glyph: IWT2_BOSS_METADATA[bossId].icon,
          meta: `${save.collectionLog.bossKills[bossId] ?? 0} kills`,
          status: `${save.collectionLog.dropsFound[bossDropIdForAppSummary(bossId)] ?? 0} drops`,
          title: IWT2_BOSS_METADATA[bossId].name,
        })),
      ],
      mode: 'collection',
      subtitle: 'IWT2 inventory and collection log',
      summary: `${save.inventory.length} inventory slots`,
      title: 'Hunter Profile',
    }
  }

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

  return null
}

function bossDropIdForAppSummary(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'
}
