import { useMemo, useState } from 'react'
import type { ReactNode } from 'react'
import {
  ACTION_EQUIP_SLOTS,
  chooseActionTalentSpec,
  getEquippedActionItem,
  getActionTalentPoints,
  respecActionTalents,
  spendActionTalentPoint,
  type ActionCharacter,
} from '../actionMode'
import {
  getTalentSpec,
  talentsFor,
  type TalentChoiceOption,
  type TalentEffect,
  type TalentNode,
} from '../claudeCraftTalents'
import { canEquipItem } from '../claudeCraftEquipmentRules'
import { getClaudeCraftItem, getItemStatsText } from '../claudeCraftItems'
import type { EquipSlot, ItemDef, Stats } from '../claudeCraftTypes'

type ItemEntry = {
  count: number
  item: ItemDef
}

type InventoryFilter = 'all' | 'weapon' | 'armor' | 'other'

export function ActionCharacterMenu({
  character,
  onOpenInventory,
  onOpenTalents,
}: {
  character: ActionCharacter
  onOpenInventory?: () => void
  onOpenTalents?: () => void
}) {
  const [selectedSlot, setSelectedSlot] = useState<EquipSlot>('mainhand')
  const selectedItem = getEquippedActionItem(character, selectedSlot)
  const stats = useMemo(() => getEquipmentStats(character), [character])
  const weapon = getEquippedActionItem(character, 'mainhand')

  return (
    <section className="action-gear-panel claudecraft-menu">
      <article className="action-gear-summary">
        <div>
          <p className="eyebrow">ClaudeCraft</p>
          <h2>Character</h2>
        </div>
        <div className="claudecraft-character-meta">
          <span>{character.classId}</span>
          <span>Level {character.level}</span>
          <span>{character.copper} Copper</span>
        </div>
      </article>

      <div className="claudecraft-character-layout">
        <div className="action-slot-grid claudecraft-paperdoll" aria-label="Equipped gear">
          {ACTION_EQUIP_SLOTS.map((slot) => {
            const equipped = getEquippedActionItem(character, slot.slot)
            return (
              <button
                className={selectedSlot === slot.slot ? 'selected' : ''}
                key={slot.slot}
                onClick={() => setSelectedSlot(slot.slot)}
                type="button"
              >
                <span>{slot.glyph}</span>
                <strong>{slot.label}</strong>
                <small>{equipped?.name ?? 'Empty'}</small>
              </button>
            )
          })}
        </div>

        <aside className="action-inventory-panel claudecraft-character-sheet">
          <header>
            <div>
              <p className="eyebrow">Stats</p>
              <h2>{character.name}</h2>
            </div>
            {onOpenInventory && (
              <button className="back-button" onClick={onOpenInventory} type="button">Bags</button>
            )}
            {onOpenTalents && (
              <button className="back-button" onClick={onOpenTalents} type="button">Talents</button>
            )}
          </header>

          <dl className="action-stat-compare claudecraft-stat-grid">
            <StatLine label="Armor" value={stats.armor} />
            <StatLine label="Stamina" value={stats.sta} />
            <StatLine label="Strength" value={stats.str} />
            <StatLine label="Agility" value={stats.agi} />
            <StatLine label="Intellect" value={stats.int} />
            <StatLine label="Spirit" value={stats.spi} />
            <StatLine label="Weapon" value={weapon?.weapon ? `${weapon.weapon.min}-${weapon.weapon.max}` : 'None'} />
            <StatLine label="Speed" value={weapon?.weapon ? weapon.weapon.speed.toFixed(1) : '-'} />
          </dl>

          <GearDetail item={selectedItem} slot={selectedSlot} />
        </aside>
      </div>
    </section>
  )
}

