// assets/data.jsx — realistic mock data for shopquantum admin
// All grounded in the spec: Bedrock models, TS errors, hook FP loops,
// merchants cloning Shopify themes, etc.

// ─── Seeded RNG so data is stable across renders ──────────────────────────
function seedRand(seed) {
  let s = seed;
  return () => { s = (s * 1664525 + 1013904223) % 4294967296; return s / 4294967296; };
}

const R = seedRand(42);
const ri = (a, b) => Math.floor(R() * (b - a + 1)) + a;
const pick = (arr) => arr[Math.floor(R() * arr.length)];
const ulid = () => {
  // pseudo-ULID: 26 chars, starts with timestamp prefix like 01KRSY...
  const cs = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
  let s = '01KR';
  for (let i = 0; i < 22; i++) s += cs[Math.floor(R() * cs.length)];
  return s;
};
const sessId = () => {
  const cs = '0123456789abcdef';
  let s = '';
  for (let i = 0; i < 32; i++) s += cs[Math.floor(R() * cs.length)];
  return s.slice(0, 8) + '-' + s.slice(8, 12) + '-' + s.slice(12, 16) + '-' + s.slice(16, 20) + '-' + s.slice(20);
};

// ─── Constants ────────────────────────────────────────────────────────────
const MODELS = [
  { name: 'claude-sonnet-4-5', short: 'sonnet-4.5', in: 0.003, out: 0.015, cIn: 0.000375, cOut: 0.00375 },
  { name: 'claude-haiku-4-5',  short: 'haiku-4.5',  in: 0.0008, out: 0.004, cIn: 0.0001, cOut: 0.001 },
  { name: 'claude-opus-4',     short: 'opus-4',     in: 0.015, out: 0.075, cIn: 0.001875, cOut: 0.01875 },
];

const TEMPLATE_KINDS = [
  { name: 'shopify-clone',    color: 'var(--chart-1)' },
  { name: 'landing-page',     color: 'var(--chart-2)' },
  { name: 'product-page',     color: 'var(--chart-3)' },
  { name: 'platform-dtc',     color: 'var(--chart-4)' },
  { name: 'onlypages-mini',   color: 'var(--chart-5)' },
  { name: 'collection-page',  color: 'var(--chart-6)' },
];

const APPS = ['shopquantum', 'platformdtc', 'onlypages'];

const MERCHANTS = [
  'phuc.nguyen@bloom-cosmetics.vn',
  'linh.tran@artisan-craft.co',
  'minh.le@coffeebar.io',
  'thao.pham@dailyessentials.shop',
  'duc.vo@tryblossom.com',
  'an.bui@northlight.studio',
  'kim.dang@kitchenkit.co',
  'huong.le@petplace.vn',
  'son.nguyen@homestay-saigon.com',
  'mai.tran@yogahaus.io',
  'tuan.pham@gearloft.shop',
  'nga.do@plantfriends.co',
  'hai.nguyen@stationerystore.vn',
  'binh.le@speakeasy-bar.com',
  'quan.tran@lensandlight.studio',
  'thu.vo@studiocandle.shop',
  'lan.pham@silkroad-jewels.co',
  'hung.dang@runclub.io',
];

const PROJECT_NAMES = [
  'clone tryblossom',
  'clone allbirds',
  'clone gymshark',
  'clone glossier',
  'bloom cosmetics homepage',
  'artisan craft v2',
  'coffeebar landing',
  'northlight studio portfolio',
  'kitchenkit product page',
  'yogahaus collection',
  'gearloft hero rebuild',
  'plantfriends product detail',
  'speakeasy bar landing',
  'silkroad jewels v3',
  'runclub onboarding',
  'lensandlight gallery',
  'studiocandle subscription',
  'petplace breed picker',
  'stationerystore homepage',
  'homestay saigon listing',
];

const TOOLS = ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep', 'WebFetch', 'TodoWrite', 'NotebookEdit'];

const HOOKS = [
  { name: 'block_inline_styles',     short: 'inline-styles',  decisionMix: { deny: 0.4, soft: 0.5, info: 0.1 } },
  { name: 'enforce_tailwind_tokens', short: 'tw-tokens',      decisionMix: { deny: 0.3, soft: 0.6, info: 0.1 } },
  { name: 'block_console_logs',      short: 'no-console',     decisionMix: { deny: 0.1, soft: 0.85, info: 0.05 } },
  { name: 'require_alt_text',        short: 'alt-text',       decisionMix: { deny: 0.15, soft: 0.75, info: 0.1 } },
  { name: 'no_external_fetch',       short: 'no-fetch',       decisionMix: { deny: 0.7, soft: 0.2, info: 0.1 } },
  { name: 'block_jsx_in_loops',      short: 'no-jsx-loops',   decisionMix: { deny: 0.5, soft: 0.4, info: 0.1 } },
  { name: 'enforce_a11y_button',     short: 'a11y-btn',       decisionMix: { deny: 0.2, soft: 0.7, info: 0.1 } },
  { name: 'block_deprecated_apis',   short: 'deprecated',     decisionMix: { deny: 0.4, soft: 0.5, info: 0.1 } },
  { name: 'tailwind_arbitrary_warn', short: 'tw-arbitrary',   decisionMix: { deny: 0.05, soft: 0.9, info: 0.05 } },
  { name: 'block_secret_strings',    short: 'no-secrets',     decisionMix: { deny: 0.95, soft: 0.04, info: 0.01 } },
  { name: 'enforce_react_keys',      short: 'react-keys',     decisionMix: { deny: 0.5, soft: 0.45, info: 0.05 } },
  { name: 'block_dangerous_html',    short: 'no-dangerhtml',  decisionMix: { deny: 0.9, soft: 0.08, info: 0.02 } },
];

