/* global React */
// Shared data, helpers, icons, and small primitive components

const { useState, useEffect, useMemo, useRef, createContext, useContext } = React;

// ─────────────────────────────────────────────────────────────────────
// Mock data
// ─────────────────────────────────────────────────────────────────────

const POOLS = [
  {
    id: "stgy",
    name: "LAUNKR Token",
    symbol: "STGY",
    decimals: 8,
    principal: "ST2ABWV7…KJJYWE.strategy-token",
    mode: "graduated",                  // direct | bonding | graduated
    stxReserve: 3500,
    tokenReserve: 612_400_000,
    tvl: 3500,
    vol24: 1500,
    burn24: 312_540,
    burnTotal: 8_120_904,
    price: 5.71e-6,                     // STX per token
    change24: +6.4,
    active: true,
    createdAt: "2026-02-11",
    feeReceiver: "ST3RJ9…NQGV1A",
  },
  {
    id: "hash",
    name: "Hash Stones",
    symbol: "HASH",
    decimals: 6,
    principal: "ST2ABWV7…KJJYWE.hash-token",
    mode: "bonding",
    stxReserve: 0,
    tokenReserve: 0,
    virtualStx: 1000,
    virtualToken: 800_000_000,
    bondedStxCollected: 700,
    graduationThreshold: 2000,
    bondedTokensSold: 197_400_000,
    tvl: 700,
    vol24: 180,
    burn24: 0,
    burnTotal: 0,
    price: 2.82e-6,
    change24: +28.2,
    active: true,
    createdAt: "2026-04-22",
    feeReceiver: "ST1F0X…W3R7QQ",
  },
  {
    id: "satk",
    name: "Satoshi Kernel",
    symbol: "SATK",
    decimals: 8,
    principal: "ST2ABWV7…KJJYWE.satk-token",
    mode: "direct",
    stxReserve: 950,
    tokenReserve: 84_300_000,
    tvl: 950,
    vol24: 500,
    burn24: 41_220,
    burnTotal: 1_004_500,
    price: 1.13e-5,
    change24: -3.1,
    active: true,
    createdAt: "2026-03-09",
    feeReceiver: "ST2QBC…AAA12P",
  },
  {
    id: "ordl",
    name: "Ordlite",
    symbol: "ORDL",
    decimals: 6,
    principal: "ST2ABWV7…KJJYWE.ordlite",
    mode: "bonding",
    stxReserve: 0,
    tokenReserve: 0,
    virtualStx: 500,
    virtualToken: 1_000_000_000,
    bondedStxCollected: 250,
    graduationThreshold: 2000,
    bondedTokensSold: 73_840_000,
    tvl: 250,
    vol24: 35,
    burn24: 0,
    burnTotal: 0,
    price: 8.10e-7,
    change24: +12.0,
    active: true,
    createdAt: "2026-04-29",
    feeReceiver: "ST1AVE…HHQQ19",
  },
  {
    id: "rune",
    name: "Runebound",
    symbol: "RUNE",
    decimals: 8,
    principal: "ST2ABWV7…KJJYWE.runebound",
    mode: "graduated",
    stxReserve: 1700,
    tokenReserve: 318_220_400,
    tvl: 1700,
    vol24: 400,
    burn24: 87_900,
    burnTotal: 2_344_900,
    price: 5.34e-6,
    change24: -1.7,
    active: false,
    createdAt: "2026-01-18",
    feeReceiver: "ST5RUN…BCDE71",
  },
];

const PROTOCOL_STATS = {
  totalVolume: 19500,          // STX
  totalBurned: 18_740_244,     // tokens, abstract
  activePools: 4,
  totalPools: 5,
  feesAccrued: 180,            // STX
};