export function ActionTalentMenu({
  character,
  onBack,
  onCharacterChange,
}: {
  character: ActionCharacter
  onBack?: () => void
  onCharacterChange?: (character: ActionCharacter) => void
}) {
  const talents = talentsFor(character.classId)
  const points = getActionTalentPoints(character)
  const [selectedChoices, setSelectedChoices] = useState<Record<string, string>>({})
  const [talentTab, setTalentTab] = useState<'class' | string>('class')
  const [selectedTalentId, setSelectedTalentId] = useState<string | null>(null)
  const canEdit = Boolean(onCharacterChange)

  if (!talents) {
    return (
      <section className="action-gear-panel claudecraft-menu">
        <article className="action-gear-summary action-talent-summary">
          <div className="claudecraft-character-meta">
            <span>No trees</span>
          </div>
          {onBack && (
            <button className="back-button" onClick={onBack} type="button">Back</button>
          )}
        </article>
      </section>
    )
  }

  const activeSpecId = talentTab === 'class' ? character.talents.spec : talentTab
  const selectedSpec = getTalentSpec(character.classId, activeSpecId)
  const classNodes = talents.nodes.filter((node) => node.tree === 'class')
  const specNodes = talents.nodes.filter((node) => node.tree === 'spec' && node.specId === activeSpecId)
  const visibleNodes = talentTab === 'class' ? classNodes : specNodes
  const selectedTalent = visibleNodes.find((node) => node.id === selectedTalentId) ?? visibleNodes[0] ?? null
  const treeSpent = (tree: 'class' | 'spec', specId = character.talents.spec) => talents.nodes
    .filter((node) => node.tree === tree && (tree === 'class' || node.specId === specId))
    .reduce((sum, node) => sum + (character.talents.ranks[node.id] ?? 0), 0)

  const spendPoint = (node: TalentNode, choiceId?: string) => {
    if (!canEdit) return
    const result = spendActionTalentPoint(character, node.id, choiceId ?? selectedChoices[node.id])
    if (result.ok) onCharacterChange?.(result.character)
  }

  return (
    <section className="action-gear-panel claudecraft-menu action-talents-menu">
      <article className="action-gear-summary action-talent-summary">
        <div className="claudecraft-character-meta">
          <span>{character.name}</span>
          <span>Level {character.level}</span>
          <span className="talent-available-pill">Available: {points.available} / {points.total}</span>
          <span>Spent: {points.spent}</span>
        </div>
        <div className="arena-pause-actions action-talent-actions">
          {onBack && (
            <button className="back-button" onClick={onBack} type="button">Back</button>
          )}
          <button
            className="back-button"
            disabled={!canEdit || points.spent <= 0}
            onClick={() => onCharacterChange?.(respecActionTalents(character))}
            type="button"
          >
            Respec
          </button>
        </div>
      </article>

      <div className="claudecraft-talent-tabs" role="tablist" aria-label="Talent trees">
        <button
          aria-selected={talentTab === 'class'}
          className={talentTab === 'class' ? 'active' : ''}
          onClick={() => {
            setTalentTab('class')
            setSelectedTalentId(null)
          }}
          role="tab"
          type="button"
        >
          <span>Class</span>
          <b>{treeSpent('class')}</b>
        </button>
        {talents.specs.map((spec) => (
          <button
            aria-selected={talentTab === spec.id}
            className={talentTab === spec.id ? 'active' : ''}
            key={spec.id}
            onClick={() => {
              setTalentTab(spec.id)
              setSelectedTalentId(null)
              if (character.talents.spec !== spec.id) {
                onCharacterChange?.(chooseActionTalentSpec(character, spec.id))
              }
            }}
            role="tab"
            type="button"
          >
            <span>{spec.name}</span>
            <b>{treeSpent('spec', spec.id)}</b>
          </button>
        ))}
      </div>

      {talentTab !== 'class' && selectedSpec && (
        <div className="claudecraft-talent-mastery">
          <strong>Mastery: {selectedSpec.mastery.name}</strong>
          <span>{selectedSpec.mastery.description}</span>
        </div>
      )}

      <div className="claudecraft-talent-body" role="tabpanel">
        <TalentTreeCanvas
          allocation={character.talents}
          canEdit={canEdit}
          classId={character.classId}
          choices={selectedChoices}
          nodes={visibleNodes}
          onChoice={(nodeId, choiceId) => setSelectedChoices((current) => ({ ...current, [nodeId]: choiceId }))}
          onSpend={spendPoint}
          points={points}
          selectedId={selectedTalent?.id ?? null}
          selectedSpec={selectedSpec?.id ?? null}
          onSelect={(node) => setSelectedTalentId(node.id)}
        />
        <TalentDetailPanel
          allocation={character.talents}
          canEdit={canEdit}
          classId={character.classId}
          choices={selectedChoices}
          onChoice={(nodeId, choiceId) => setSelectedChoices((current) => ({ ...current, [nodeId]: choiceId }))}
          onSpend={spendPoint}
          node={selectedTalent}
          nodes={visibleNodes}
          points={points}
          selectedSpec={selectedSpec?.id ?? null}
        />
      </div>

    </section>
  )
}

