import { useMemo } from 'react'
import PublicLayout from '../../components/PublicLayout.jsx'
import PageHeader from '../../components/PageHeader.jsx'
import { Loading, ErrorState } from '../../components/PageState.jsx'
import { useAsync } from '../../lib/useAsync.js'
import { useShardFeed } from '../../lib/useShardFeed.js'
import { api } from '../../api/client.js'

// The champion-spawn board. Loaded once from /public/shard/champs, then kept live
// by merging champ.update / champ.remove deltas from the public SSE feed. Three
// families share the board, split by category into their own sections.
const CHAMP_KINDS = new Set(['champ.update', 'champ.remove'])

const SECTIONS = [
  { id: 'champion', title: 'Champion altars', blurb: 'Felucca-style altar spawns.' },
  { id: 'mini', title: 'Mini champs', blurb: 'TerMur controllers — they re-arm on their own.' },
  { id: 'sea', title: 'Sea bosses', blurb: 'High Seas world bosses, alive only while summoned.' },
]

const STATUS_STYLE = {
  active: { bg: 'rgba(95,185,138,0.16)', fg: '#8fdcae', border: 'rgba(95,185,138,0.45)', label: 'Active' },
  cooldown: { bg: 'rgba(230,194,106,0.14)', fg: '#e6c26a', border: 'rgba(230,194,106,0.4)', label: 'Cooldown' },
  dormant: { bg: 'rgba(140,150,165,0.14)', fg: '#aab3c0', border: 'rgba(140,150,165,0.35)', label: 'Dormant' },
}

// A short "in 4m" / "in 2h" for a future ISO timestamp (restartAt / expireAt).
function until(iso) {
  if (!iso) return ''
  const ms = new Date(iso).getTime() - Date.now()
  if (!Number.isFinite(ms)) return ''
  if (ms <= 0) return 'due'
  const mins = Math.round(ms / 60000)
  if (mins < 60) return `in ${mins}m`
  const hrs = Math.round(mins / 60)
  return `in ${hrs}h`
}

function StatusBadge({ status }) {
  const s = STATUS_STYLE[status] || STATUS_STYLE.dormant
  return (
    <span
      className="sans"
      style={{
        flex: 'none',
        fontSize: '0.68rem',
        letterSpacing: '0.08em',
        textTransform: 'uppercase',
        padding: '3px 9px',
        borderRadius: 999,
        color: s.fg,
        background: s.bg,
        border: `1px solid ${s.border}`,
      }}
    >
      {s.label}
    </span>
  )
}

// A slim progress bar (kills toward the next level, or a sea boss's hit points).
function Meter({ value, max, tone = 'var(--accent)' }) {
  if (!max) return null
  const pct = Math.max(0, Math.min(100, (Number(value) / Number(max)) * 100))
  return (
    <div style={{ height: 6, borderRadius: 4, background: 'rgba(255,255,255,0.07)', overflow: 'hidden' }}>
      <div style={{ width: `${pct}%`, height: '100%', background: tone, borderRadius: 4 }} />
    </div>
  )
}

// Category-specific middle line + meter for one spawn.
function ChampDetail({ s }) {
  const line = { display: 'flex', justifyContent: 'space-between', gap: 12, fontSize: '0.8rem', color: 'var(--muted)', marginTop: 8 }
  if (s.category === 'sea') {
    return (
      <>
        <div className="sans" style={line}>
          <span>{s.boss || s.type}</span>
          {s.hitsMax != null && <span>{Number(s.hits).toLocaleString()} / {Number(s.hitsMax).toLocaleString()} hp</span>}
        </div>
        <div style={{ marginTop: 6 }}><Meter value={s.hits} max={s.hitsMax} tone="#d9736f" /></div>
      </>
    )
  }
  if (s.category === 'mini') {
    return (
      <div className="sans" style={line}>
        <span>Level {s.level ?? 0}{s.maxLevel != null ? ` / ${s.maxLevel}` : ''}</span>
        <span>{s.status === 'active' ? 'Running' : 'Re-arming'}</span>
      </div>
    )
  }
  // champion
  let progress = ''
  if (s.status === 'cooldown') progress = until(s.restartAt) || 'restarting'
  else if (s.status === 'active') {
    progress = `${Number(s.kills || 0).toLocaleString()} / ${Number(s.maxKills || 0).toLocaleString()} kills`
  }
  return (
    <>
      <div className="sans" style={line}>
        <span>
          Level {s.level ?? 0}
          {s.bossUp && s.boss ? ` — ${s.boss}` : ''}
        </span>
        <span>{progress}</span>
      </div>
      {s.status === 'active' && (
        <div style={{ marginTop: 6 }}><Meter value={s.kills} max={s.maxKills} /></div>
      )}
    </>
  )
}