const HISTORY = [
  { kind: "swap", side: "buy",  pool: "stgy", who: "ST2X…RBTC", stx: 35, tokens: 5_834_211, fee: 1.75, time: "2m ago", txid: "0x9a…f2c1" },
  { kind: "swap-and-burn",      pool: "stgy", who: "ST7BU…RNB0T", stx: 10, tokens: 1_741_004, fee: 0,    time: "4m ago", txid: "0x12…aa90" },
  { kind: "swap", side: "sell", pool: "stgy", who: "STA1…N9X1", stx: 70, tokens: 11_900_000, fee: 3.5, time: "9m ago", txid: "0xfe…0011" },
  { kind: "swap", side: "buy",  pool: "hash", who: "STBOND…001", stx: 200, tokens: 71_000_000, fee: 2, time: "14m ago", txid: "0x44…c0d1" },
  { kind: "swap-and-burn",      pool: "stgy", who: "ST7BU…RNB0T", stx: 17, tokens: 2_891_400, fee: 0,    time: "26m ago", txid: "0x12…ab10" },
  { kind: "pool-active",        pool: "rune", who: "Protocol admin", time: "1h ago", state: false, txid: "0x88…dada" },
  { kind: "swap", side: "buy",  pool: "satk", who: "ST3JK…11ZZ", stx: 15, tokens: 1_230_000, fee: 0.75, time: "1h ago", txid: "0xa1…b220" },
  { kind: "graduated",          pool: "stgy", who: "—", time: "9d ago", txid: "0x55…f100" },
];

const ALLOWLIST = {
  principals: [
    { p: "ST2ABWV7…KJJYWE.lp-singleton-v6", note: "AMM contract", added: "2026-05-08" },
    { p: "ST7BURN…AGG", note: "Burn aggregator", added: "2026-03-04" },
    { p: "STDF…YIELDV", note: "Yield router", added: "2026-04-01" },
  ],
  hashes: [
    { h: "0x5352…fbb9 …defe6", note: "lp-singleton-v6 source", added: "2026-05-08" },
    { h: "0x9bd3…a814 …2c40", note: "burn-router-v2 source",  added: "2026-03-04" },
  ],
  pending: [
    { kind: "principal", value: "ST5NEW…ROUTR", proposer: "Signer 2 of 3", confirmedBy: 1, required: 2, action: "add" },
  ],
};

// Network-scoped globals. setNetworkGlobals(cfg) mutates window.* so legacy
// callers reading window.NETWORK / window.DEPLOYER / window.SINGLETON etc.
// transparently pick up the active network. NetworkProvider in main.jsx calls
// this on mount and whenever the user switches network; the initial call
// below bootstraps the globals for first paint before the provider mounts.
//
// window.CFG holds the full cfg object so new callers can grab everything in
// one read instead of plumbing through React context.
//
// DEMO_TOKEN_PRINCIPAL is the per-network demo pool token (cfg.deployer +
// cfg.suffix). On testnet it points to the pool created by
// scripts/deploy-demo-pool.mjs; on mainnet to the mainnet-demo equivalent.
// PoolDetail uses 30s get-pool polling against the active singleton.
function setNetworkGlobals(cfg) {
  window.NETWORK              = cfg.id;
  window.DEPLOYER             = cfg.deployer;
  window.TRAIT                = cfg.trait;
  window.SINGLETON            = cfg.singleton;
  window.TEMPLATE             = cfg.tokenTemplate;
  window.DEMO_TOKEN_PRINCIPAL = cfg.demoTokenPrincipal;
  window.CFG                  = cfg;
}
window.setNetworkGlobals = setNetworkGlobals;
setNetworkGlobals(window.NetworkConfig.get(window.NetworkConfig.DEFAULT));

// ─────────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────────

const fmt = {
  stx:      (n) => n.toLocaleString(undefined, { maximumFractionDigits: 4, minimumFractionDigits: 2 }),
  stxShort: (n) => n.toLocaleString(undefined, { maximumFractionDigits: 2 }),
  num:      (n) => n.toLocaleString(undefined, { maximumFractionDigits: 0 }),
  big:      (n) => {
    if (n >= 1e9) return (n / 1e9).toFixed(2) + "B";
    if (n >= 1e6) return (n / 1e6).toFixed(2) + "M";
    if (n >= 1e3) return (n / 1e3).toFixed(2) + "K";
    return n.toFixed(0);
  },
  pct:      (n) => (n > 0 ? "+" : "") + n.toFixed(2) + "%",
  micro:    (n, decimals = 6) => (n * 10 ** decimals).toLocaleString(undefined, { maximumFractionDigits: 0 }),
};

