// council-data.jsx
// The 25 Council agents — each one an NFT on Base, each one a smart-wallet voter.
// This file is the single source of truth: roster, sigils, on-chain helpers,
// deterministic stats / agreement values.

// ── On-chain constants ────────────────────────────────────────
const NFT_CONTRACT  = '0x8d5b1246CA5fb193F9E76bAdB4405D5024DD431e';
const COUNCIL_OWNER = '0x9092BC0Fe1540fD34d9786EA11e450c5ed28bb8c';
const BASE_CHAIN    = { name: 'Base', explorer: 'https://basescan.org' };

const basescanContract = () => `${BASE_CHAIN.explorer}/token/${NFT_CONTRACT}`;
const basescanAddr     = (a) => `${BASE_CHAIN.explorer}/address/${a}`;
const basescanTx       = (h) => `${BASE_CHAIN.explorer}/tx/${h}`;

// ── Specialty palette ─────────────────────────────────────────
// Five specialties, each anchored to a QP color. These are the cluster colors
// used in the matrix and filter chips. Individual agents may inherit a
// related but distinct palette color (see AGENTS below).
const SPEC = {
  liquidity:  { label: 'Liquidity',   color: '#23c8e9', soft: 'rgba(35,200,233,0.10)',  tag: 'LIQ' },
  volatility: { label: 'Volatility',  color: '#e72020', soft: 'rgba(231,32,32,0.10)',   tag: 'VOL' },
  pattern:    { label: 'Pattern',     color: '#42f977', soft: 'rgba(66,249,119,0.10)',  tag: 'PAT' },
  sentiment:  { label: 'Sentiment',   color: '#c6207a', soft: 'rgba(198,32,122,0.10)',  tag: 'SEN' },
  risk:       { label: 'Risk',        color: '#b05222', soft: 'rgba(176,82,34,0.10)',   tag: 'RSK' },
};