const TS_ERRORS = [
  { code: 'TS2352', msg: "Conversion of type 'string' to type 'Theme' may be a mistake" },
  { code: 'TS2339', msg: "Property '_veidx' does not exist on type 'ProductCard'" },
  { code: 'TS2741', msg: "Property 'altText' is missing in type 'ImageProps'" },
  { code: 'TS2322', msg: "Type 'string' is not assignable to type 'ColorToken'" },
  { code: 'TS7053', msg: "Element implicitly has an 'any' type" },
  { code: 'TS2304', msg: "Cannot find name 'cn'" },
  { code: 'TS2769', msg: "No overload matches this call" },
  { code: 'TS2554', msg: "Expected 2 arguments, but got 1" },
];

const HOOK_SAMPLES = {
  block_inline_styles: 'Inline style detected in <div style={{...}}>. Use Tailwind utility classes or a CSS module.',
  enforce_tailwind_tokens: "Arbitrary value 'text-[#1a2b3c]' detected. Use design token text-brand-primary instead.",
  block_console_logs: 'console.log() found in production code path at src/components/Cart.tsx:42',
  require_alt_text: "<img> missing alt attribute (src='/hero.png').",
  no_external_fetch: 'fetch() to external URL https://api.fakestore.com — blocked.',
  block_jsx_in_loops: 'Detected JSX construction inside .map() without key prop. Extract to component.',
  enforce_a11y_button: 'Clickable <div> without role/keyboard handler. Use <button>.',
  block_deprecated_apis: 'componentWillMount is deprecated. Use useEffect/constructor.',
  tailwind_arbitrary_warn: 'Arbitrary value bg-[rgb(255,170,0)] — consider design token bg-warning.',
  block_secret_strings: 'Suspected API key pattern in source. Remove before commit.',
  enforce_react_keys: 'Missing key prop in iterated <li>. Use stable id.',
  block_dangerous_html: 'dangerouslySetInnerHTML detected without sanitization.',
};

// ─── Sessions ─────────────────────────────────────────────────────────────
function mkSession(i) {
  const startedAt = Date.now() - ri(0, 24 * 3600 * 1000);
  const tk = pick(TEMPLATE_KINDS);
  const model = pick(MODELS);
  const dur = pick([45, 180, 320, 480, 720, 900, 1200, 1480, 1800, 2100]) + ri(-30, 90);
  const statusPool = ['running', 'completed', 'completed', 'completed', 'failed', 'completed'];
  const status = i < 3 ? 'running' : pick(statusPool);
  const toolCalls = ri(8, 124);
  const inTok = ri(8000, 180000);
  const outTok = ri(1500, 24000);
  const cacheReadTok = Math.floor(inTok * (0.4 + R() * 0.4));
  const cacheCreateTok = Math.floor(inTok * 0.1);
  const cost = (inTok / 1000 * model.in) + (outTok / 1000 * model.out) + (cacheReadTok / 1000 * model.cIn) + (cacheCreateTok / 1000 * model.cOut);
  return {
    id: sessId(),
    project_id: ulid(),
    project_name: pick(PROJECT_NAMES),
    template_kind: tk.name,
    template_color: tk.color,
    app: pick(APPS),
    merchant: pick(MERCHANTS),
    started_at: startedAt,
    duration: dur,
    status,
    tool_calls: toolCalls,
    cost,
    model: model.short,
    model_full: model.name,
    tokens: { in: inTok, out: outTok, cacheRead: cacheReadTok, cacheCreate: cacheCreateTok },
    hooks_total: ri(3, 48),
    hooks_deny: ri(0, 6),
    hooks_soft: ri(2, 24),
    error_count: status === 'failed' ? ri(1, 4) : ri(0, 2),
    loop_detected: R() < 0.2,
    files_touched: ri(2, 18),
  };
}
const SESSIONS = Array.from({ length: 124 }, (_, i) => mkSession(i)).sort((a, b) => b.started_at - a.started_at);