const ERROR_MAP = {
  u100: "Recipient not allowed — token allowlist rejected the destination.",
  u101: "Not the allowlist admin (token-level governance, distinct from singleton's u250).",
  u107: "Not the pending allowlist admin — only the proposed admin can accept.",
  u108: "No pending allowlist admin to accept — call set-pending-allowlist-admin first.",
  u200: "Pool already exists for this token.",
  u201: "Token not ours — code-hash mismatch with the template.",
  u202: "Invalid token contract.",
  u203: "STX seed below 100 STX minimum.",
  u204: "Zero supply — supply must be > 0.",
  u205: "Decimals out of range — max is 18.",
  u206: "Singleton paused — global pause is active.",
  u207: "Virtual STX below 500 STX minimum (bonding).",
  u208: "Graduation threshold below 2,000 STX minimum (bonding).",
  u209: "Virtual reserve too low for this supply — increase virtual-stx or reduce supply.",
  u210: "Pool not found.",
  u211: "Pool paused — this pool is frozen.",
  u212: "Deadline passed — sign and submit again.",
  u213: "Slippage exceeded — try a wider tolerance or a smaller amount.",
  u214: "Zero input — amount must be greater than 0.",
  u215: "Insufficient liquidity for this trade.",
  u216: "Insufficient real STX in bonding curve — try a smaller sell.",
  u217: "Not graduated — swap-and-burn is disabled during bonding.",
  u230: "Not the current fee-receiver.",
  u231: "Not the pending fee-receiver.",
  u232: "No pending fee-receiver to accept.",
  u250: "Not the protocol admin (singleton-level pause/freeze; distinct from token's u101).",
  u251: "Not the pending admin.",
  u252: "No pending admin to accept.",
};
const formatContractError = (code) => ERROR_MAP[code] || `Unknown error (${code}).`;

// XYK quote with mode-aware reserves and fees
function quote({ pool, mode, dir, amountIn }) {
  const a = parseFloat(amountIn);
  if (!a || a <= 0) return null;
  let xR, yR, feeBps;
  if (mode === "bonding") {
    xR = pool.virtualStx + pool.bondedStxCollected;
    yR = pool.virtualToken - pool.bondedTokensSold;
    feeBps = 100; // 1%
  } else {
    xR = pool.stxReserve;
    yR = pool.tokenReserve;
    feeBps = 500; // 5%
  }
  if (dir === "buy") {
    const inAfterFee = a * (1 - feeBps / 10000);
    const out = (yR * inAfterFee) / (xR + inAfterFee);
    return { out, feeBps, inAfterFee, priceImpact: (a / xR) * 100 };
  } else {
    // sell tokens for STX
    const grossIn = a;
    const dx_gross_xyk = (xR * grossIn) / (yR + grossIn);
    if (mode === "bonding" && dx_gross_xyk > pool.bondedStxCollected) return null;
    const out = dx_gross_xyk * (1 - feeBps / 10000);
    return { out, feeBps, gross: dx_gross_xyk, priceImpact: (a / yR) * 100 };
  }
}

// ─────────────────────────────────────────────────────────────────────
// Icons (line-drawn, monospace-aligned)
// ─────────────────────────────────────────────────────────────────────