export function ActionInventoryMenu({
  character,
  onEquip,
}: {
  character: ActionCharacter
  onEquip: (itemId: string) => void
}) {
  const [filter, setFilter] = useState<InventoryFilter>('all')
  const [query, setQuery] = useState('')
  const [tooltipItemId, setTooltipItemId] = useState<string | null>(null)
  const entries = useMemo(() => getInventoryEntries(character), [character])
  const filteredEntries = useMemo(() => {
    const normalizedQuery = query.trim().toLowerCase()
    return entries
      .filter(({ item }) => {
        if (filter === 'weapon' && item.kind !== 'weapon') return false
        if (filter === 'armor' && item.kind !== 'armor') return false
        if (filter === 'other' && (item.kind === 'weapon' || item.kind === 'armor')) return false
        if (!normalizedQuery) return true
        return item.name.toLowerCase().includes(normalizedQuery)
      })
      .sort((a, b) => (getItemPower(b.item) - getItemPower(a.item)) || a.item.name.localeCompare(b.item.name))
  }, [entries, filter, query])
  const tooltipEntry = filteredEntries.find((entry) => entry.item.id === tooltipItemId) ?? null
  const tooltipItem = tooltipEntry?.item ?? null
  const tooltipEquippedItem = tooltipItem?.slot ? getEquippedActionItem(character, tooltipItem.slot) : null

  return (
    <section className="action-gear-panel claudecraft-menu">
      <article className="action-gear-summary">
        <div>
          <p className="eyebrow">ClaudeCraft</p>
          <h2>Inventory</h2>
        </div>
        <div className="claudecraft-character-meta">
          <span>{entries.length} Slots</span>
          <span>{getInventoryCount(entries)} Items</span>
          <span>{character.copper} Copper</span>
        </div>
      </article>

      <div className="claudecraft-bag-layout">
        <section className="action-inventory-panel claudecraft-bag-panel">
          <header>
            <div>
              <p className="eyebrow">Bags</p>
              <h2>{getFilterLabel(filter)}</h2>
            </div>
            <input
              aria-label="Search bags"
              onChange={(event) => setQuery(event.target.value)}
              placeholder="Search"
              value={query}
            />
          </header>

          <div className="claudecraft-bag-filters" aria-label="Bag filters">
            {(['all', 'weapon', 'armor', 'other'] as InventoryFilter[]).map((nextFilter) => (
              <button
                aria-pressed={filter === nextFilter}
                key={nextFilter}
                onClick={() => {
                  setFilter(nextFilter)
                  setTooltipItemId(null)
                }}
                type="button"
              >
                {getFilterLabel(nextFilter)}
              </button>
            ))}
          </div>

          <div className="action-inventory-list claudecraft-bag-list" onMouseLeave={() => setTooltipItemId(null)}>
            {filteredEntries.length === 0 ? (
              <p>Bags empty.</p>
            ) : (
              filteredEntries.map(({ item, count }) => (
                <button
                  key={item.id}
                  onBlur={() => setTooltipItemId(null)}
                  onClick={() => {
                    if (canEquipInventoryItem(character, item)) onEquip(item.id)
                  }}
                  onFocus={() => setTooltipItemId(item.id)}
                  onMouseEnter={() => setTooltipItemId(item.id)}
                  type="button"
                >
                  <strong>{item.name}</strong>
                  <small>{getItemTag(item)}{count > 1 ? ` x${count}` : ''}</small>
                </button>
              ))
            )}
          </div>
          {tooltipItem && (
            <InventoryItemTooltip
              canEquip={canEquipInventoryItem(character, tooltipItem)}
              count={tooltipEntry?.count ?? 1}
              equippedItem={tooltipEquippedItem}
              item={tooltipItem}
            />
          )}
        </section>
      </div>
    </section>
  )
}