function ChampCard({ s }) {
  return (
    <div className="panel" style={{ padding: 16 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
        <strong className="display" style={{ fontSize: '1.02rem', color: 'var(--head)', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
          {s.name || s.type || 'Spawn'}
        </strong>
        <StatusBadge status={s.status} />
      </div>
      <ChampDetail s={s} />
      <div className="sans dim" style={{ marginTop: 10, fontSize: '0.74rem' }}>
        {s.map || '—'}{s.x != null ? ` (${s.x}, ${s.y})` : ''}
      </div>
    </div>
  )
}

export default function ChampSpawns() {
  const { loading, error, data } = useAsync(() => api.shard.champs())
  const { events, connected } = useShardFeed({ filter: CHAMP_KINDS, max: 60 })

  // Merge the initial snapshot with live deltas: seed a map by serial, then apply
  // buffered events oldest → newest (the buffer is newest-first) so live wins.
  const board = useMemo(() => {
    const map = new Map()
    for (const s of data || []) if (s && s.serial) map.set(s.serial, s)
    for (let i = events.length - 1; i >= 0; i -= 1) {
      const ev = events[i]
      if (!ev || !ev.serial) continue
      if (ev.kind === 'champ.update') map.set(ev.serial, ev)
      else if (ev.kind === 'champ.remove') map.delete(ev.serial)
    }
    return [...map.values()]
  }, [data, events])

  const byCategory = (id) =>
    board.filter((s) => (s.category || 'champion') === id).sort((a, b) => (a.name || '').localeCompare(b.name || ''))

  const activeCount = board.filter((s) => s.status === 'active').length

  return (
    <PublicLayout section="website">
      <div className="shell-narrow page-body">
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>
          <PageHeader eyebrow="Live" title="Champion spawns" lead="Every altar, mini-champ and sea boss across the shard, updating in real time." />
          <span className="sans" style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: '0.74rem', color: connected ? '#7fd0a4' : 'var(--muted)', flex: 'none', marginTop: 6 }}>
            <span style={{ width: 8, height: 8, borderRadius: '50%', background: connected ? '#7fd0a4' : 'var(--dim)' }} />
            {connected ? 'Live' : 'Offline'}
          </span>
        </div>

        {loading && <Loading />}
        {error && <ErrorState message="Could not load the champion board right now." />}

        {!loading && !error && (
          <>
            {board.length === 0 ? (
              <section className="panel" style={{ padding: 24, textAlign: 'center' }}>
                <p className="sans dim" style={{ margin: 0 }}>No champion spawns are being tracked right now.</p>
              </section>
            ) : (
              <>
                <p className="sans" style={{ color: 'var(--accent)', fontSize: '0.8rem', marginTop: -12, marginBottom: 24 }}>
                  {activeCount} active · {board.length} tracked
                </p>
                {SECTIONS.map((sec) => {
                  const rows = byCategory(sec.id)
                  if (rows.length === 0) return null
                  return (
                    <section key={sec.id} style={{ marginBottom: 28 }}>
                      <div style={{ marginBottom: 12 }}>
                        <h2 className="display" style={{ margin: 0, fontSize: '1.1rem', color: 'var(--head)' }}>{sec.title}</h2>
                        <p className="sans dim" style={{ margin: '2px 0 0', fontSize: '0.8rem' }}>{sec.blurb}</p>
                      </div>
                      <div className="grid-2" style={{ gap: 12 }}>
                        {rows.map((s) => <ChampCard key={s.serial} s={s} />)}
                      </div>
                    </section>
                  )
                })}
              </>
            )}
          </>
        )}
      </div>
    </PublicLayout>
  )
}
