// Shared formatting for shard events — used by the public Shard page, the
// Activity feed, and the admin live feed. One place decides how each kind reads
// and which category/badge it belongs to.

function nameOf(who) {
  if (!who) return 'Someone'
  if (typeof who === 'string') return who
  return who.name || who.acct || 'Someone'
}

const n = (v) => Number(v || 0).toLocaleString()

// A one-line human description of each event kind, keyed by kind. Each formatter
// takes the payload and returns a string. Conditional suffixes are pulled into
// locals so no template literal is nested inside another.
const DESCRIBERS = {
  'vendor.sale': (p) => {
    const qty = p.amount > 1 ? ` ×${p.amount}` : ''
    return `${p.itemType || 'An item'}${qty} sold for ${n(p.price)}gp`
  },
  'player.death': (p) => {
    const by = p.killer ? ` by ${nameOf(p.killer)}` : ''
    return `${nameOf(p.who)} was slain${by}`
  },
  'player.murdered': (p) => {
    const by = p.murderer ? ` by ${nameOf(p.murderer)}` : ''
    return `${nameOf(p.victim)} was murdered${by}`
  },
  'mob.killed': (p) => `${nameOf(p.killer)} killed ${nameOf(p.killed)}`,
  'skill.gain': (p) => {
    const base = p.base != null ? ` (${p.base})` : ''
    return `${nameOf(p.who)} gained ${p.skill}${base}`
  },
  'fame.change': (p) => `${nameOf(p.who)}’s fame changed to ${n(p.new)}`,
  'karma.change': (p) => `${nameOf(p.who)}’s karma changed to ${n(p.new)}`,
  'quest.complete': (p) => `${nameOf(p.who)} completed “${p.quest}”`,
  'house.decay': (p) => {
    const region = p.region ? ` — ${p.region}` : ''
    return `${p.name || 'A house'} is now ${p.to || p.stage}${region}`
  },
  'mob.login': (p) => `${nameOf(p.who)} entered the world`,
  'mob.logout': (p) => `${nameOf(p.who)} left the world`,
  'economy.supply': (p) => `Gold supply: ${n(p.gold)} across ${n(p.accounts)} accounts`,
  'server.hello': (p) => `Shard online — ${n(p.accounts)} accounts, ${n(p.mobiles)} mobiles`,
  'server.shutdown': () => 'Shard shut down',
  'server.crashed': (p) => {
    const err = p.error ? `: ${p.error}` : ''
    return `Shard crashed${err}`
  },
  'champ.update': (p) => {
    const where = p.name || p.type || 'A champion spawn'
    if (p.status === 'active' && p.bossUp) {
      const boss = p.boss ? ` (${p.boss})` : ''
      return `${where}: boss is up${boss}`
    }
    if (p.status === 'active') {
      const level = p.level != null ? ` — level ${p.level}` : ''
      return `${where} is active${level}`
    }
    if (p.status === 'cooldown') return `${where} is on cooldown`
    return `${where} is ${p.status || 'idle'}`
  },
  'champ.remove': () => `A champion spawn ended`,
  // Support (help-page) queue + in-game moderation (admin channel only)
  'page.new': (p) => `New ${p.type || 'help'} page from ${nameOf(p.sender)}`,
  'page.updated': (p) => {
    const claimed = p.handled ? ' (claimed)' : ''
    return `Help page from ${nameOf(p.sender)} updated${claimed}`
  },
  'page.closed': (p) => `Help page ${p.pageId || ''} closed`,
  'admin.audit': (p) => {
    const on = p.target ? ` on ${p.target}` : ''
    const origin = p.origin ? ` [${p.origin}]` : ''
    return `${p.actor || 'Staff'} ${p.action || 'acted'}${on}${origin}`
  },
  // Staff / sensitive (admin channel only)
  'audit.set': (p) =>
    `${nameOf(p.staff) || 'Staff'} set ${p.prop} on ${p.target || p.targetSerial} (${p.old} → ${p.new})`,
  'audit.command': (p) => {
    const args = p.args ? ` ${p.args}` : ''
    return `${nameOf(p.staff) || 'Staff'} ran ${p.command}${args}`
  },
  'cheat.fastwalk': (p) => {
    const ip = p.ip ? ` (${p.ip})` : ''
    return `Fast-walk flagged: ${nameOf(p.who)}${ip}`
  },
  'account.login.attempt': (p) => {
    const ip = p.ip ? ` from ${p.ip}` : ''
    return `Login attempt: ${p.acct}${ip}`
  },
  'gold.change': (p) => {
    const sign = p.delta >= 0 ? '+' : ''
    return `${p.acct}: gold ${sign}${n(p.delta)} → ${n(p.new)}`
  },
}

// A one-line human description of an event. Accepts either a stored event
// (with .payload) or a raw live frame (fields at top level).
export function describe(ev) {
  const fmt = DESCRIBERS[ev.kind]
  return fmt ? fmt(ev.payload || ev) : ev.kind
}

// Category grouping for the filter tabs.
// Vendor sales are intentionally NOT a public category — they are owner-private
// (a linked player sees their own under the portal). The admin live feed still
// describes vendor.sale via describe() below.
export const CATEGORIES = [
  { id: 'all', label: 'All', kinds: null },
  { id: 'pvp', label: 'Deaths & PvP', kinds: ['player.death', 'player.murdered', 'mob.killed'] },
  { id: 'progress', label: 'Progression', kinds: ['skill.gain', 'fame.change', 'karma.change', 'quest.complete'] },
  { id: 'world', label: 'World', kinds: ['house.decay', 'mob.login', 'mob.logout', 'server.hello', 'server.shutdown', 'server.crashed', 'economy.supply'] },
]

const CATEGORY_OF = (() => {
  const m = {}
  for (const c of CATEGORIES) if (c.kinds) for (const k of c.kinds) m[k] = c.id
  return m
})()

export function categoryOf(kind) {
  return CATEGORY_OF[kind] || 'other'
}

// Short badge label for a kind (the part after the dot, title-cased-ish).
export function kindLabel(kind) {
  return String(kind || '').replace(/[._]/g, ' ')
}