// ─── Timeline events for a session ────────────────────────────────────────
function mkSessionEvents(sess) {
  const events = [];
  let t = 0;
  events.push({ id: 'sys-0', type: 'system', t, content: 'Session started. Loaded system prompt, project context (3.2k tokens cached), 12 hook scripts.' });
  t += 0.4;
  events.push({ id: 'u-0', type: 'user', t,
    content: `Build a homepage clone of ${sess.project_name.replace('clone ', '')} matching the design — hero, featured products grid (6 items), newsletter, footer. Use our Tailwind preset and product card from /shared/.`
  });
  t += ri(2, 6);
  events.push({ id: 'a-0', type: 'assistant', t,
    content: "I'll start by reading the existing shared components and template structure, then plan the page sections.",
    tokens: { in: 8200, out: 320 },
  });
  t += 0.6;

  const files = ['src/pages/index.tsx', 'src/components/Hero.tsx', 'src/components/ProductGrid.tsx', 'src/components/Newsletter.tsx', 'src/components/Footer.tsx', 'shared/ProductCard.tsx'];
  files.slice(0, 3).forEach((f, i) => {
    events.push({ id: `tc-r${i}`, type: 'tool-call', t, tool: 'Read', args: { path: f }, durationMs: ri(40, 180) });
    t += 0.15;
    events.push({ id: `tr-r${i}`, type: 'tool-result', t, tool: 'Read', ok: true, content: `// ${f}\nimport { ... } from '@/shared'\n// ${ri(40, 320)} lines\n` });
    t += 0.05;
  });

  events.push({ id: 'a-1', type: 'assistant', t,
    content: 'The structure looks good — I see the shared `ProductCard` accepts `{ product, variant }`. I\'ll create the page and section components now.',
    tokens: { in: 12400, out: 410 },
  });
  t += 0.4;

  // Write Hero
  events.push({ id: 'tc-w1', type: 'tool-call', t, tool: 'Write', args: { path: 'src/components/Hero.tsx' }, durationMs: ri(80, 260) });
  t += 0.2;
  events.push({ id: 'h-1', type: 'hook-soft', t, hook: 'enforce_tailwind_tokens',
    message: HOOK_SAMPLES.enforce_tailwind_tokens, file: 'src/components/Hero.tsx', decision: 'soft' });
  t += 0.05;
  events.push({ id: 'tr-w1', type: 'tool-result', t, tool: 'Write', ok: true, content: 'File written. 1 hook soft-warning (continued).' });
  t += ri(2, 5);

  // Now the famous FP loop pattern: tw-arbitrary fires repeatedly on the same file
  if (sess.loop_detected) {
    for (let i = 0; i < 5; i++) {
      events.push({ id: `tc-l${i}`, type: 'tool-call', t, tool: 'Edit', args: { path: 'src/components/ProductGrid.tsx', edit: `arbitrary-${i}` }, durationMs: ri(60, 140) });
      t += 0.12;
      events.push({ id: `h-l${i}`, type: 'hook-soft', t, hook: 'tailwind_arbitrary_warn',
        message: `Arbitrary value 'grid-cols-[repeat(${3+i},minmax(0,1fr))]' — consider grid-cols-${3+i}.`,
        file: 'src/components/ProductGrid.tsx', decision: 'soft' });
      t += 0.04;
      events.push({ id: `tr-l${i}`, type: 'tool-result', t, tool: 'Edit', ok: true });
      t += ri(2, 4);
    }
    events.push({ id: 'loop-1', type: 'loop', t, message: '🔁 Loop detected: same hook fired on same file ×5 in 20s', count: 5, hook: 'tailwind_arbitrary_warn', file: 'src/components/ProductGrid.tsx' });
  }

  // Deny event for inline style
  events.push({ id: 'tc-w2', type: 'tool-call', t, tool: 'Write', args: { path: 'src/components/Newsletter.tsx' }, durationMs: ri(80, 200) });
  t += 0.15;
  events.push({ id: 'h-2', type: 'hook-deny', t, hook: 'block_inline_styles',
    message: HOOK_SAMPLES.block_inline_styles, file: 'src/components/Newsletter.tsx', decision: 'deny' });
  t += 0.05;
  events.push({ id: 'tr-w2', type: 'tool-result', t, tool: 'Write', ok: false, content: 'BLOCKED by hook block_inline_styles. File not written.' });
  t += 0.5;

  events.push({ id: 'a-2', type: 'assistant', t,
    content: "Hook blocked inline styles — let me refactor to use utility classes.",
    tokens: { in: 14200, out: 280 },
  });
  t += ri(40, 90); // long gap shows up here

  events.push({ id: 'tc-w3', type: 'tool-call', t, tool: 'Write', args: { path: 'src/components/Newsletter.tsx' }, durationMs: ri(80, 200) });
  t += 0.2;
  events.push({ id: 'tr-w3', type: 'tool-result', t, tool: 'Write', ok: true });
  t += 1;

  // Bash for type check
  events.push({ id: 'tc-b1', type: 'tool-call', t, tool: 'Bash', args: { cmd: 'pnpm tsc --noEmit' }, durationMs: ri(2000, 5000) });
  t += 3.5;
  const err = pick(TS_ERRORS);
  events.push({ id: 'tr-b1', type: 'tool-result', t, tool: 'Bash', ok: false,
    content: `src/components/ProductGrid.tsx:42:13\n${err.code}: ${err.msg}\n\n  42 |   <ProductCard product={p as Theme} />\n     |               ^^^^^^^\nFound 1 error.\n` });
  t += 0.5;
  if (sess.status === 'failed') {
    events.push({ id: 'err-1', type: 'error', t, message: `Build failed: ${err.code} ${err.msg}`, file: 'src/components/ProductGrid.tsx' });
  } else {
    events.push({ id: 'a-3', type: 'assistant', t, content: `Fixing the ${err.code} by removing the cast and using the typed prop directly.`, tokens: { in: 16200, out: 180 } });
    t += 0.3;
    events.push({ id: 'tc-e1', type: 'tool-call', t, tool: 'Edit', args: { path: 'src/components/ProductGrid.tsx' }, durationMs: 120 });
    t += 0.2;
    events.push({ id: 'tr-e1', type: 'tool-result', t, tool: 'Edit', ok: true });
    t += 0.5;
    events.push({ id: 'tc-b2', type: 'tool-call', t, tool: 'Bash', args: { cmd: 'pnpm tsc --noEmit' }, durationMs: ri(2200, 3800) });
    t += 3;
    events.push({ id: 'tr-b2', type: 'tool-result', t, tool: 'Bash', ok: true, content: 'No errors. Done.' });
    t += 0.4;
    events.push({ id: 'a-4', type: 'assistant', t, content: 'All sections built, types pass. Ready to publish.', tokens: { in: 17500, out: 80 } });
  }
  return events;
}