// ── The roster — 25 named agents ──────────────────────────────
// Names drawn from mythology / archetype so each agent reads as a character.
// Each agent inherits a palette color from the Quantum Palette — the brand's
// own 14 atomic hues, each one carrying a measurable advantage + multiplier.
// `palette` is the QP color name; the agent surfaces its advantage as a stat.
const AGENTS = [
  // Liquidity & Market Structure (1–5) — Calibration-led
  { id: 1,  name: 'ATLAS',      specialty: 'liquidity',  role: 'order-book depth across 10 levels',  palette: 'Calibration Cyan' },
  { id: 2,  name: 'KRAKEN',     specialty: 'liquidity',  role: 'block-trade detection',              palette: 'Coherence Indigo' },
  { id: 3,  name: 'NAUTILUS',   specialty: 'liquidity',  role: 'bid-ask spread monitor',             palette: 'Calibration Cyan' },
  { id: 4,  name: 'OCEAN',      specialty: 'liquidity',  role: 'dark-pool flow tracker',             palette: 'Trunk Wave' },
  { id: 5,  name: 'KELP',       specialty: 'liquidity',  role: 'odd-lot pattern reader',             palette: 'Coherence Indigo' },
  // Volatility (6–10) — Crimson + Shield
  { id: 6,  name: 'HERA',       specialty: 'volatility', role: '30-day realized volatility',         palette: 'Oscillation Crimson' },
  { id: 7,  name: 'STORM',      specialty: 'volatility', role: 'implied vol surface',                palette: 'Shield Barrier' },
  { id: 8,  name: 'IRIS',       specialty: 'volatility', role: 'vol-of-vol regime detector',         palette: 'Oscillation Crimson' },
  { id: 9,  name: 'GALE',       specialty: 'volatility', role: 'intraday range scanner',             palette: 'Shield Barrier' },
  { id: 10, name: 'CALM',       specialty: 'volatility', role: 'quiet-period detector',              palette: 'Oscillation Crimson' },
  // Pattern & Technicals (11–15) — Velocity + Fidelity
  { id: 11, name: 'ARGUS',      specialty: 'pattern',    role: '90-day price action',                palette: 'Velocity Teal' },
  { id: 12, name: 'SCOUT',      specialty: 'pattern',    role: 'moving-average crossover',           palette: 'Fidelity Lime' },
  { id: 13, name: 'ORACLE',     specialty: 'pattern',    role: 'support and resistance',             palette: 'Velocity Teal' },
  { id: 14, name: 'SAGE',       specialty: 'pattern',    role: 'candle pattern library',             palette: 'Fidelity Lime' },
  { id: 15, name: 'PROPHET',    specialty: 'pattern',    role: 'fib retracement reader',             palette: 'Velocity Teal' },
  // News & Sentiment (16–20) — Magenta + Seed
  { id: 16, name: 'ECHO',       specialty: 'sentiment',  role: 'headline sentiment scan',            palette: 'Session Magenta' },
  { id: 17, name: 'HERMES',     specialty: 'sentiment',  role: 'earnings calendar',                  palette: 'Seed Pulse' },
  { id: 18, name: 'RUMOR',      specialty: 'sentiment',  role: 'social sentiment delta',             palette: 'Session Magenta' },
  { id: 19, name: 'BEACON',     specialty: 'sentiment',  role: 'analyst rating changes',             palette: 'Seed Pulse' },
  { id: 20, name: 'SIREN',      specialty: 'sentiment',  role: 'false-alarm filter',                 palette: 'Session Magenta' },
  // Risk & Exposure (21–25) — Consensus Orange + Crown Resonance (OVERSEER)
  { id: 21, name: 'AEGIS',      specialty: 'risk',       role: 'sector concentration',               palette: 'Yield Gold' },
  { id: 22, name: 'NEMESIS',    specialty: 'risk',       role: 'sizing vs daily volume',             palette: 'Consensus Orange' },
  { id: 23, name: 'PROMETHEUS', specialty: 'risk',       role: 'correlation cluster',                palette: 'Budget Seafoam' },
  { id: 24, name: 'STYX',       specialty: 'risk',       role: 'event proximity (Fed · earnings)',   palette: 'Swarm Violet' },
  { id: 25, name: 'OVERSEER',   specialty: 'risk',       role: 'hard refuser of last resort',        palette: 'Crown Resonance' },
];

// ── Deterministic helpers ─────────────────────────────────────
// Same input → same output, every render. Lets the page act as if it's
// reading on-chain data even though it's actually computed locally.
function lcg(seed) {
  let n = seed >>> 0;
  return () => { n = (n * 1103515245 + 12345) >>> 0; return n; };
}

// Produce a 40-hex address that looks indistinguishable from a real Base address.
// In production this is the ERC-6551 token-bound account computed from
// (chainId, NFT_CONTRACT, tokenId, salt) — same shape, same length.
function tbaFor(id) {
  const r = lcg(id * 2654435761 + 0xABCDEF);
  let s = '';
  for (let i = 0; i < 40; i++) s += (r() & 0xf).toString(16);
  // Surface a stable owner prefix so agents look related (same collection)
  return '0x' + s.padStart(40, '0');
}

function fmtAddr(a, lead = 6, tail = 4) {
  if (!a) return '';
  return a.slice(0, 2 + lead) + '…' + a.slice(-tail);
}

// Per-agent stats — stake, accuracy, vote counts, last-vote latency.
// Deterministic from token ID so the page is reproducible.
function statsFor(id) {
  const r = lcg(id * 16807 + 13);
  const f = () => (r() & 0xFFFF) / 0xFFFF;

  const stakeEth = +(0.6 + f() * 4.4).toFixed(2);            // 0.6 – 5.0 ETH
  const accuracy = +(0.78 + f() * 0.17).toFixed(3);          // 78 – 95%
  const voteCount = 800 + Math.floor(f() * 5000);            // 800 – 5800
  const clearedRatio = 0.72 + f() * 0.18;                    // 72 – 90%
  const cleared = Math.floor(voteCount * clearedRatio);
  const refused = voteCount - cleared - Math.floor(voteCount * 0.04);
  const abstained = voteCount - cleared - refused;
  const lastVoteSec = Math.floor(2 + f() * 1700);            // 2 – 1700s ago
  const slashCount  = Math.floor(f() * 4);                    // 0 – 3 historical slashes

  return { stakeEth, accuracy, voteCount, cleared, refused, abstained, lastVoteSec, slashCount };
}

