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 shard ruleset. Loaded from /public/shard/ruleset, replaced wholesale by any
// world.ruleset frame on the live feed (the shard re-emits the entire ruleset, so
// there is nothing to merge — latest wins).
//
// Everything on this page is published BY THE SHARD from its own Config/*.cfg, so
// it cannot drift the way a hand-written rules page does. That is the whole point
// of the feature, and the page says so.
const RULESET_KINDS = new Set(['world.ruleset'])

// Skill and stat caps arrive in tenths, the way ServUO stores them: 1000 is 100.0
// skill. Showing the raw number would be actively misleading.
const tenths = (v) => (Number.isFinite(v) ? (v / 10).toFixed(1) : null)

const num = (v) => (Number.isFinite(v) ? v.toLocaleString() : null)

const pct = (v) => (Number.isFinite(v) ? `${v}%` : null)

// The systems block is a flat bag of booleans; these are their display names, and
// the order here is the order they render. A key the shard sends that we don't
// know about still renders, humanised, rather than being silently dropped — a new
// plugin must not go invisible against an older client.
const SYSTEM_LABELS = {
  cityLoyalty: 'City Loyalty (governors)',
  vvv: 'Vice vs Virtue',
  factions: 'Factions',
  siege: 'Siege ruleset',
  chat: 'In-game chat',
  store: 'Ultima Store',
  dailyRares: 'Daily rares',
  honesty: 'Honesty virtue',
  shadowguard: 'Shadowguard',
  treasureMaps: 'Treasure maps',
  vetRewards: 'Veteran rewards',
  testCenter: 'Test Center',
}

const humanise = (key) =>
  key.replace(/([A-Z])/g, ' $1').replace(/^./, (c) => c.toUpperCase())

function Panel({ title, children }) {
  return (
    <section className="panel" style={{ padding: 18 }}>
      <h2
        className="display"
        style={{ margin: '0 0 12px', fontSize: '1.02rem', color: 'var(--head)' }}
      >
        {title}
      </h2>
      {children}
    </section>
  )
}

// A label/value row. Rows whose value is null are dropped by the caller, so a
// block never renders a dangling label for something the shard didn't publish.
function Row({ label, value }) {
  return (
    <div
      className="sans"
      style={{
        display: 'flex',
        alignItems: 'baseline',
        justifyContent: 'space-between',
        gap: 12,
        padding: '5px 0',
        borderBottom: '1px solid var(--line)',
        fontSize: '0.86rem',
      }}
    >
      <span className="dim" style={{ minWidth: 0 }}>{label}</span>
      <strong style={{ flex: 'none', color: 'var(--head)' }}>{value}</strong>
    </div>
  )
}

function Rows({ items }) {
  const rows = items.filter(([, value]) => value !== null && value !== undefined)
  if (rows.length === 0) return null
  return (
    <div>
      {rows.map(([label, value]) => (
        <Row key={label} label={label} value={value} />
      ))}
    </div>
  )
}

function SystemPill({ label, on }) {
  const color = on ? '#8fdcae' : 'var(--muted)'
  return (
    <span
      className="sans"
      style={{
        display: 'inline-flex',
        alignItems: 'center',
        gap: 7,
        fontSize: '0.8rem',
        padding: '5px 11px',
        borderRadius: 999,
        color,
        background: on ? 'rgba(95,185,138,0.12)' : 'rgba(140,150,165,0.1)',
        border: `1px solid ${on ? 'rgba(95,185,138,0.4)' : 'var(--line)'}`,
      }}
    >
      <span
        aria-hidden="true"
        style={{ width: 7, height: 7, borderRadius: '50%', background: color, flex: 'none' }}
      />
      {label}
    </span>
  )
}

function Systems({ systems }) {
  // Known keys first in their declared order, then anything the shard added that
  // this build doesn't know about.
  const known = Object.keys(SYSTEM_LABELS).filter((k) => k in systems)
  const extra = Object.keys(systems).filter((k) => !(k in SYSTEM_LABELS))
  const keys = [...known, ...extra]
  if (keys.length === 0) return null
  return (
    <Panel title="Systems">
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
        {keys.map((k) => (
          <SystemPill key={k} label={SYSTEM_LABELS[k] || humanise(k)} on={!!systems[k]} />
        ))}
      </div>
    </Panel>
  )
}

function Caps({ caps }) {
  return (
    <Panel title="Skill & stat caps">
      <Rows
        items={[
          ['Individual skill cap', tenths(caps.skill)],
          ['Total skill cap', tenths(caps.totalSkill)],
          ['Total stat cap', num(caps.stat)],
          ['Strength cap', num(caps.str)],
          ['Dexterity cap', num(caps.dex)],
          ['Intelligence cap', num(caps.int)],
          ['Strength max', num(caps.strMax)],
          ['Dexterity max', num(caps.dexMax)],
          ['Intelligence max', num(caps.intMax)],
        ]}
      />
    </Panel>
  )
}

