import { useEffect, useMemo, useRef, useState, type CSSProperties } from 'react'
import {
  saveProfile,
  type CharacterProfile,
  type GameClass,
} from '../profile'
import { useDualScreen, useDualScreenWorkshopPublisher, type DualScreenWorkshopState } from '../dualScreen'
import { useGameAction, type InputAction } from '../input'
import { EquipmentScreen } from './EquipmentScreen'
import { TalentScreen } from './TalentScreen'

type Props = {
  profile: CharacterProfile
  onBack: () => void
  onSaved: (profile: CharacterProfile) => void
}

type CustomizeTab = 'equipment' | 'crafting' | 'talents' | 'class'

type CustomizeNavEntry =
  | { kind: 'back'; key: string; row: number; column: number }
  | { kind: 'tab'; key: string; row: number; column: number; tab: CustomizeTab }
  | { kind: 'class'; key: string; row: number; column: number; classId: number }
  | { kind: 'slot'; key: string; row: number; column: number; slotIndex: number }
  | { kind: 'clear'; key: string; row: number; column: number }
  | { kind: 'abilityPagePrev'; key: string; row: number; column: number }
  | { kind: 'abilityPageNext'; key: string; row: number; column: number }
  | { kind: 'ability'; key: string; row: number; column: number; abilityId: number }
  | { kind: 'save'; key: string; row: number; column: number }

const CUSTOMIZE_TABS: Array<{ key: CustomizeTab; label: string }> = [
  { key: 'equipment', label: 'Equipment' },
  { key: 'crafting', label: 'Crafting' },
  { key: 'talents', label: 'Talents' },
  { key: 'class', label: 'Class' },
]

const CLASS_PICKER_COLUMN = 0
const CLASS_CONTENT_COLUMN = 2
const ABILITY_LIBRARY_COLUMNS = 5
const ABILITY_LIBRARY_PAGE_SIZE = 10