function timeAgo(seconds) {
  if (seconds < 60)      return `${seconds}s ago`;
  if (seconds < 3600)    return `${Math.floor(seconds/60)}m ago`;
  if (seconds < 86400)   return `${Math.floor(seconds/3600)}h ago`;
  return `${Math.floor(seconds/86400)}d ago`;
}

// Pairwise agreement: 1.0 on the diagonal, ~0.70–0.85 same-specialty,
// ~0.55–0.70 cross-specialty. Deterministic and symmetric.
function agreementBetween(a, b) {
  if (a.id === b.id) return 1.0;
  const same = a.specialty === b.specialty;
  const lo = Math.min(a.id, b.id);
  const hi = Math.max(a.id, b.id);
  const key = (lo * 53 + hi) * 2654435761 >>> 0;
  const r = (key & 0xFFFF) / 0xFFFF;
  return same ? 0.70 + r * 0.15 : 0.55 + r * 0.15;
}

// ── Sigil — generative SVG per agent ──────────────────────────
// Synapse-mode constellation glyph. Layered build:
//   1. Outer dashed ring in the agent's core palette color
//   2. Inner faint ring at 55.6% radius
//   3. Constellation field — sparse line graph between deterministic points
//      in the duotone partner color (low opacity, like the brand logo)
//   4. Inner polygon + radiating spokes in the core color
//   5. Center pip in the focus-glow color
function Sigil({ id, color, size = 80, dim = false, fieldColor }) {
  const r        = lcg(id * 257 + 911);
  const sides    = 3 + ((id - 1) % 6);
  const spokes   = 5 + (id % 7);
  const rot      = (id * 11) % 360;
  const inner    = size * 0.18;
  const outer    = size * 0.42;
  const center   = size / 2;
  const op       = dim ? 0.5 : 1;
  const fc       = fieldColor || color;

  // Constellation node positions — fixed deterministic points around the ring
  const nodes = Array.from({ length: 12 }).map((_, i) => {
    const angle = (i / 12) * Math.PI * 2 + (id * 0.27);
    const radius = outer * (0.62 + ((r() & 0xff) / 0xff) * 0.32);
    return { x: center + Math.cos(angle) * radius, y: center + Math.sin(angle) * radius };
  });

  // Choose which node pairs to connect — sparse graph (~14-20 lines)
  const lineCount = 10 + (id % 8);
  const lines = [];
  for (let i = 0; i < lineCount; i++) {
    const a = (i * 5 + id) % nodes.length;
    const b = (i * 7 + id * 3 + 4) % nodes.length;
    if (a === b) continue;
    const weight = i % 2 === 0 ? 1 : 0.5;
    const lineOp = i % 2 === 0 ? 0.18 : 0.10;
    lines.push({ a: nodes[a], b: nodes[b], weight, lineOp });
  }

  const polyPoints = Array.from({ length: sides }).map((_, i) => {
    const a = (i / sides) * Math.PI * 2 - Math.PI / 2 + (rot * Math.PI / 180);
    return `${center + Math.cos(a) * inner},${center + Math.sin(a) * inner}`;
  }).join(' ');

  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}
         style={{ display: 'block', opacity: op }}
         role="img" aria-label={`Agent ${id} sigil`}>
      {/* outer dashed ring — matches the Alpha Agent Logo geometry */}
      <circle cx={center} cy={center} r={outer + size * 0.04} fill="none"
        stroke={color} strokeWidth={size * 0.025} strokeDasharray={`${size * 0.08} ${size * 0.04}`}
        opacity="0.95"/>

      {/* inner faint ring at ~55.6% (logo spec) */}
      <circle cx={center} cy={center} r={outer * 0.78} fill="none"
        stroke={color} strokeWidth="0.75" opacity="0.32"/>

      {/* constellation lines — duotone partner color */}
      {lines.map((ln, i) => (
        <line key={i}
          x1={ln.a.x} y1={ln.a.y} x2={ln.b.x} y2={ln.b.y}
          stroke={fc} strokeWidth={ln.weight} opacity={ln.lineOp}
          strokeLinecap="round"/>
      ))}

      {/* radiating spokes — anchored to the inner polygon */}
      {Array.from({ length: spokes }).map((_, i) => {
        const a = (i / spokes) * Math.PI * 2;
        const x1 = center + Math.cos(a) * (outer * 0.55);
        const y1 = center + Math.sin(a) * (outer * 0.55);
        const x2 = center + Math.cos(a) * (outer * 0.74);
        const y2 = center + Math.sin(a) * (outer * 0.74);
        return <line key={`sp${i}`} x1={x1} y1={y1} x2={x2} y2={y2}
          stroke={color} strokeWidth="0.7" opacity="0.55"/>;
      })}

      {/* inner polygon — core mark */}
      <polygon points={polyPoints}
        fill={color} fillOpacity="0.22"
        stroke={color} strokeWidth="1.4" opacity="0.95"/>

      {/* center pip */}
      <circle cx={center} cy={center} r={size * 0.028} fill={color}/>
      <circle cx={center} cy={center} r={size * 0.014} fill="#fff" opacity="0.85"/>
    </svg>
  );
}