// ─── Hook events (flat list, last 24h) ────────────────────────────────────
function mkHookEvents() {
  const out = [];
  for (let i = 0; i < 600; i++) {
    const h = pick(HOOKS);
    const decRoll = R();
    let decision = 'soft';
    let cum = 0;
    for (const [k, v] of Object.entries(h.decisionMix)) {
      cum += v; if (decRoll < cum) { decision = k; break; }
    }
    const sess = pick(SESSIONS);
    out.push({
      id: `hk-${i}`,
      ts: Date.now() - ri(0, 24 * 3600 * 1000),
      hook: h.name,
      decision,
      file: pick(['src/components/Hero.tsx', 'src/components/ProductGrid.tsx', 'src/components/Newsletter.tsx', 'src/pages/index.tsx', 'shared/ProductCard.tsx', 'src/components/Footer.tsx', 'src/components/Cart.tsx', 'src/lib/api.ts']),
      project_id: sess.project_id,
      session_id: sess.id,
      message: HOOK_SAMPLES[h.name] || 'Hook fired',
    });
  }
  return out.sort((a, b) => b.ts - a.ts);
}
const HOOK_EVENTS = mkHookEvents();

// FP suspects: group by session×hook×file with count > 5
function mkHookFpSuspects() {
  const buckets = {};
  HOOK_EVENTS.forEach(e => {
    const k = `${e.session_id}|${e.hook}|${e.file}`;
    buckets[k] = buckets[k] || { ...e, count: 0 };
    buckets[k].count++;
  });
  // Force a few extreme cases for realism
  const forced = [
    { session_id: SESSIONS[1].id, hook: 'tailwind_arbitrary_warn', file: 'src/components/ProductGrid.tsx', count: 23, message: HOOK_SAMPLES.tailwind_arbitrary_warn, ts: Date.now() - 600000 },
    { session_id: SESSIONS[4].id, hook: 'enforce_tailwind_tokens', file: 'src/components/Hero.tsx', count: 18, message: HOOK_SAMPLES.enforce_tailwind_tokens, ts: Date.now() - 3600000 },
    { session_id: SESSIONS[7].id, hook: 'block_inline_styles', file: 'src/components/Newsletter.tsx', count: 14, message: HOOK_SAMPLES.block_inline_styles, ts: Date.now() - 5400000 },
    { session_id: SESSIONS[2].id, hook: 'enforce_react_keys', file: 'src/components/ProductGrid.tsx', count: 11, message: HOOK_SAMPLES.enforce_react_keys, ts: Date.now() - 7200000 },
    { session_id: SESSIONS[9].id, hook: 'require_alt_text', file: 'src/components/Hero.tsx', count: 9, message: HOOK_SAMPLES.require_alt_text, ts: Date.now() - 9000000 },
    { session_id: SESSIONS[12].id, hook: 'tailwind_arbitrary_warn', file: 'shared/ProductCard.tsx', count: 8, message: HOOK_SAMPLES.tailwind_arbitrary_warn, ts: Date.now() - 12600000 },
    { session_id: SESSIONS[15].id, hook: 'block_console_logs', file: 'src/lib/api.ts', count: 7, message: HOOK_SAMPLES.block_console_logs, ts: Date.now() - 14400000 },
    { session_id: SESSIONS[6].id, hook: 'enforce_tailwind_tokens', file: 'src/components/Footer.tsx', count: 6, message: HOOK_SAMPLES.enforce_tailwind_tokens, ts: Date.now() - 16200000 },
  ];
  const rest = Object.values(buckets).filter(b => b.count > 5).slice(0, 6);
  return [...forced, ...rest];
}
const HOOK_FP_SUSPECTS = mkHookFpSuspects();