export function CustomizeScreen({ profile, onBack, onSaved }: Props) {
  const [activeTab, setActiveTab] = useState<CustomizeTab>('class')
  const { enabled: dualScreenEnabled } = useDualScreen()
  const [classId, setClassId] = useState(profile.character.classId)
  const [slots, setSlots] = useState<Array<number | null>>(profile.abilitySlots)
  const [selectedSlot, setSelectedSlot] = useState(0)
  const [selectedNavKey, setSelectedNavKey] = useState('tab:class')
  const [abilityPage, setAbilityPage] = useState(0)
  const [message, setMessage] = useState('')
  const [saving, setSaving] = useState(false)
  const scrollRef = useRef<number>(0)
  const navRefs = useRef<Record<string, HTMLElement | null>>({})
  const gameClass = profile.classes.find((candidate) => candidate.id === classId)!
  const abilityMap = useMemo(
    () => new Map(gameClass.spells.map((ability) => [ability.id, ability])),
    [gameClass],
  )
  const abilityPageCount = Math.max(1, Math.ceil(gameClass.spells.length / ABILITY_LIBRARY_PAGE_SIZE))
  const currentAbilityPage = Math.min(abilityPage, abilityPageCount - 1)
  const visibleAbilities = useMemo(
    () => gameClass.spells.slice(
      currentAbilityPage * ABILITY_LIBRARY_PAGE_SIZE,
      currentAbilityPage * ABILITY_LIBRARY_PAGE_SIZE + ABILITY_LIBRARY_PAGE_SIZE,
    ),
    [currentAbilityPage, gameClass.spells],
  )
  const classNavActive = activeTab === 'class'
  const navEntries = useMemo<CustomizeNavEntry[]>(() => {
    const entries: CustomizeNavEntry[] = [
      { kind: 'back', key: 'back', row: 0, column: 0 },
      ...CUSTOMIZE_TABS.map((tab, index) => ({
        kind: 'tab' as const,
        key: `tab:${tab.key}`,
        row: 0,
        column: index + 1,
        tab: tab.key,
      })),
    ]
    if (activeTab !== 'class') return entries

    entries.push(
      ...profile.classes.map((candidate, index) => ({
        kind: 'class' as const,
        key: `class:${candidate.id}`,
        row: index + 1,
        column: CLASS_PICKER_COLUMN,
        classId: candidate.id,
      })),
      ...slots.map((_, index) => ({
        kind: 'slot' as const,
        key: `slot:${index}`,
        row: 1,
        column: CLASS_CONTENT_COLUMN + index,
        slotIndex: index,
      })),
      { kind: 'clear', key: 'clear-slot', row: 2, column: CLASS_CONTENT_COLUMN + 4 },
      ...(abilityPageCount > 1 && currentAbilityPage > 0
        ? [{ kind: 'abilityPagePrev' as const, key: 'ability-page-prev', row: 2, column: CLASS_CONTENT_COLUMN + 2 }]
        : []),
      ...(abilityPageCount > 1 && currentAbilityPage < abilityPageCount - 1
        ? [{ kind: 'abilityPageNext' as const, key: 'ability-page-next', row: 2, column: CLASS_CONTENT_COLUMN + 3 }]
        : []),
      ...visibleAbilities
        .filter((ability) => ability.unlockLevel <= profile.character.level)
        .map((ability, index) => ({
          kind: 'ability' as const,
          key: `ability:${ability.id}`,
          row: Math.floor(index / ABILITY_LIBRARY_COLUMNS) + 3,
          column: CLASS_CONTENT_COLUMN + (index % ABILITY_LIBRARY_COLUMNS),
          abilityId: ability.id,
        })),
    )
    if (!saving) {
      entries.push({ kind: 'save', key: 'save', row: 99, column: CLASS_CONTENT_COLUMN + 4 })
    }
    return entries
  }, [abilityPageCount, activeTab, currentAbilityPage, profile.character.level, profile.classes, saving, slots, visibleAbilities])
  const activeEntry = navEntries.find((entry) => entry.key === selectedNavKey)
    ?? navEntries.find((entry) => entry.key === `tab:${activeTab}`)
    ?? navEntries[0]

  function selected(entryKey: string) {
    return classNavActive && activeEntry?.key === entryKey
  }

  function navRef(entryKey: string) {
    return (node: HTMLElement | null) => {
      navRefs.current[entryKey] = node
    }
  }

  useEffect(() => {
    window.scrollTo(0, scrollRef.current)
  }, [profile])

  function saveScroll() {
    scrollRef.current = window.scrollY
  }

  function chooseClass(nextClass: GameClass) {
    const starterAbilities = nextClass.spells
      .filter((ability) => ability.unlockLevel <= profile.character.level)
      .slice(0, 6)
      .map((ability) => ability.id)
    setClassId(nextClass.id)
    setSlots([...starterAbilities, ...Array(6 - starterAbilities.length).fill(null)])
    setSelectedSlot(0)
    setAbilityPage(0)
    setMessage('')
  }

  function selectTab(tab: CustomizeTab) {
    setActiveTab(tab)
    setSelectedNavKey(`tab:${tab}`)
  }

  function equipAbility(abilityId: number) {
    if (slots.includes(abilityId)) {
      setMessage('That ability is already equipped.')
      return
    }
    setSlots((current) =>
      current.map((spellId, index) => index === selectedSlot ? abilityId : spellId),
    )
    setMessage('')
  }

  function clearSlot() {
    setSlots((current) =>
      current.map((spellId, index) => index === selectedSlot ? null : spellId),
    )
  }

  function moveSelection(action: InputAction) {
    if (!action.startsWith('navigate') || navEntries.length === 0 || !activeEntry) return
    const activeIndex = Math.max(0, navEntries.findIndex((entry) => entry.key === activeEntry.key))
    const candidates = navEntries
      .map((entry, index) => ({ entry, index }))
      .filter(({ index }) => index !== activeIndex)
      .filter(({ entry }) => {
        if (action === 'navigateLeft') return entry.column < activeEntry.column
        if (action === 'navigateRight') return entry.column > activeEntry.column
        if (action === 'navigateUp') return entry.row < activeEntry.row
        return entry.row > activeEntry.row
      })
    if (candidates.length === 0) return
    candidates.sort((a, b) => {
      const aPrimary = Math.abs(a.entry.row - activeEntry.row) + Math.abs(a.entry.column - activeEntry.column)
      const bPrimary = Math.abs(b.entry.row - activeEntry.row) + Math.abs(b.entry.column - activeEntry.column)
      const aSecondary = action === 'navigateLeft' || action === 'navigateRight'
        ? Math.abs(a.entry.row - activeEntry.row)
        : Math.abs(a.entry.column - activeEntry.column)
      const bSecondary = action === 'navigateLeft' || action === 'navigateRight'
        ? Math.abs(b.entry.row - activeEntry.row)
        : Math.abs(b.entry.column - activeEntry.column)
      return aPrimary - bPrimary || aSecondary - bSecondary || a.index - b.index
    })
    setSelectedNavKey(candidates[0]?.entry.key ?? activeEntry.key)
  }

  function openEntry(entry: CustomizeNavEntry | undefined) {
    if (!entry) return
    if (entry.kind === 'back') onBack()
    else if (entry.kind === 'tab') selectTab(entry.tab)
    else if (entry.kind === 'class') {
      const nextClass = profile.classes.find((candidate) => candidate.id === entry.classId)
      if (nextClass) chooseClass(nextClass)
    } else if (entry.kind === 'slot') setSelectedSlot(entry.slotIndex)
    else if (entry.kind === 'clear') clearSlot()
    else if (entry.kind === 'abilityPagePrev') setAbilityPage((page) => Math.max(0, page - 1))
    else if (entry.kind === 'abilityPageNext') setAbilityPage((page) => Math.min(abilityPageCount - 1, page + 1))
    else if (entry.kind === 'ability') equipAbility(entry.abilityId)
    else if (entry.kind === 'save') void persistChanges()
  }

  useEffect(() => {
    if (!classNavActive) return
    navRefs.current[activeEntry?.key ?? '']?.scrollIntoView({ block: 'nearest', inline: 'nearest' })
  }, [activeEntry, classNavActive])

  useGameAction((action, device) => {
    if (device !== 'controller' || !classNavActive) return
    if (action === 'back') {
      onBack()
      return
    }
    if (action === 'confirm') {
      openEntry(activeEntry)
      return
    }
    moveSelection(action)
  })

  const classWorkshopState = useMemo<DualScreenWorkshopState | null>(() => {
    if (activeTab !== 'class') return null
    return {
      mode: 'class',
      title: 'Ability Library',
      subtitle: gameClass.name,
      summary: `Selected slot ${selectedSlot + 1}. ${message || 'Choose an ability for the active loadout.'}`,
      items: gameClass.spells.map((ability) => {
        const locked = ability.unlockLevel > profile.character.level
        const equipped = slots.includes(ability.id)
        return {
          glyph: locked ? 'L' : ability.glyph,
          title: ability.name,
          meta: locked ? `Level ${ability.unlockLevel}` : `${ability.cost} ${gameClass.resourceName}`,
          detail: ability.description,
          status: equipped ? 'Equipped' : locked ? 'Locked' : '',
        }
      }),
    }
  }, [activeTab, gameClass, message, profile.character.level, selectedSlot, slots])

  useDualScreenWorkshopPublisher(classWorkshopState, dualScreenEnabled)

  async function persistChanges() {
    saveScroll()
    setSaving(true)
    setMessage('')
    try {
      const updated = await saveProfile(classId, slots)
      onSaved(updated)
      setMessage('Character saved.')
    } catch (reason) {
      setMessage(reason instanceof Error ? reason.message : 'Unable to save character.')
    } finally {
      setSaving(false)
    }
  }

  return (
    <section className="content-screen customize-screen" data-game-nav-active={classNavActive ? 'true' : undefined}>
      <div className="screen-heading customize-heading">
        <div>
          <p className="eyebrow">Character Workshop</p>
          <h1>Customize Character</h1>
        </div>
        <button
          className={`back-button ${selected('back') ? 'game-selected' : ''}`}
          data-controller-nav={classNavActive ? 'skip' : undefined}
          data-game-selected={selected('back') ? 'true' : undefined}
          onClick={onBack}
          onPointerDown={() => setSelectedNavKey('back')}
          ref={navRef('back')}
          type="button"
        >
          Back
        </button>
      </div>

      <div className="customize-tabs" role="tablist" aria-label="Customize character sections">
        <button
          className={`back-button customize-tab-back ${selected('back') ? 'game-selected' : ''}`}
          data-controller-nav={classNavActive ? 'skip' : undefined}
          data-game-selected={selected('back') ? 'true' : undefined}
          onClick={onBack}
          onPointerDown={() => setSelectedNavKey('back')}
          type="button"
        >
          Back
        </button>
        {CUSTOMIZE_TABS.map((tab) => (
          <button
            aria-selected={activeTab === tab.key}
            className={`${activeTab === tab.key ? 'active' : ''} ${selected(`tab:${tab.key}`) ? 'game-selected' : ''}`}
            data-controller-nav={classNavActive ? 'skip' : undefined}
            data-game-selected={selected(`tab:${tab.key}`) ? 'true' : undefined}
            key={tab.key}
            onClick={() => selectTab(tab.key)}
            onPointerDown={() => setSelectedNavKey(`tab:${tab.key}`)}
            ref={navRef(`tab:${tab.key}`)}
            role="tab"
            type="button"
          >
            {tab.label}
          </button>
        ))}
      </div>

      {activeTab === 'equipment' && (
        <EquipmentScreen
          embedded
          mode="equipment"
          showModeTabs={false}
          profile={profile}
          onUpdated={onSaved}
        />
      )}

      {activeTab === 'crafting' && (
        <EquipmentScreen
          embedded
          mode="crafting"
          showModeTabs={false}
          profile={profile}
          onUpdated={onSaved}
        />
      )}

      {activeTab === 'talents' && (
        <TalentScreen
          embedded
          profile={profile}
          onUpdated={onSaved}
        />
      )}

      {activeTab === 'class' && (
        <div className="customize-layout">
          <aside className="class-picker">
            <p className="eyebrow">Healing Class</p>
            {profile.classes.map((candidate) => (
              <button
                className={`${candidate.id === classId ? 'active' : ''} ${selected(`class:${candidate.id}`) ? 'game-selected' : ''}`}
                data-controller-nav="skip"
                data-game-selected={selected(`class:${candidate.id}`) ? 'true' : undefined}
                key={candidate.id}
                onClick={() => chooseClass(candidate)}
                onPointerDown={() => setSelectedNavKey(`class:${candidate.id}`)}
                ref={navRef(`class:${candidate.id}`)}
                style={{ '--class-color': candidate.themeColor } as CSSProperties}
                type="button"
              >
                <span>{candidate.name[0]}</span>
                <div>
                  <strong>{candidate.name}</strong>
                  <small>{candidate.resourceName}</small>
                </div>
              </button>
            ))}
          </aside>

          <div className="loadout-editor">
            <div className="class-detail">
              <div
                className="class-portrait"
                style={{ borderColor: gameClass.themeColor, color: gameClass.themeColor }}
              >
                {gameClass.name[0]}
              </div>
              <div>
                <p className="eyebrow">Level {profile.character.level} Healer</p>
                <h2>{gameClass.name}</h2>
                <p>{gameClass.description}</p>
              </div>
            </div>

            <div className="loadout-heading">
              <div>
                <p className="eyebrow">Active Loadout</p>
                <h2>Ability Bar</h2>
              </div>
              <span>Select a slot, then choose an ability.</span>
            </div>

            <div className="ability-slots">
              {slots.map((abilityId, index) => {
                const ability = abilityId ? abilityMap.get(abilityId) : undefined
                return (
                  <button
                    className={`${selectedSlot === index ? 'selected' : ''} ${selected(`slot:${index}`) ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    data-game-selected={selected(`slot:${index}`) ? 'true' : undefined}
                    key={index}
                    onClick={() => setSelectedSlot(index)}
                    onPointerDown={() => setSelectedNavKey(`slot:${index}`)}
                    ref={navRef(`slot:${index}`)}
                    type="button"
                  >
                    <kbd>{index + 1}</kbd>
                    <span>{ability?.glyph ?? '-'}</span>
                    <strong>{ability?.name ?? 'Empty Slot'}</strong>
                  </button>
                )
              })}
            </div>

            <div className="ability-library-heading">
              <div>
                <p className="eyebrow">Class Abilities</p>
                <h2>Ability Library</h2>
              </div>
              <div className="ability-library-actions">
                {abilityPageCount > 1 && (
                  <div className="ability-library-pager">
                    <button
                      className={`text-button ${selected('ability-page-prev') ? 'game-selected' : ''}`}
                      data-controller-nav="skip"
                      data-game-selected={selected('ability-page-prev') ? 'true' : undefined}
                      disabled={currentAbilityPage === 0}
                      onClick={() => setAbilityPage((page) => Math.max(0, page - 1))}
                      onPointerDown={() => setSelectedNavKey('ability-page-prev')}
                      ref={navRef('ability-page-prev')}
                      type="button"
                    >
                      Prev
                    </button>
                    <span>{currentAbilityPage + 1}/{abilityPageCount}</span>
                    <button
                      className={`text-button ${selected('ability-page-next') ? 'game-selected' : ''}`}
                      data-controller-nav="skip"
                      data-game-selected={selected('ability-page-next') ? 'true' : undefined}
                      disabled={currentAbilityPage >= abilityPageCount - 1}
                      onClick={() => setAbilityPage((page) => Math.min(abilityPageCount - 1, page + 1))}
                      onPointerDown={() => setSelectedNavKey('ability-page-next')}
                      ref={navRef('ability-page-next')}
                      type="button"
                    >
                      Next
                    </button>
                  </div>
                )}
                <button
                  className={`text-button ${selected('clear-slot') ? 'game-selected' : ''}`}
                  data-controller-nav="skip"
                  data-game-selected={selected('clear-slot') ? 'true' : undefined}
                  onClick={clearSlot}
                  onPointerDown={() => setSelectedNavKey('clear-slot')}
                  ref={navRef('clear-slot')}
                  type="button"
                >
                  Clear Selected Slot
                </button>
              </div>
            </div>

            <div className="ability-library">
              {visibleAbilities.map((ability) => {
                const locked = ability.unlockLevel > profile.character.level
                const equipped = slots.includes(ability.id)
                return (
                  <button
                    className={`${locked ? 'locked' : ''} ${equipped ? 'equipped' : ''} ${selected(`ability:${ability.id}`) ? 'game-selected' : ''}`}
                    data-controller-nav="skip"
                    data-game-selected={selected(`ability:${ability.id}`) ? 'true' : undefined}
                    disabled={locked}
                    key={ability.id}
                    onClick={() => equipAbility(ability.id)}
                    onPointerDown={() => setSelectedNavKey(`ability:${ability.id}`)}
                    ref={navRef(`ability:${ability.id}`)}
                    type="button"
                  >
                    <span>{locked ? 'L' : ability.glyph}</span>
                    <div>
                      <strong>{ability.name}</strong>
                      <small>{ability.description}</small>
                    </div>
                    <i>{locked ? `Level ${ability.unlockLevel}` : equipped ? 'Equipped' : `${ability.cost} ${gameClass.resourceName}`}</i>
                  </button>
                )
              })}
            </div>

            <div className="save-row">
              <span>{message}</span>
              <button
                className={`primary-button ${selected('save') ? 'game-selected' : ''}`}
                data-controller-nav="skip"
                data-game-selected={selected('save') ? 'true' : undefined}
                disabled={saving}
                onClick={persistChanges}
                onPointerDown={() => setSelectedNavKey('save')}
                ref={navRef('save')}
                type="button"
              >
                {saving ? 'Saving...' : 'Save Character'}
              </button>
            </div>
          </div>
        </div>
      )}
    </section>
  )
}