function AccountsAndHousing({ accounts, housing, vetRewards }) {
  const items = []
  if (accounts) {
    items.push(['Accounts per IP', num(accounts.perIp)])
    items.push(['Character slots', num(accounts.charSlots)])
    items.push([
      'In-game account creation',
      accounts.autoCreate === undefined ? null : accounts.autoCreate ? 'Enabled' : 'Website only',
    ])
  }
  if (housing) items.push(['Houses per account', num(housing.accountHouseLimit)])
  if (vetRewards?.enabled) {
    items.push(['Veteran reward interval', vetRewards.rewardIntervalDays
      ? `${vetRewards.rewardIntervalDays} days`
      : null])
  }
  if (items.length === 0) return null
  return (
    <Panel title="Accounts & housing">
      <Rows items={items} />
    </Panel>
  )
}

function Champions({ champions }) {
  const t = champions.rankThresholds
  return (
    <Panel title="Champion spawns">
      <Rows
        items={[
          ['Power scrolls per spawn', num(champions.powerScrolls)],
          ['Stat scrolls per spawn', num(champions.statScrolls)],
          ['Scroll drop chance', pct(champions.scrollChance)],
          ['Transcendence chance', pct(champions.transcendenceChance)],
          [
            'Red skulls per rank',
            Array.isArray(t) && t.length > 0 ? t.join(' · ') : null,
          ],
        ]}
      />
    </Panel>
  )
}

function Felucca({ loot }) {
  return (
    <Panel title="Felucca bonuses">
      <Rows
        items={[
          ['Luck bonus', num(loot.feluccaLuckBonus)],
          ['Loot budget bonus', num(loot.feluccaBudgetBonus)],
          ['Max item properties', num(loot.feluccaMaxProps)],
        ]}
      />
    </Panel>
  )
}

function Vendors({ vendors }) {
  return (
    <Panel title="Vendors">
      <Rows
        items={[
          ['Restock delay', vendors.restockDelayMinutes
            ? `${vendors.restockDelayMinutes} min`
            : null],
          ['Max items sold at once', num(vendors.maxSell)],
          ['Economy stock amount', num(vendors.economyStockAmount)],
        ]}
      />
    </Panel>
  )
}

function Pvp({ vvv }) {
  return (
    <Panel title="Vice vs Virtue">
      <Rows
        items={[
          ['Starting silver', num(vvv.startSilver)],
          ['Enhanced rules', vvv.enhancedRules === undefined
            ? null
            : vvv.enhancedRules ? 'On' : 'Off'],
        ]}
      />
    </Panel>
  )
}

function Schedule({ schedule }) {
  const items = []
  if (schedule.autoSaveEnabled && schedule.autoSaveFrequencyMinutes) {
    items.push(['World save', `every ${schedule.autoSaveFrequencyMinutes} min`])
  } else if (schedule.autoSaveEnabled === false) {
    items.push(['World save', 'Disabled'])
  }
  if (schedule.autoRestartEnabled) {
    const h = String(schedule.autoRestartHour ?? 0).padStart(2, '0')
    const m = String(schedule.autoRestartMinute ?? 0).padStart(2, '0')
    items.push(['Automatic restart', `${h}:${m} server time`])
    if (schedule.autoRestartFrequencyHours) {
      items.push(['Restart interval', `every ${schedule.autoRestartFrequencyHours}h`])
    }
  }
  if (items.length === 0) return null
  return (
    <Panel title="Save & restart schedule">
      <Rows items={items} />
    </Panel>
  )
}

export default function Rules() {
  const { loading, error, data } = useAsync(() => api.shard.ruleset())
  const { events, connected } = useShardFeed({ filter: RULESET_KINDS, max: 4 })

  // The newest world.ruleset on the feed wins outright over the fetched copy —
  // the frame is a complete ruleset, not a delta.
  const ruleset = useMemo(() => events[0] || data || null, [data, events])

  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="Shard ruleset"
            lead="Published by the server itself, straight from its configuration — so it cannot drift from how the shard actually plays."
          />
          <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 shard ruleset right now." />}

        {!loading && !error && !ruleset && (
          <section className="panel" style={{ padding: 24, textAlign: 'center' }}>
            <p className="sans dim" style={{ margin: 0 }}>
              The shard has not published its ruleset yet.
            </p>
          </section>
        )}

        {!loading && !error && ruleset && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            <Panel title="Shard">
              <Rows
                items={[
                  ['Name', ruleset.shard || null],
                  ['Expansion', ruleset.expansion || null],
                  ['Connect', ruleset.connect || null],
                ]}
              />
            </Panel>

            {ruleset.systems && <Systems systems={ruleset.systems} />}
            {ruleset.caps && <Caps caps={ruleset.caps} />}
            <AccountsAndHousing
              accounts={ruleset.accounts}
              housing={ruleset.housing}
              vetRewards={ruleset.vetRewards}
            />
            {ruleset.champions && <Champions champions={ruleset.champions} />}
            {ruleset.loot && <Felucca loot={ruleset.loot} />}
            {ruleset.vendors && <Vendors vendors={ruleset.vendors} />}
            {ruleset.vvv?.enabled && <Pvp vvv={ruleset.vvv} />}
            {ruleset.schedule && <Schedule schedule={ruleset.schedule} />}
          </div>
        )}
      </div>
    </PublicLayout>
  )
}