// ─── Deployments ──────────────────────────────────────────────────────────
function mkDeployment(i, sess) {
  sess = sess || pick(SESSIONS);
  const statusPool = ['succeeded', 'succeeded', 'succeeded', 'succeeded', 'failed', 'failed', 'pending', 'building'];
  const status = i < 2 ? pick(['building', 'pending']) : pick(statusPool);
  const errPool = TS_ERRORS;
  const err = status === 'failed' ? pick(errPool) : null;
  const created = Date.now() - ri(0, 24 * 3600 * 1000);
  const dur = status === 'pending' ? null : (status === 'building' ? ri(30, 120) : ri(45, 320));
  return {
    id: ulid(),
    project_id: sess.project_id,
    project_name: sess.project_name,
    session_id: sess.id,
    merchant: sess.merchant,
    status,
    created_at: created,
    duration: dur,
    error_code: err?.code,
    error_msg: err?.msg,
    error_file: err ? pick(['src/components/ProductGrid.tsx', 'src/pages/index.tsx', 'src/components/Hero.tsx', 'shared/ProductCard.tsx']) : null,
    bundle_size_kb: status === 'succeeded' ? ri(180, 480) : null,
    sanitizer_warnings: status === 'succeeded' ? ri(0, 4) : null,
    cdn_url: status === 'succeeded' ? `https://${sess.project_name.replace(/\s+/g, '-').replace(/[^a-z0-9-]/gi, '')}.shopquantum.app` : null,
  };
}
const DEPLOYMENTS = Array.from({ length: 86 }, (_, i) => mkDeployment(i)).sort((a, b) => b.created_at - a.created_at);

// ─── Build log mock ───────────────────────────────────────────────────────
function mkBuildLog(d) {
  const lines = [];
  const add = (level, msg) => lines.push({ ts: Date.now() - ri(1000, 60000), level, msg });
  add('info', `[shopquantum-builder v2.18.4] Starting build for ${d.project_id}`);
  add('info', `Pulling source from S3: s3://sq-projects/${d.project_id}/`);
  add('info', '✓ Source fetched (217 files, 1.4 MB)');
  add('info', 'Running sanitizer pass…');
  add('info', '  ✓ DOM purify on 14 JSX files');
  add('info', '  ✓ Removed 3 dangerouslySetInnerHTML occurrences');
  add('info', '  ✓ Tailwind safelist verified');
  add('info', 'Installing dependencies (pnpm)…');
  add('info', '  Lockfile is up to date, resolution step is skipped');
  add('info', '  ✓ pnpm install (8.2s)');
  add('info', 'Running tsc --noEmit…');
  if (d.status === 'failed' && d.error_code) {
    add('error', `${d.error_file}:${ri(20, 120)}:${ri(2, 30)}`);
    add('error', `${d.error_code}: ${d.error_msg}`);
    add('error', 'Found 1 error in 1 file. Build aborted.');
  } else {
    add('info', '  ✓ Type-check clean');
    add('info', 'Running next build…');
    add('info', '  ▲ Next.js 15.2.1');
    add('info', '  Creating an optimized production build…');
    add('info', '  ✓ Compiled successfully');
    add('info', '  ✓ Linting and checking validity of types');
    add('info', '  ✓ Generating static pages (8/8)');
    add('info', `  ✓ Finalizing page optimization (bundle ${d.bundle_size_kb} kB gzipped)`);
    add('info', 'Uploading to S3…');
    add('info', '  ✓ Uploaded 24 files to s3://sq-cdn/sites/' + d.project_id + '/');
    add('info', 'Invalidating CloudFront distribution E2QWXJ1ABCDE…');
    add('info', '  ✓ Invalidation I1KRSY18ABC submitted');
    add('info', `✓ Build completed in ${d.duration}s. Live at ${d.cdn_url}`);
  }
  return lines;
}

// ─── Time series helpers ──────────────────────────────────────────────────
function timeSeries(points, baseline, jitter, trend = 0) {
  const out = [];
  for (let i = 0; i < points; i++) {
    out.push(Math.max(0, Math.round(baseline + trend * i + (R() - 0.5) * jitter)));
  }
  return out;
}