const I = {
  swap: (p={}) => <svg width="16" height="16" viewBox="0 0 16 16" fill="none" {...p}><path d="M3 5h9m0 0L9 2m3 3L9 8M13 11H4m0 0l3-3m-3 3l3 3" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  fire: (p={}) => <svg width="16" height="16" viewBox="0 0 16 16" fill="none" {...p}><path d="M8 1.5s1.5 2 1.5 3.75S8 7.5 8 7.5s-1.5-.5-1.5-2.25S8 1.5 8 1.5z" stroke="currentColor" strokeWidth="1.2"/><path d="M3.5 9.5C3.5 6.5 6 5.5 6 5.5s.5 2 2 2 2-2 2-2 2.5 1 2.5 4-2 4.5-4.5 4.5S3.5 12.5 3.5 9.5z" stroke="currentColor" strokeWidth="1.2"/></svg>,
  plus: (p={}) => <svg width="16" height="16" viewBox="0 0 16 16" fill="none" {...p}><path d="M8 3v10M3 8h10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/></svg>,
  shield: (p={}) => <svg width="16" height="16" viewBox="0 0 16 16" fill="none" {...p}><path d="M8 1.5l5 1.6V8c0 3-2.4 5.4-5 6.5C5.4 13.4 3 11 3 8V3.1l5-1.6z" stroke="currentColor" strokeWidth="1.2"/></svg>,
  list: (p={}) => <svg width="16" height="16" viewBox="0 0 16 16" fill="none" {...p}><path d="M3 4h10M3 8h10M3 12h10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/></svg>,
  arrow: (p={}) => <svg width="14" height="14" viewBox="0 0 16 16" fill="none" {...p}><path d="M4 8h8m0 0L8.5 4.5M12 8l-3.5 3.5" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/></svg>,
  ext: (p={}) => <svg width="12" height="12" viewBox="0 0 16 16" fill="none" {...p}><path d="M6 3H4a1 1 0 00-1 1v8a1 1 0 001 1h8a1 1 0 001-1v-2M9 3h4v4M13 3l-6 6" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  copy: (p={}) => <svg width="12" height="12" viewBox="0 0 16 16" fill="none" {...p}><rect x="5" y="5" width="8" height="8" rx="1" stroke="currentColor" strokeWidth="1.3"/><path d="M3 11V4a1 1 0 011-1h7" stroke="currentColor" strokeWidth="1.3"/></svg>,
  check: (p={}) => <svg width="12" height="12" viewBox="0 0 16 16" fill="none" {...p}><path d="M3 8.5L6.5 12L13 5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  warn: (p={}) => <svg width="14" height="14" viewBox="0 0 16 16" fill="none" {...p}><path d="M8 2l6.5 11H1.5L8 2z" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round"/><path d="M8 6.5v3.5M8 11.5v.5" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/></svg>,
  pause: (p={}) => <svg width="14" height="14" viewBox="0 0 16 16" fill="none" {...p}><rect x="4" y="3" width="3" height="10" stroke="currentColor" strokeWidth="1.3"/><rect x="9" y="3" width="3" height="10" stroke="currentColor" strokeWidth="1.3"/></svg>,
  user: (p={}) => <svg width="14" height="14" viewBox="0 0 16 16" fill="none" {...p}><circle cx="8" cy="6" r="2.5" stroke="currentColor" strokeWidth="1.3"/><path d="M3 14c0-2.5 2.2-4 5-4s5 1.5 5 4" stroke="currentColor" strokeWidth="1.3"/></svg>,
  multisig: (p={}) => <svg width="14" height="14" viewBox="0 0 16 16" fill="none" {...p}><circle cx="5" cy="6" r="2" stroke="currentColor" strokeWidth="1.3"/><circle cx="11" cy="6" r="2" stroke="currentColor" strokeWidth="1.3"/><path d="M2 13c.5-1.5 1.7-2.5 3-2.5s2.5 1 3 2.5M8 13c.5-1.5 1.7-2.5 3-2.5s2.5 1 3 2.5" stroke="currentColor" strokeWidth="1.3"/></svg>,
};

// ─────────────────────────────────────────────────────────────────────
// Small primitives
// ─────────────────────────────────────────────────────────────────────

function ModeBadge({ mode, paused }) {
  if (paused) return <span className="badge badge-paused badge-dot">PAUSED</span>;
  const map = {
    direct:     { c: "badge-direct",    l: "DIRECT" },
    bonding:    { c: "badge-bonding",   l: "BONDING" },
    graduated:  { c: "badge-graduated", l: "GRADUATED" },
  };
  const v = map[mode] || map.direct;
  return <span className={"badge " + v.c}>{v.l}</span>;
}

function Mono({ children, className="" }) {
  return <span className={"mono " + className}>{children}</span>;
}

function Hash({ children, copy=true }) {
  const [c, setC] = useState(false);
  return (
    <span className="mono dim" style={{ display:"inline-flex", alignItems:"center", gap:6, fontSize:12 }}>
      {children}
      {copy && (
        <button className="btn-ghost" style={{ padding:"2px 4px", borderRadius:4, fontSize:11 }}
          onClick={() => { setC(true); setTimeout(()=>setC(false), 1200); }}>
          {c ? <I.check/> : <I.copy/>}
        </button>
      )}
    </span>
  );
}

function Stat({ label, value, sub, accent }) {
  return (
    <div className="num-stack">
      <div className="lbl">{label}</div>
      <div className="big" style={accent ? { color: accent } : null}>{value}</div>
      {sub && <div className="sub">{sub}</div>}
    </div>
  );
}

// Sparkline / mini line chart
function Spark({ data, color="var(--purple)", height=36, width=120, fill=true }) {
  const min = Math.min(...data), max = Math.max(...data);
  const span = max - min || 1;
  const pts = data.map((v, i) => [(i/(data.length-1))*width, height - ((v-min)/span)*height]);
  const path = "M " + pts.map(p => p.map(n=>n.toFixed(2)).join(",")).join(" L ");
  const area = path + ` L ${width},${height} L 0,${height} Z`;
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} style={{ display:"block" }}>
      {fill && <path d={area} fill={color} opacity=".12" />}
      <path d={path} stroke={color} strokeWidth="1.4" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