export function ActionMenuOverlay({
  children,
  onBack,
  onClose,
  title,
}: {
  children: ReactNode
  onBack: () => void
  onClose: () => void
  title: string
}) {
  return (
    <div className="arena-pause-backdrop claudecraft-menu-backdrop" role="dialog" aria-modal="true" aria-labelledby="action-menu-overlay-title">
      <section className="claudecraft-menu-overlay">
        <header>
          <div>
            <p className="eyebrow">Paused</p>
            <h2 id="action-menu-overlay-title">{title}</h2>
          </div>
          <div className="arena-pause-actions">
            <button className="back-button" onClick={onBack} type="button">Back</button>
            <button className="primary-button" onClick={onClose} type="button">Resume</button>
          </div>
        </header>
        {children}
      </section>
    </div>
  )
}

function TalentTreeCanvas({
  allocation,
  canEdit,
  classId,
  choices,
  nodes,
  onChoice,
  onSelect,
  onSpend,
  points,
  selectedId,
  selectedSpec,
}: {
  allocation: ActionCharacter['talents']
  canEdit: boolean
  classId: ActionCharacter['classId']
  choices: Record<string, string>
  nodes: TalentNode[]
  onChoice: (nodeId: string, choiceId: string) => void
  onSelect: (node: TalentNode) => void
  onSpend: (node: TalentNode, choiceId?: string) => void
  points: { available: number, spent: number, total: number }
  selectedId: string | null
  selectedSpec: string | null
}) {
  const cols = Math.max(1, ...nodes.map((node) => node.col + 1))
  const rows = Math.max(1, ...nodes.map((node) => node.row + 1))
  const cellWidth = 86
  const cellHeight = 70
  const nodeSize = 46
  const top = 8
  const width = cols * cellWidth
  const height = rows * cellHeight + top
  const byId = new Map(nodes.map((node) => [node.id, node]))
  const centerX = (node: TalentNode) => node.col * cellWidth + cellWidth / 2
  const centerY = (node: TalentNode) => node.row * cellHeight + top + nodeSize / 2

  if (nodes.length === 0) {
    return <div className="claudecraft-talent-empty">Choose a specialization.</div>
  }

  return (
    <div className="claudecraft-talent-tree-scroll">
      <div className="claudecraft-talent-tree" style={{ height, width }}>
        <svg className="claudecraft-talent-arrows" height={height} width={width}>
          {nodes.flatMap((node) => (node.requires ?? []).map((required) => {
            const parent = byId.get(required)
            if (!parent) return null
            const filled = (allocation.ranks[required] ?? 0) > 0
            return (
              <line
                key={`${required}-${node.id}`}
                stroke={filled ? '#f5c843' : '#5a4a22'}
                strokeWidth="2"
                x1={centerX(parent)}
                x2={centerX(node)}
                y1={centerY(parent) + nodeSize / 2}
                y2={centerY(node) - nodeSize / 2}
              />
            )
          }))}
        </svg>
        {nodes.map((node) => {
          const rank = allocation.ranks[node.id] ?? 0
          const locked = isTalentLocked(node, allocation, nodes, points, selectedSpec)
          const choiceId = choices[node.id] ?? allocation.choices[node.id] ?? node.choices?.[0]?.id
          const selectedChoice = node.choices?.find((choice) => choice.id === choiceId)
          const canSpend = canEdit && !locked && rank < node.maxRank && points.available > 0
          const maxed = rank >= node.maxRank
          const shape = node.kind === 'active' ? 'square' : node.kind === 'choice' ? 'octagon' : 'circle'
          const state = locked ? 'locked' : maxed ? 'maxed' : rank > 0 ? 'filled' : canSpend ? 'avail' : 'locked'
          return (
            <button
              aria-label={`${node.name}, rank ${rank}/${node.maxRank}`}
              aria-pressed={rank > 0}
              aria-disabled={!canSpend && rank <= 0}
              className={`claudecraft-talent-node ${shape} ${state} ${selectedId === node.id ? 'selected' : ''}`}
              key={node.id}
              onClick={() => {
                onSelect(node)
                if (node.kind === 'choice') {
                  if (choiceId) onChoice(node.id, choiceId)
                  if (rank <= 0 && canSpend) onSpend(node, choiceId)
                  return
                }
                if (canSpend) onSpend(node)
              }}
              onMouseEnter={() => onSelect(node)}
              style={{
                left: node.col * cellWidth + (cellWidth - nodeSize) / 2,
                top: node.row * cellHeight + top,
              }}
              type="button"
            >
              <span
                className="claudecraft-talent-icon"
                style={{ backgroundImage: `url(${getTalentIconUrl(classId, selectedChoice ?? node)})` }}
              />
              {(rank > 0 || node.maxRank > 1) && (
                <span className="claudecraft-talent-rank">{rank}/{node.maxRank}</span>
              )}
            </button>
          )
        })}
      </div>
    </div>
  )
}