// ─── Dashboard metrics ────────────────────────────────────────────────────
const DASHBOARD = {
  kpi: {
    sessions_24h:    { value: 247, delta: 12, spark: timeSeries(24, 10, 8, 0.2) },
    avg_gen_time:    { value_p50: 7.4, value_p95: 24.8, delta: -8, spark: timeSeries(24, 7, 3, -0.05) },
    publish_success: { value: 87.2, delta: 3.4, spark: timeSeries(24, 86, 6, 0.05) },
    total_cost:      { value: 142.83, delta: 18.6, spark: timeSeries(24, 5, 3, 0.2) },
  },
  sessions_timeline: {
    labels: Array.from({ length: 24 }, (_, i) => `${(new Date().getHours() - 23 + i + 24) % 24}h`),
    series: TEMPLATE_KINDS.slice(0, 4).map(t => ({
      name: t.name, color: t.color,
      data: timeSeries(24, ri(2, 8), 6, 0.15),
    })),
  },
  failures: [
    { label: 'TS2352 cast Theme',     value: 34 },
    { label: 'TS2339 _veidx missing', value: 28 },
    { label: 'Hook FP loop (tw)',     value: 22 },
    { label: 'TS2741 alt missing',    value: 17 },
    { label: 'Bedrock 429',           value: 14 },
    { label: 'Bedrock 500',           value: 11 },
    { label: 'TS2322 token',          value: 9 },
    { label: 'Sanitizer DOM',         value: 6 },
    { label: 'S3 upload timeout',     value: 4 },
    { label: 'CloudFront inv. fail',  value: 2 },
  ],
  cost_trend: {
    labels: Array.from({ length: 7 }, (_, i) => `D-${6-i}`),
    series: [
      { name: 'sonnet-4.5', color: 'var(--chart-1)', data: [82, 94, 78, 105, 124, 118, 132] },
      { name: 'haiku-4.5',  color: 'var(--chart-2)', data: [12, 14, 11, 16, 18, 17, 19] },
      { name: 'opus-4',     color: 'var(--chart-5)', data: [5, 8, 4, 9, 12, 7, 6] },
    ],
  },
};

// ─── Alerts ───────────────────────────────────────────────────────────────
const ALERT_TYPES = [
  { value: 'slow_session', label: 'Slow session', icon: 'cost' },
  { value: 'publish_fail_spike', label: 'Publish fail spike', icon: 'alert' },
  { value: 'hook_fp_suspect', label: 'Hook FP suspect', icon: 'hook' },
  { value: 'cost_spike_merchant', label: 'Cost spike per merchant', icon: 'cost' },
  { value: 'worker_queue_backlog', label: 'Worker queue backlog', icon: 'cpu' },
  { value: 'deployment_error_pattern', label: 'Deployment error pattern', icon: 'deploy' },
  { value: 'cost_total_daily', label: 'Cost total daily', icon: 'cost' },
];

const ALERTS = [
  { id: 'al-1', name: 'Slow session > 20m',        type: 'slow_session',           condition: 'duration > 20m', threshold: '20m', window: 'live', severity: 'warning',  cooldown: '15m', channels: ['#sq-alerts', 'eng@shopquantum.io'], enabled: true,  last_triggered: Date.now() - 1800000 },
  { id: 'al-2', name: 'Publish fail rate > 30%',   type: 'publish_fail_spike',     condition: 'fail_rate > 30% in 1h', threshold: '30%', window: '1h', severity: 'critical', cooldown: '1h', channels: ['#sq-alerts', '#oncall'], enabled: true, last_triggered: Date.now() - 14400000 },
  { id: 'al-3', name: 'Hook FP > 5 in session',    type: 'hook_fp_suspect',        condition: 'same hook fires > 5 / session', threshold: '5', window: 'session', severity: 'warning', cooldown: '5m', channels: ['#sq-hooks-fp'], enabled: true,  last_triggered: Date.now() - 600000 },
  { id: 'al-4', name: 'Merchant spend > $20/h',    type: 'cost_spike_merchant',    condition: 'merchant spend > $20 in 1h', threshold: '$20', window: '1h', severity: 'info', cooldown: '1h', channels: ['#sq-cost'], enabled: true, last_triggered: Date.now() - 7200000 },
  { id: 'al-5', name: 'Queue depth > 50',          type: 'worker_queue_backlog',   condition: 'celery queue depth > 50', threshold: '50', window: 'live', severity: 'warning', cooldown: '5m', channels: ['#sq-alerts'], enabled: false, last_triggered: null },
  { id: 'al-6', name: 'TS2352 pattern > 10/h',     type: 'deployment_error_pattern', condition: 'same TS error > 10 in 1h', threshold: '10', window: '1h', severity: 'warning', cooldown: '1h', channels: ['#sq-deploys'], enabled: true,  last_triggered: Date.now() - 5400000 },
  { id: 'al-7', name: 'Daily cost > $500',         type: 'cost_total_daily',       condition: 'total cost > $500 today', threshold: '$500', window: '24h', severity: 'critical', cooldown: '24h', channels: ['#sq-cost', 'finance@shopquantum.io'], enabled: true, last_triggered: null },
];