// ── Agent lookup ──────────────────────────────────────────────
function agentById(id)        { return AGENTS.find(a => a.id === id); }
function agentsBySpec(spec)   { return AGENTS.filter(a => a.specialty === spec); }
const ALL_SPECS              = ['liquidity', 'volatility', 'pattern', 'sentiment', 'risk'];

// ── Aggregate roster stats ────────────────────────────────────
function aggregateStats() {
  let totalStake = 0, totalVotes = 0, totalSlashes = 0, totalCleared = 0;
  for (const a of AGENTS) {
    const s = statsFor(a.id);
    totalStake += s.stakeEth;
    totalVotes += s.voteCount;
    totalSlashes += s.slashCount;
    totalCleared += s.cleared;
  }
  return {
    totalAgents: AGENTS.length,
    totalStake: +totalStake.toFixed(2),
    totalVotes,
    totalCleared,
    totalSlashes,
    avgAccuracy: +(AGENTS.reduce((s, a) => s + statsFor(a.id).accuracy, 0) / AGENTS.length).toFixed(3),
  };
}

// ── Palette resolution per agent ─────────────────────────────
// Each agent's `palette` field names a QP atomic color. We resolve it lazily
// to handle script load order; QP and its helpers live in palette-qp.jsx.
function agentPaletteColor(agent) {
  if (typeof qpByName !== 'function') return SPEC[agent.specialty].color;
  const c = qpByName(agent.palette);
  return c ? c.hex : SPEC[agent.specialty].color;
}
function agentPaletteEntry(agent) {
  if (typeof qpByName !== 'function') return null;
  return qpByName(agent.palette);
}
function agentFieldKey(agent) {
  const entry = agentPaletteEntry(agent);
  if (!entry || typeof qpFieldFor !== 'function') return null;
  return qpFieldFor(entry);
}
function agentFieldColor(agent) {
  const key = agentFieldKey(agent);
  if (!key || typeof qpDuotone !== 'function') return null;
  const duo = qpDuotone(key);
  return duo ? duo[1] : null;
}

Object.assign(window, {
  NFT_CONTRACT, COUNCIL_OWNER, BASE_CHAIN,
  basescanContract, basescanAddr, basescanTx,
  SPEC, ALL_SPECS, AGENTS,
  tbaFor, fmtAddr, statsFor, timeAgo, agreementBetween,
  agentById, agentsBySpec, aggregateStats,
  agentPaletteColor, agentPaletteEntry, agentFieldKey, agentFieldColor,
  Sigil,
});