function TalentDetailPanel({
  allocation,
  canEdit,
  classId,
  choices,
  node,
  nodes,
  onChoice,
  onSpend,
  points,
  selectedSpec,
}: {
  allocation: ActionCharacter['talents']
  canEdit: boolean
  classId: ActionCharacter['classId']
  choices: Record<string, string>
  node: TalentNode | null
  nodes: TalentNode[]
  onChoice: (nodeId: string, choiceId: string) => void
  onSpend: (node: TalentNode, choiceId?: string) => void
  points: { available: number, spent: number, total: number }
  selectedSpec: string | null
}) {
  if (!node) return <aside className="claudecraft-talent-detail"><span>Select a talent.</span></aside>

  const rank = allocation.ranks[node.id] ?? 0
  const locked = isTalentLocked(node, allocation, nodes, points, selectedSpec)
  const choiceId = choices[node.id] ?? allocation.choices[node.id] ?? node.choices?.[0]?.id
  const selectedChoice = node.choices?.find((choice) => choice.id === choiceId)
  const canSpend = canEdit && !locked && rank < node.maxRank && points.available > 0

  return (
    <aside className="claudecraft-talent-detail">
      <header>
        <span
          className="claudecraft-talent-detail-icon"
          style={{ backgroundImage: `url(${getTalentIconUrl(classId, selectedChoice ?? node)})` }}
        />
        <div>
          <strong>{node.name}</strong>
          <small>Rank {rank}/{node.maxRank}</small>
        </div>
      </header>
      <p>{node.description}</p>
      {node.requires?.length ? <small>Requires: {node.requires.join(', ')}</small> : null}
      {node.pointsGate ? <small>{node.pointsGate} points required above.</small> : null}
      {locked && <small className="claudecraft-talent-warning">Locked</small>}
      {node.kind === 'choice' && node.choices && (
        <div className="claudecraft-talent-choice-list">
          {node.choices.map((choice) => {
            const selected = choiceId === choice.id
            return (
              <button
                aria-pressed={selected}
                className={selected ? 'selected' : ''}
                disabled={!canEdit || (rank > 0 && !selected)}
                key={choice.id}
                onClick={() => {
                  onChoice(node.id, choice.id)
                  if (rank <= 0 && canSpend) onSpend(node, choice.id)
                }}
                type="button"
              >
                <span style={{ backgroundImage: `url(${getTalentIconUrl(classId, choice)})` }} />
                <strong>{choice.name}</strong>
                <small>{choice.description}</small>
              </button>
            )
          })}
        </div>
      )}
      {node.kind !== 'choice' && (
        <button
          className="primary-button"
          disabled={!canSpend}
          onClick={() => onSpend(node)}
          type="button"
        >
          Add Point
        </button>
      )}
    </aside>
  )
}

function getTalentIconUrl(classId: ActionCharacter['classId'], source: TalentNode | TalentChoiceOption) {
  const abilityId = getTalentAbilityIconId(source.effect)
  if (abilityId) return `/ui/skills/${classId}/${abilityId}.png`
  return getTalentCrestIconUrl(source.effect, 'choices' in source ? 'choice' : 'node')
}

function getTalentAbilityIconId(effect?: TalentEffect) {
  return effect?.grant?.ability ?? effect?.ability?.[0]?.ability ?? null
}