// Full line chart with axis
function LineChart({ data, color, height=180, label, valueFmt = (v)=>v.toFixed(3), fill=true }) {
  const w = 600, h = height;
  const pad = { l: 44, r: 12, t: 14, b: 22 };
  const min = Math.min(...data), max = Math.max(...data);
  const span = max - min || 1;
  const pts = data.map((v, i) => [
    pad.l + (i/(data.length-1)) * (w - pad.l - pad.r),
    pad.t + (1 - (v-min)/span) * (h - pad.t - pad.b)
  ]);
  const path = "M " + pts.map(p => p.map(n=>n.toFixed(2)).join(",")).join(" L ");
  const area = path + ` L ${w-pad.r},${h-pad.b} L ${pad.l},${h-pad.b} Z`;
  const ticks = [max, min + span*.5, min];
  return (
    <svg viewBox={`0 0 ${w} ${h}`} width="100%" preserveAspectRatio="none" style={{ display:"block" }}>
      <defs>
        <linearGradient id={`g-${label}`} x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.22"/>
          <stop offset="100%" stopColor={color} stopOpacity="0"/>
        </linearGradient>
      </defs>
      {ticks.map((t, i) => {
        const y = pad.t + (i/(ticks.length-1)) * (h - pad.t - pad.b);
        return (
          <g key={i}>
            <line x1={pad.l} x2={w-pad.r} y1={y} y2={y} stroke="var(--line)" strokeDasharray="2 3"/>
            <text x={pad.l-8} y={y+3} textAnchor="end" fill="var(--ink-3)" fontSize="10" fontFamily="var(--mono)">{valueFmt(t)}</text>
          </g>
        );
      })}
      {fill && <path d={area} fill={`url(#g-${label})`}/>}
      <path d={path} stroke={color} strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

function BarsChart({ data, color, height=180, label, valueFmt = (v)=>v.toFixed(2) }) {
  const w = 600, h = height;
  const pad = { l: 44, r: 12, t: 14, b: 22 };
  const max = Math.max(...data) || 1;
  const bw = (w - pad.l - pad.r) / data.length;
  const ticks = [max, max*.5, 0];
  return (
    <svg viewBox={`0 0 ${w} ${h}`} width="100%" preserveAspectRatio="none" style={{ display:"block" }}>
      {ticks.map((t, i) => {
        const y = pad.t + (i/(ticks.length-1)) * (h - pad.t - pad.b);
        return (
          <g key={i}>
            <line x1={pad.l} x2={w-pad.r} y1={y} y2={y} stroke="var(--line)" strokeDasharray="2 3"/>
            <text x={pad.l-8} y={y+3} textAnchor="end" fill="var(--ink-3)" fontSize="10" fontFamily="var(--mono)">{valueFmt(t)}</text>
          </g>
        );
      })}
      {data.map((v, i) => {
        const barH = (v / max) * (h - pad.t - pad.b);
        const x = pad.l + i*bw + 1.5;
        const y = h - pad.b - barH;
        return <rect key={i} x={x} y={y} width={Math.max(0, bw-3)} height={barH} fill={color} opacity=".85" rx="1"/>;
      })}
    </svg>
  );
}

// Generates pseudo-random series
function gen(n, base, vol, drift=0, seed=1) {
  let s = seed * 9301 + 49297;
  const out = [];
  let v = base;
  for (let i = 0; i < n; i++) {
    s = (s * 9301 + 49297) % 233280;
    const r = (s / 233280 - 0.5) * 2;
    v = Math.max(0.0001, v + r*vol + drift);
    out.push(v);
  }
  return out;
}

// Export for other scripts. The six network-scoped globals (NETWORK, DEPLOYER,
// TRAIT, SINGLETON, TEMPLATE, DEMO_TOKEN_PRINCIPAL) are no longer module-level
// constants -- setNetworkGlobals assigns them above, and re-assigns when the
// user switches network.
Object.assign(window, {
  POOLS, PROTOCOL_STATS, HISTORY, ALLOWLIST,
  fmt, formatContractError, quote,
  I, ModeBadge, Mono, Hash, Stat, Spark, LineChart, BarsChart, gen,
  setNetworkGlobals,
});