const ALERT_HISTORY = [
  { id: 'h-1', alert_id: 'al-3', alert_name: 'Hook FP > 5 in session',   ts: Date.now() - 600000,    value: '8 fires (tailwind_arbitrary_warn × ProductGrid.tsx)', severity: 'warning',  channels: ['#sq-hooks-fp'], ack: false },
  { id: 'h-2', alert_id: 'al-1', alert_name: 'Slow session > 20m',       ts: Date.now() - 1800000,   value: '24m 38s (6c630b95… clone tryblossom)',                severity: 'warning',  channels: ['#sq-alerts', 'eng@shopquantum.io'], ack: true, ack_by: 'phong@shopquantum.io' },
  { id: 'h-3', alert_id: 'al-6', alert_name: 'TS2352 pattern > 10/h',    ts: Date.now() - 5400000,   value: '14 occurrences of TS2352 Theme cast',                  severity: 'warning',  channels: ['#sq-deploys'], ack: true, ack_by: 'huy@shopquantum.io' },
  { id: 'h-4', alert_id: 'al-4', alert_name: 'Merchant spend > $20/h',   ts: Date.now() - 7200000,   value: 'duc.vo@tryblossom.com — $24.18 in 1h',                 severity: 'info',     channels: ['#sq-cost'], ack: true, ack_by: 'mai@shopquantum.io' },
  { id: 'h-5', alert_id: 'al-2', alert_name: 'Publish fail rate > 30%',  ts: Date.now() - 14400000,  value: '38.4% (51 fails / 132 publishes)',                     severity: 'critical', channels: ['#sq-alerts', '#oncall'], ack: true, ack_by: 'huy@shopquantum.io' },
  { id: 'h-6', alert_id: 'al-3', alert_name: 'Hook FP > 5 in session',   ts: Date.now() - 21600000,  value: '6 fires (enforce_tailwind_tokens × Hero.tsx)',         severity: 'warning',  channels: ['#sq-hooks-fp'], ack: true, ack_by: 'phong@shopquantum.io' },
  { id: 'h-7', alert_id: 'al-1', alert_name: 'Slow session > 20m',       ts: Date.now() - 32400000,  value: '21m 12s (b7e441a2… bloom cosmetics)',                  severity: 'warning',  channels: ['#sq-alerts', 'eng@shopquantum.io'], ack: true, ack_by: 'mai@shopquantum.io' },
  { id: 'h-8', alert_id: 'al-6', alert_name: 'TS2352 pattern > 10/h',    ts: Date.now() - 86400000,  value: '12 occurrences of TS2352 Theme cast',                  severity: 'warning',  channels: ['#sq-deploys'], ack: true, ack_by: 'huy@shopquantum.io' },
  { id: 'h-9', alert_id: 'al-3', alert_name: 'Hook FP > 5 in session',   ts: Date.now() - 100800000, value: '7 fires (block_inline_styles × Newsletter.tsx)',       severity: 'warning',  channels: ['#sq-hooks-fp'], ack: true, ack_by: 'phong@shopquantum.io' },
];

// ─── Notification channels ────────────────────────────────────────────────
const DISCORD_CHANNELS = [
  { id: 'dc-1', name: '#sq-alerts',     url_masked: 'discord.com/api/webhooks/1234……7d8f', last_used: Date.now() - 1800000,  status: 'ok',  subscribed: ['slow_session', 'publish_fail_spike', 'worker_queue_backlog'] },
  { id: 'dc-2', name: '#sq-hooks-fp',   url_masked: 'discord.com/api/webhooks/5678……a2c1', last_used: Date.now() - 600000,   status: 'ok',  subscribed: ['hook_fp_suspect'] },
  { id: 'dc-3', name: '#sq-cost',       url_masked: 'discord.com/api/webhooks/9abc……ef12', last_used: Date.now() - 7200000,  status: 'ok',  subscribed: ['cost_spike_merchant', 'cost_total_daily'] },
  { id: 'dc-4', name: '#sq-deploys',    url_masked: 'discord.com/api/webhooks/def0……8810', last_used: Date.now() - 5400000,  status: 'ok',  subscribed: ['deployment_error_pattern'] },
  { id: 'dc-5', name: '#oncall',        url_masked: 'discord.com/api/webhooks/1357……9b3a', last_used: Date.now() - 14400000, status: 'warn', subscribed: ['publish_fail_spike'] },
];
const EMAIL_RECIPIENTS = ['eng@shopquantum.io', 'oncall@shopquantum.io', 'finance@shopquantum.io', 'huy@shopquantum.io', 'phong@shopquantum.io'];

// ─── Cost views ───────────────────────────────────────────────────────────
const COST_BY_MERCHANT = MERCHANTS.map(m => ({ merchant: m, spend: +(R() * 80 + 1).toFixed(2), sessions: ri(1, 18) })).sort((a, b) => b.spend - a.spend);
const COST_BY_SESSION = [...SESSIONS].sort((a, b) => b.cost - a.cost).slice(0, 50);
const COST_DAILY = {
  labels: Array.from({ length: 30 }, (_, i) => {
    const d = new Date(); d.setDate(d.getDate() - (29 - i));
    return (d.getMonth()+1) + '/' + d.getDate();
  }),
  series: [
    { name: 'sonnet-4.5', color: 'var(--chart-1)', data: timeSeries(30, 80, 40, 0.5) },
    { name: 'haiku-4.5',  color: 'var(--chart-2)', data: timeSeries(30, 14, 8, 0.05) },
    { name: 'opus-4',     color: 'var(--chart-5)', data: timeSeries(30, 8, 4, 0.05) },
  ],
};