function getTalentCrestIconUrl(effect: TalentEffect | undefined, kind: 'choice' | 'node') {
  const stat = effect?.stats ? Object.keys(effect.stats)[0] : ''
  const crest = stat.includes('armor')
    ? { bg: '#2d3542', fg: '#d8e2ee', glyph: 'shield' }
    : stat.includes('dodge') || stat === 'agi'
      ? { bg: '#163b4f', fg: '#9fe4ff', glyph: 'spark' }
      : stat.includes('hp') || stat === 'sta'
        ? { bg: '#4f1418', fg: '#ffd6d6', glyph: 'heart' }
        : stat.includes('ap') || stat === 'str'
          ? { bg: '#4d2f0c', fg: '#ffd36e', glyph: 'fist' }
          : effect?.global?.healPct
            ? { bg: '#254525', fg: '#bcffb5', glyph: 'cross' }
            : effect?.global?.threatPct
              ? { bg: '#313641', fg: '#d9e5ff', glyph: 'shield' }
              : effect?.global
                ? { bg: '#4a3510', fg: '#ffe28a', glyph: 'burst' }
                : kind === 'choice'
                  ? { bg: '#2e1c4e', fg: '#f0d2ff', glyph: 'gem' }
                  : { bg: '#252b35', fg: '#dde7ff', glyph: 'rune' }
  return svgDataUrl(renderTalentCrestSvg(crest.bg, crest.fg, crest.glyph))
}

function svgDataUrl(svg: string) {
  return `data:image/svg+xml;base64,${window.btoa(svg)}`
}

function renderTalentCrestSvg(bg: string, fg: string, glyph: string) {
  const common = `fill="${fg}" stroke="#120d04" stroke-width="3" stroke-linejoin="round"`
  const mark = glyph === 'shield'
    ? `<path ${common} d="M64 20 L100 34 V62 C100 88 82 106 64 116 C46 106 28 88 28 62 V34 Z"/>`
    : glyph === 'heart'
      ? `<path ${common} d="M64 108 C36 82 22 67 22 47 C22 32 32 22 46 22 C54 22 60 26 64 34 C68 26 74 22 82 22 C96 22 106 32 106 47 C106 67 92 82 64 108 Z"/>`
      : glyph === 'cross'
        ? `<path ${common} d="M54 22 H74 V52 H104 V72 H74 V106 H54 V72 H24 V52 H54 Z"/>`
        : glyph === 'fist'
          ? `<path ${common} d="M32 56 H44 V28 H58 V56 H66 V24 H80 V58 H88 V34 H101 V74 C101 94 86 108 64 108 C42 108 28 94 28 74 Z"/>`
          : glyph === 'spark'
            ? `<path ${common} d="M70 12 L46 58 H66 L54 116 L86 50 H66 Z"/>`
            : glyph === 'gem'
              ? `<path ${common} d="M64 18 L104 48 L64 112 L24 48 Z M24 48 H104 M46 48 L64 112 L82 48"/>`
              : glyph === 'burst'
                ? `<path ${common} d="M64 14 L74 48 L108 38 L84 64 L108 90 L74 80 L64 114 L54 80 L20 90 L44 64 L20 38 L54 48 Z"/>`
                : `<path ${common} d="M32 28 H96 V100 H32 Z M46 44 H82 M46 64 H82 M46 84 H72"/>`
  return `<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128"><defs><radialGradient id="g" cx="35%" cy="25%" r="80%"><stop stop-color="#ffffff" stop-opacity=".25"/><stop offset=".45" stop-color="${bg}"/><stop offset="1" stop-color="#07090d"/></radialGradient></defs><rect width="128" height="128" rx="18" fill="url(#g)"/><rect x="8" y="8" width="112" height="112" rx="16" fill="none" stroke="#f5c843" stroke-opacity=".35" stroke-width="4"/>${mark}</svg>`
}

function isTalentLocked(
  node: TalentNode,
  allocation: ActionCharacter['talents'],
  siblingNodes: TalentNode[],
  points: { available: number },
  selectedSpec: string | null,
) {
  if (!allocation.spec) return true
  if (node.tree === 'spec' && node.specId !== selectedSpec) return true
  for (const required of node.requires ?? []) {
    if ((allocation.ranks[required] ?? 0) <= 0) return true
  }
  const gate = node.pointsGate ?? 0
  if (gate > 0) {
    const spentAbove = siblingNodes
      .filter((candidate) => candidate.tree === node.tree && candidate.row < node.row)
      .reduce((sum, candidate) => sum + (allocation.ranks[candidate.id] ?? 0), 0)
    if (spentAbove < gate) return true
  }
  return points.available <= 0 && (allocation.ranks[node.id] ?? 0) < node.maxRank
}

function StatLine({ label, value }: { label: string, value: number | string }) {
  return (
    <div>
      <dt>{label}</dt>
      <dd>{value}</dd>
    </div>
  )
}

function GearDetail({
  equippedItem,
  item,
  onEquip,
  slot,
}: {
  equippedItem?: ItemDef | null
  item: ItemDef | null
  onEquip?: () => void
  slot?: EquipSlot
}) {
  if (!item) {
    return (
      <section className="action-gear-detail empty">
        <p>{slot ? `No item equipped in ${slot}.` : 'Select an item.'}</p>
      </section>
    )
  }

  return (
    <section className="action-gear-detail">
      <header>
        <div>
          <p className="eyebrow">Selected</p>
          <h2>{item.name}</h2>
        </div>
        <span>{item.quality ?? 'common'}</span>
      </header>
      <dl className="action-stat-compare">
        <div>
          <dt>Stats</dt>
          <dd>{getItemStatsText(item)}</dd>
        </div>
        <div>
          <dt>Equipped</dt>
          <dd>{equippedItem ? getItemStatsText(equippedItem) : 'Empty'}</dd>
        </div>
        <div>
          <dt>Slot</dt>
          <dd>{item.slot ?? 'Bag'}</dd>
        </div>
        <div>
          <dt>Class</dt>
          <dd>{item.requiredClass?.join(', ') ?? 'Any'}</dd>
        </div>
      </dl>
      {onEquip && (
        <button disabled={equippedItem?.id === item.id} onClick={onEquip} type="button">
          {equippedItem?.id === item.id ? 'Equipped' : 'Equip'}
        </button>
      )}
    </section>
  )
}

function InventoryItemTooltip({
  canEquip,
  count,
  equippedItem,
  item,
}: {
  canEquip: boolean
  count: number
  equippedItem?: ItemDef | null
  item: ItemDef
}) {
  const rows = getItemTooltipRows(item, equippedItem)
  return (
    <aside className={`claudecraft-action-tooltip claudecraft-item-tooltip quality-${item.quality ?? 'common'}`} role="tooltip">
      <div className="tooltip-heading">
        <span className="item-tooltip-icon">{getItemIconGlyph(item)}</span>
        <div>
          <strong>{item.name}</strong>
          <span>{formatItemQuality(item)}{count > 1 ? ` x${count}` : ''}</span>
        </div>
      </div>
      <dl>
        <div><dt>Slot</dt><dd>{item.slot ? formatEquipSlot(item.slot) : 'Bag'}</dd></div>
        <div><dt>Type</dt><dd>{getItemKindLabel(item)}</dd></div>
        {item.requiredClass?.length ? <div><dt>Class</dt><dd>{item.requiredClass.join(', ')}</dd></div> : null}
        {item.sellValue > 0 ? <div><dt>Sell</dt><dd>{item.sellValue} copper</dd></div> : null}
      </dl>
      <dl className="item-tooltip-stats">
        {rows.length === 0 ? (
          <div><dt>Stats</dt><dd>No combat stats</dd></div>
        ) : rows.map((row) => (
          <div className={row.delta === undefined ? '' : row.delta > 0 ? 'better' : row.delta < 0 ? 'worse' : 'same'} key={row.label}>
            <dt>{row.label}</dt>
            <dd>
              <span>{row.value}</span>
              {row.delta !== undefined && <em>{formatStatDelta(row.delta)}</em>}
            </dd>
          </div>
        ))}
      </dl>
      {item.slot && (
        <p>
          {equippedItem
            ? `Equipped: ${equippedItem.name}`
            : `Equipped: empty ${formatEquipSlot(item.slot)}`}
        </p>
      )}
      {item.slot && <em>{canEquip ? 'Click to equip.' : 'Cannot equip.'}</em>}
    </aside>
  )
}

function getInventoryEntries(character: ActionCharacter): ItemEntry[] {
  return character.inventory
    .map((slot) => ({ item: getClaudeCraftItem(slot.itemId), count: slot.count }))
    .filter((entry): entry is ItemEntry => Boolean(entry.item))
}

function getInventoryCount(entries: ItemEntry[]) {
  return entries.reduce((total, entry) => total + entry.count, 0)
}

type ItemTooltipRow = {
  delta?: number
  label: string
  value: string
}

const STAT_ORDER: Array<keyof Stats> = ['armor', 'str', 'agi', 'sta', 'int', 'spi']
const STAT_LABELS: Record<keyof Stats, string> = {
  agi: 'Agility',
  armor: 'Armor',
  int: 'Intellect',
  spi: 'Spirit',
  sta: 'Stamina',
  str: 'Strength',
}