// ─── Health ───────────────────────────────────────────────────────────────
const HEALTH = {
  workers: [
    { name: 'celery-default-1',  host: 'ip-10-0-1-12',   status: 'ok',  load: 0.42, active: 4, processed: 12483 },
    { name: 'celery-default-2',  host: 'ip-10-0-1-13',   status: 'ok',  load: 0.38, active: 3, processed: 12104 },
    { name: 'celery-default-3',  host: 'ip-10-0-1-14',   status: 'ok',  load: 0.51, active: 5, processed: 13208 },
    { name: 'celery-build-1',    host: 'ip-10-0-2-22',   status: 'ok',  load: 0.61, active: 2, processed:  6843 },
    { name: 'celery-build-2',    host: 'ip-10-0-2-23',   status: 'ok',  load: 0.58, active: 2, processed:  6712 },
    { name: 'celery-build-3',    host: 'ip-10-0-2-24',   status: 'warn',load: 0.84, active: 3, processed:  7102 },
    { name: 'celery-priority-1', host: 'ip-10-0-3-31',   status: 'ok',  load: 0.22, active: 1, processed:  2438 },
    { name: 'celery-priority-2', host: 'ip-10-0-3-32',   status: 'idle',load: 0.04, active: 0, processed:  2401 },
    { name: 'celery-canary',     host: 'ip-10-0-4-41',   status: 'ok',  load: 0.12, active: 1, processed:   843 },
  ],
  queues: [
    { name: 'default',  depth: 12, age: 3,  workers: 3 },
    { name: 'build',    depth: 47, age: 28, workers: 3 },
    { name: 'priority', depth: 1,  age: 1,  workers: 2 },
    { name: 'canary',   depth: 0,  age: 0,  workers: 1 },
  ],
  integrations: [
    { name: 'PostgreSQL',  status: 'ok',  latency: 4.2, sub: 'pool 42/100' },
    { name: 'Redis',       status: 'ok',  latency: 0.8, sub: 'ping 0.8ms' },
    { name: 'S3 sq-projects', status: 'ok', latency: 38, sub: 'us-east-1' },
    { name: 'S3 sq-cdn',   status: 'ok',  latency: 41, sub: 'us-east-1' },
    { name: 'CloudFront',  status: 'ok',  latency: 12, sub: 'E2QWXJ1ABCDE' },
    { name: 'AWS Bedrock', status: 'warn',latency: 184, sub: '3 rate-limits/min' },
    { name: 'Stripe',      status: 'ok',  latency: 92, sub: 'webhook ok' },
    { name: 'Brevo',       status: 'ok',  latency: 124, sub: 'transactional ok' },
  ],
  recent_errors: [
    { ts: Date.now() -    3000, level: 'ERROR', source: 'celery.build-2', msg: 'TS2352 in src/components/ProductGrid.tsx — clone tryblossom' },
    { ts: Date.now() -    9000, level: 'ERROR', source: 'bedrock.client', msg: 'ThrottlingException: rate exceeded for claude-sonnet-4-5 (4/4 retries)' },
    { ts: Date.now() -   22000, level: 'WARN',  source: 'celery.build-3', msg: 'High CPU load (0.84). Consider scaling out.' },
    { ts: Date.now() -   46000, level: 'ERROR', source: 'sanitizer',      msg: 'dangerouslySetInnerHTML detected in Newsletter.tsx — blocked' },
    { ts: Date.now() -   88000, level: 'ERROR', source: 'celery.default-1', msg: 'Hook script timeout: enforce_tailwind_tokens (4.2s > 4.0s)' },
    { ts: Date.now() -  146000, level: 'WARN',  source: 'discord.notify', msg: 'Webhook #oncall returned 429, will retry in 60s' },
    { ts: Date.now() -  210000, level: 'ERROR', source: 'bedrock.client', msg: 'Connection timeout to bedrock-runtime.us-east-1.amazonaws.com' },
    { ts: Date.now() -  340000, level: 'ERROR', source: 's3.uploader',    msg: 'PutObject failed with SlowDown (sq-cdn): 1 retry' },
  ],
};

// ─── Discord webhook example JSON ─────────────────────────────────────────
const DISCORD_EXAMPLE = {
  embeds: [{
    title: '🚨 Slow Session Alert',
    color: 15158332,
    fields: [
      { name: 'Session', value: '6c630b95… (24.7m)' },
      { name: 'Project', value: '01KRSY18… (clone tryblossom)' },
      { name: 'Cost',    value: '$4.35' },
      { name: 'Top issue', value: 'Hook FP loop (tailwind_arbitrary_warn ×23)' }
    ],
    url: 'https://admin.shopquantum.io/sessions/6c630b95…'
  }]
};

// Export
Object.assign(window, {
  MODELS, TEMPLATE_KINDS, APPS, MERCHANTS, TOOLS, HOOKS, TS_ERRORS, HOOK_SAMPLES, ALERT_TYPES,
  SESSIONS, HOOK_EVENTS, HOOK_FP_SUSPECTS, DEPLOYMENTS, ALERTS, ALERT_HISTORY,
  DISCORD_CHANNELS, EMAIL_RECIPIENTS, COST_BY_MERCHANT, COST_BY_SESSION, COST_DAILY,
  DASHBOARD, HEALTH, DISCORD_EXAMPLE,
  mkSessionEvents, mkBuildLog, timeSeries, ulid, sessId,
});