function getItemTooltipRows(item: ItemDef, equippedItem?: ItemDef | null): ItemTooltipRow[] {
  const rows: ItemTooltipRow[] = []
  if (item.weapon) {
    const equippedWeapon = equippedItem?.weapon
    const itemAverage = (item.weapon.min + item.weapon.max) / 2
    const equippedAverage = equippedWeapon ? (equippedWeapon.min + equippedWeapon.max) / 2 : 0
    rows.push({
      delta: equippedItem ? itemAverage - equippedAverage : undefined,
      label: 'Damage',
      value: `${item.weapon.min}-${item.weapon.max}`,
    })
    rows.push({
      delta: equippedWeapon ? equippedWeapon.speed - item.weapon.speed : undefined,
      label: 'Speed',
      value: item.weapon.speed.toFixed(1),
    })
  }

  for (const key of STAT_ORDER) {
    const value = item.stats?.[key] ?? 0
    if (!value) continue
    const equippedValue = equippedItem?.stats?.[key] ?? 0
    rows.push({
      delta: equippedItem ? value - equippedValue : undefined,
      label: STAT_LABELS[key],
      value: key === 'armor' ? String(value) : `+${value}`,
    })
  }
  return rows
}

function formatStatDelta(delta: number) {
  if (delta === 0) return '+0'
  return delta > 0 ? `+${formatDeltaNumber(delta)}` : formatDeltaNumber(delta)
}

function formatDeltaNumber(value: number) {
  return Number.isInteger(value) ? String(value) : value.toFixed(1)
}

function getItemIconGlyph(item: ItemDef) {
  if (item.kind === 'weapon') return '/'
  if (item.kind === 'armor') return 'H'
  if (item.kind === 'potion' || item.kind === 'elixir') return '+'
  return '*'
}

function formatItemQuality(item: ItemDef) {
  const quality = item.quality ?? 'common'
  return quality[0].toUpperCase() + quality.slice(1)
}

function formatEquipSlot(slot: EquipSlot) {
  return ACTION_EQUIP_SLOTS.find((entry) => entry.slot === slot)?.label ?? slot
}

function getItemKindLabel(item: ItemDef) {
  if (item.kind === 'weapon' && item.weapon?.dagger) return 'Dagger'
  if (item.kind === 'weapon') return 'Weapon'
  if (item.kind === 'armor') return item.armorType ? `${item.armorType} armor` : 'Armor'
  return item.kind
}

function getEquipmentStats(character: ActionCharacter): Stats {
  const stats: Stats = { str: 0, agi: 0, sta: 0, int: 0, spi: 0, armor: 0 }
  for (const slot of ACTION_EQUIP_SLOTS) {
    const item = getEquippedActionItem(character, slot.slot)
    if (!item?.stats) continue
    stats.str += item.stats.str ?? 0
    stats.agi += item.stats.agi ?? 0
    stats.sta += item.stats.sta ?? 0
    stats.int += item.stats.int ?? 0
    stats.spi += item.stats.spi ?? 0
    stats.armor += item.stats.armor ?? 0
  }
  return stats
}

function canEquipInventoryItem(character: ActionCharacter, item: ItemDef) {
  return Boolean(item.slot && (item.kind === 'weapon' || item.kind === 'armor') && canEquipItem(character.classId, item))
}

function getFilterLabel(filter: InventoryFilter) {
  if (filter === 'weapon') return 'Weapons'
  if (filter === 'armor') return 'Armor'
  if (filter === 'other') return 'Other'
  return 'All'
}

function getItemTag(item: ItemDef) {
  const quality = item.quality ?? 'common'
  const slot = item.slot ?? item.kind
  const stats = getItemStatsText(item)
  return stats ? `${quality} - ${slot} - ${stats}` : `${quality} - ${slot}`
}

function getItemPower(item: ItemDef) {
  const stats = item.stats ?? {}
  const statScore = (stats.str ?? 0) + (stats.agi ?? 0) + (stats.sta ?? 0) + (stats.int ?? 0) + (stats.spi ?? 0)
  const armorScore = (stats.armor ?? 0) / 12
  const weaponScore = item.weapon ? item.weapon.min + item.weapon.max : 0
  return statScore + armorScore + weaponScore
}
