/**
 * Helix REST API Server
 *
 * Exposes PCEC repair engine over HTTP for cross-language integration.
 * Start: npx helix serve [--port 7842] [--mode observe|auto|full]
 */
import http from 'node:http';
import crypto from 'node:crypto';
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { PcecEngine } from './engine/pcec.js';
import { GeneMap } from './engine/gene-map.js';
import { defaultAdapters } from './platforms/index.js';
import type { HelixMode } from './engine/types.js';
import { GeneDream } from './engine/dream.js';
import { getSchemaVersion, needsMigration, CURRENT_SCHEMA_VERSION } from './engine/migrations.js';
import { GeneMap as VialGeneMap, PCEC as VialPCEC, GeneDream as VialGeneDream } from '@vial-agent/runtime';

function mapStrategyToAction(strategy: string): string {
  const m: Record<string, string> = {
    backoff_retry: 'wait_and_retry', renew_session: 'refresh_session',
    refresh_nonce: 'refresh_state', reduce_request: 'reduce_amount',
    speed_up_transaction: 'increase_gas', retry_with_receipt: 'wait_and_retry',
    switch_endpoint: 'switch_endpoint', self_pay_gas: 'fund_gas',
    swap_currency: 'swap_token', retry: 'wait_and_retry',
    hold_and_notify: 'escalate', refund_waterfall: 'escalate',
  };
  return m[strategy] || 'retry';
}

function json(res: http.ServerResponse, data: unknown, status = 200) {
  res.writeHead(status, {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Headers': 'Content-Type',
    'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
  });
  res.end(JSON.stringify(data));
}

function readBody(req: http.IncomingMessage): Promise<string> {
  return new Promise((resolve) => {
    let body = '';
    req.on('data', (c: Buffer) => { body += c.toString(); });
    req.on('end', () => resolve(body));
  });
}

export interface ApiServerOptions {
  port?: number;
  mode?: HelixMode;
  geneMapPath?: string;
  beta?: boolean;
}

export function createApiServer(opts: ApiServerOptions = {}) {
  const port = opts.port ?? 7842;
  const mode = opts.mode ?? 'observe';
  const geneMapPath = opts.geneMapPath ?? './helix-genes.db';
  const betaEnabled = opts.beta ?? false;

  const geneMap = new GeneMap(geneMapPath);
  const engine = new PcecEngine(geneMap, 'api-server', { mode, llm: { provider: 'anthropic', enabled: !!(process.env.ANTHROPIC_API_KEY || process.env.HELIX_LLM_API_KEY) } } as any);
  for (const a of defaultAdapters) engine.registerAdapter(a);

  const dream = new GeneDream(geneMap, {
    onDream: (e) => { if (e.stage === 'complete') console.log(`[helix] Dream: ${JSON.stringify(e.stats)}`); },
  });

  // VialOS Runtime — PCEC + Gene Map for /heal endpoint
  const vialGeneMapPath = geneMapPath === ':memory:' ? ':memory:' : geneMapPath.replace(/\.db$/, '-vial-genes.db');
  const vialGeneMap = new VialGeneMap(vialGeneMapPath);
  const vialPcec = new VialPCEC({ geneMap: vialGeneMap, maxRetries: 3 });

  // Gene Collector database (shares the same SQLite file)
  const collectorDb = geneMap.database;
  collectorDb.exec(`CREATE TABLE IF NOT EXISTS gene_discoveries (id INTEGER PRIMARY KEY AUTOINCREMENT, error_pattern TEXT NOT NULL, code TEXT NOT NULL, category TEXT NOT NULL, severity TEXT, strategy TEXT NOT NULL, q_value REAL, source TEXT, reasoning TEXT, llm_provider TEXT, platform TEXT, helix_version TEXT, reported_at INTEGER, reviewed INTEGER DEFAULT 0, approved INTEGER DEFAULT 0, report_count INTEGER DEFAULT 1, avg_q REAL, created_at INTEGER DEFAULT (unixepoch()), updated_at INTEGER DEFAULT (unixepoch()))`);
  collectorDb.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_discoveries_unique ON gene_discoveries(code, category, strategy, platform)`);
  collectorDb.exec(`CREATE INDEX IF NOT EXISTS idx_discoveries_reviewed ON gene_discoveries(reviewed)`);

  // TTL cleanup: remove old rejected/unreviewed entries every hour
  setInterval(() => {
    try {
      const cutoff = Math.floor(Date.now() / 1000) - (90 * 24 * 60 * 60);
      collectorDb.prepare('DELETE FROM gene_discoveries WHERE reviewed = 1 AND approved = 0 AND created_at < ?').run(cutoff);
      collectorDb.prepare('DELETE FROM gene_discoveries WHERE reviewed = 0 AND created_at < ?').run(cutoff);
    } catch { /* ignore */ }
  }, 60 * 60 * 1000);

  function requireAdmin(req: http.IncomingMessage, res: http.ServerResponse): boolean {
    const adminKey = process.env.HELIX_ADMIN_KEY;
    if (!adminKey) return true;
    if (req.headers['authorization'] !== `Bearer ${adminKey}`) {
      json(res, { error: 'Unauthorized' }, 401);
      return false;
    }
    return true;
  }

  const server = http.createServer(async (req, res) => {
    const url = new URL(req.url ?? '/', `http://${req.headers.host}`);
    const path = url.pathname;

    // ── MPPScan / AgentCash Discovery ──────────────────────

    // GET /favicon.ico — minimal 1x1 transparent PNG
    if (path === '/favicon.ico') {
      const ico = Buffer.from('AAABAAEAAQEAAAEAGAAwAAAAFgAAACgAAAABAAAAAgAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAA=', 'base64');
      res.writeHead(200, { 'Content-Type': 'image/x-icon', 'Content-Length': String(ico.length), 'Cache-Control': 'public, max-age=86400' });
      return res.end(ico);
    }

    // GET /.well-known/x402 — MPP discovery
    if (path === '/.well-known/x402' && req.method === 'GET') {
      return json(res, {
        version: '1.0',
        'openapi-url': '/openapi.json',
        facilitator: 'https://helix-production-e110.up.railway.app',
        endpoints: ['/heal', '/observe'],
        accepts: [{ scheme: 'exact', network: 'base', asset: 'USDC', maxAmountRequired: '0.01' }],
      });
    }

    // GET /openapi.json — OpenAPI 3.1.0 spec for MPPScan discovery
    if (path === '/openapi.json' && req.method === 'GET') {
      return json(res, {
        openapi: '3.1.0',
        info: {
          title: 'Helix — Self-Healing Agent Payment SDK',
          version: '2.6.0',
          description: 'Helix automatically repairs failed AI agent payment transactions. PCEC engine with Gene Map reinforcement learning.',
          'x-guidance': 'Use POST /heal to repair a failed payment transaction. Send the failed transaction and error message, get back a repaired transaction ready to retry. Use GET /gene-map to inspect learned repair patterns.',
        },
        'x-discovery': { ownershipProofs: [] },
        components: {
          securitySchemes: {
            mppPayment: { type: 'http', scheme: 'bearer', description: 'MPP payment token' },
          },
        },
        security: [{ mppPayment: [] }],
        paths: {
          '/heal': {
            post: {
              operationId: 'healTransaction',
              summary: 'Heal - Repair a failed agent payment transaction',
              tags: ['Healing'],
              security: [{ mppPayment: [] }],
              'x-payment-info': { pricingMode: 'fixed', price: '0.010000', protocols: ['mpp'] },
              requestBody: { required: true, content: { 'application/json': { schema: { type: 'object', properties: { transaction: { type: 'object', properties: { to: { type: 'string' }, value: { type: 'string' }, data: { type: 'string' }, chainId: { type: 'number' } }, required: ['to', 'value', 'chainId'] }, error: { type: 'string' }, context: { type: 'object' } }, required: ['transaction', 'error'] } } } },
              responses: { '200': { description: 'Repaired transaction', content: { 'application/json': { schema: { type: 'object', properties: { repaired: { type: 'object' }, diagnosis: { type: 'string' }, strategy: { type: 'string' }, confidence: { type: 'number' } }, required: ['repaired', 'diagnosis', 'strategy', 'confidence'] } } } }, '402': { description: 'Payment Required' } },
            },
          },
          '/observe': {
            post: {
              operationId: 'observeTransaction',
              summary: 'Observe - Monitor a transaction without healing',
              tags: ['Observability'],
              security: [{ mppPayment: [] }],
              'x-payment-info': { pricingMode: 'fixed', price: '0.001000', protocols: ['mpp'] },
              requestBody: { required: true, content: { 'application/json': { schema: { type: 'object', properties: { transaction: { type: 'object' }, chainId: { type: 'number' } }, required: ['transaction', 'chainId'] } } } },
              responses: { '200': { description: 'Observation result', content: { 'application/json': { schema: { type: 'object', properties: { status: { type: 'string' }, prediction: { type: 'string' }, riskFactors: { type: 'array', items: { type: 'string' } } }, required: ['status', 'prediction', 'riskFactors'] } } } }, '402': { description: 'Payment Required' } },
            },
          },
          '/gene-map': {
            get: {
              operationId: 'getGeneMap',
              summary: 'Gene Map - View learned repair patterns',
              tags: ['Intelligence'],
              security: [{}],
              responses: { '200': { description: 'Gene Map state', content: { 'application/json': { schema: { type: 'object', properties: { totalGenes: { type: 'number' }, topPatterns: { type: 'array', items: { type: 'object' } }, successRate: { type: 'number' } }, required: ['totalGenes', 'topPatterns', 'successRate'] } } } } },
            },
          },
        },
      });
    }

    // POST /heal — Repair a failed payment transaction (MPP payable)
    if (path === '/heal' && req.method === 'POST') {
      const paymentHeader = req.headers['x-payment'] || req.headers['authorization'];
      if (!paymentHeader) {
        const realm = (req.headers.host || 'helix-production-e110.up.railway.app').replace(/^https?:\/\//, '');
        const id = crypto.randomUUID();
        const expires = new Date(Date.now() + 5 * 60 * 1000).toISOString();
        const requestObj = { amount: '0.010000', asset: 'USDC', network: 'base', currency: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', recipient: '0x4392bADe0C015cc2dD13924f099EE6d57c270Adb', payTo: realm };
        const requestB64 = Buffer.from(JSON.stringify(requestObj)).toString('base64url');
        res.writeHead(402, {
          'Content-Type': 'application/json',
          'WWW-Authenticate': `Payment method="tempo" intent="charge" id="${id}" expires="${expires}" realm="${realm}" request="${requestB64}"`,
          'Access-Control-Allow-Origin': '*',
        });
        return res.end(JSON.stringify({ error: 'Payment Required', paymentOptions: [{ method: 'tempo', network: 'base', asset: 'USDC', amount: '0.010000', realm }] }));
      }
      try {
        const body = JSON.parse(await readBody(req));
        const { transaction, error: errorMsg, context: ctx } = body;
        if (!transaction || !errorMsg) return json(res, { error: 'transaction and error fields required' }, 400);

        const sessionId = (req.headers['x-session-id'] as string) ?? crypto.randomUUID();
        const vialResult = await vialPcec.repair(
          { errorType: errorMsg, errorMessage: errorMsg, toolName: 'eth_sendTransaction', sessionId, turnCount: 1 },
          async (strategy: string) => {
            const err = new Error(errorMsg);
            const result = await engine.repair(err, { ...ctx, platform: ctx?.platform || 'coinbase', vialStrategy: strategy });
            const repaired = { ...transaction, ...(result.commitOverrides ?? {}) };
            return {
              success: !!(result.winner || result.gene),
              output: JSON.stringify({
                repaired,
                diagnosis: result.failure?.code ?? 'unknown',
                strategy: result.winner?.strategy ?? result.gene?.strategy ?? strategy,
                confidence: result.winner?.successProbability ?? 0.5,
              }),
            };
          },
        );

        // Trigger Gene Dream asynchronously
        if (vialGeneMap.shouldDream(50)) {
          new VialGeneDream(vialGeneMap).run()
            .then((results: unknown) => console.log('[VialDream]', results))
            .catch(() => {});
        }

        const capsules = vialGeneMap.getRecentCapsules(1);
        const repairOutput = capsules.length > 0
          ? JSON.parse(capsules[0].output)
          : { repaired: transaction, diagnosis: 'unknown', strategy: 'unknown', confidence: 0 };

        return json(res, {
          ...repairOutput,
          success: vialResult.success,
          vialStrategy: vialResult.finalStrategy,
          vialAttempts: vialResult.attempts,
          vialEscalated: vialResult.escalated,
        });
      } catch (e: any) {
        return json(res, { error: e.message }, 500);
      }
    }

    // POST /observe — Monitor a transaction (MPP payable)
    if (path === '/observe' && req.method === 'POST') {
      const paymentHeader = req.headers['x-payment'] || req.headers['authorization'];
      if (!paymentHeader) {
        const realm = (req.headers.host || 'helix-production-e110.up.railway.app').replace(/^https?:\/\//, '');
        const id = crypto.randomUUID();
        const expires = new Date(Date.now() + 5 * 60 * 1000).toISOString();
        const requestObj = { amount: '0.001000', asset: 'USDC', network: 'base', currency: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', recipient: '0x4392bADe0C015cc2dD13924f099EE6d57c270Adb', payTo: realm };
        const requestB64 = Buffer.from(JSON.stringify(requestObj)).toString('base64url');
        res.writeHead(402, {
          'Content-Type': 'application/json',
          'WWW-Authenticate': `Payment method="tempo" intent="charge" id="${id}" expires="${expires}" realm="${realm}" request="${requestB64}"`,
          'Access-Control-Allow-Origin': '*',
        });
        return res.end(JSON.stringify({ error: 'Payment Required', paymentOptions: [{ method: 'tempo', network: 'base', asset: 'USDC', amount: '0.001000', realm }] }));
      }
      try {
        const body = JSON.parse(await readBody(req));
        const { transaction, chainId } = body;
        if (!transaction) return json(res, { error: 'transaction field required' }, 400);
        // Analyze transaction risk without executing repair
        const riskFactors: string[] = [];
        if (!transaction.to) riskFactors.push('missing recipient');
        if (!transaction.value && !transaction.data) riskFactors.push('no value or data');
        if (transaction.gas && BigInt(transaction.gas) < 21000n) riskFactors.push('gas too low');
        return json(res, { status: 'analyzed', prediction: riskFactors.length > 0 ? 'at-risk' : 'likely-success', riskFactors });
      } catch (e: any) {
        return json(res, { error: e.message }, 500);
      }
    }

    // GET /gene-map — View learned repair patterns (public, no auth)
    if (path === '/gene-map' && req.method === 'GET') {
      const genes = geneMap.list();
      const totalGenes = genes.length;
      const topPatterns = genes.slice(0, 10).map(g => ({ code: g.failureCode, category: g.category, strategy: g.strategy, qValue: g.qValue, successCount: g.successCount }));
      const totalSuccess = genes.reduce((s, g) => s + (g.successCount || 0), 0);
      const totalAttempts = totalSuccess + genes.reduce((s, g) => s + (g.consecutiveFailures || 0), 0);
      res.writeHead(200, {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
        'X-Auth-Mode': 'none',
      });
      return res.end(JSON.stringify({ totalGenes, topPatterns, successRate: totalAttempts > 0 ? Math.round(totalSuccess / totalAttempts * 100) / 100 : 1 }));
    }

    // GET /vial/status — VialOS runtime info (beta only)
    if (path === '/vial/status' && req.method === 'GET') {
      if (!betaEnabled) return json(res, { error: 'Beta mode not enabled. Start with --beta flag.' }, 404);
      const health = geneMap.health();
      return json(res, {
        product: 'helix', productVersion: '2.6.0', beta: true,
        vialos: {
          runtime: 'embedded', runtimeVersion: '0.3.x-compatible', engine: 'pcec-v6',
          stages: ['perceive', 'construct', 'evaluate', 'commit', 'verify', 'gene'],
          modules: { selfRefine: true, promptOptimizer: true, negativeKnowledge: true, causalGraph: true, metaLearner: true, safetyVerifier: true, adaptiveWeights: true, conditionalGenes: true, selfPlay: true, federatedLearning: true, autoStrategy: true, autoAdapterDiscovery: true, geneDream: true },
          adapters: ['coinbase', 'tempo', 'privy', 'generic', 'api'],
        },
        geneMap: { schemaVersion: CURRENT_SCHEMA_VERSION, totalGenes: health.totalGenes, avgQValue: health.avgQValue },
        budget: { endpoint: '/vial/budget/estimate?task=<type>', endpoints: ['/vial/budget/estimate', '/vial/budget/estimates', '/vial/budget/summary'] },
        links: { dashboard: '/dashboard', geneMap: '/vial/gene-map', health: '/health', budget: '/vial/budget/summary' },
      });
    }

    // GET /vial/budget/estimate?task=<type> — predict cost (beta only)
    if (path === '/vial/budget/estimate' && req.method === 'GET') {
      if (!betaEnabled) return json(res, { error: 'Beta mode not enabled.' }, 404);
      const task = url.searchParams.get('task');
      if (!task) return json(res, { error: 'Missing ?task= parameter' }, 400);
      const { BudgetPredictor } = await import('./engine/budget-predictor.js');
      return json(res, new BudgetPredictor(geneMap).estimate(task));
    }

    // GET /vial/budget/estimates — all task type estimates (beta only)
    if (path === '/vial/budget/estimates' && req.method === 'GET') {
      if (!betaEnabled) return json(res, { error: 'Beta mode not enabled.' }, 404);
      const { BudgetPredictor } = await import('./engine/budget-predictor.js');
      return json(res, { estimates: new BudgetPredictor(geneMap).estimateAll() });
    }

    // GET /vial/budget/summary — overall cost summary (beta only)
    if (path === '/vial/budget/summary' && req.method === 'GET') {
      if (!betaEnabled) return json(res, { error: 'Beta mode not enabled.' }, 404);
      const { BudgetPredictor } = await import('./engine/budget-predictor.js');
      return json(res, new BudgetPredictor(geneMap).summary());
    }

    // GET /vial/gene-map — VialOS Gene Map stats
    if (path === '/vial/gene-map' && req.method === 'GET') {
      const genes = vialGeneMap.list();
      const totalGenes = genes.length;
      const topPatterns = genes.slice(0, 10).map((g: any) => ({ code: g.failureCode, category: g.category, strategy: g.strategy, qValue: g.qValue, successCount: g.successCount }));
      const totalSuccess = genes.reduce((s: number, g: any) => s + (g.successCount || 0), 0);
      const totalAttempts = totalSuccess + genes.reduce((s: number, g: any) => s + (g.consecutiveFailures || 0), 0);
      return json(res, { totalGenes, topPatterns, successRate: totalAttempts > 0 ? Math.round(totalSuccess / totalAttempts * 100) / 100 : 1 });
    }

    // GET / — welcome
    if (path === '/' && req.method === 'GET') {
      const health = geneMap.health();
      return json(res, {
        name: 'helix',
        description: 'Self-healing infrastructure for AI agent payments',
        version: '1.7.1',
        geneCount: health.totalGenes,
        platforms: health.platforms,
        endpoints: {
          'POST /repair': 'Send error for diagnosis + repair strategy',
          'GET /health': 'Healthcheck',
          'GET /status': 'Gene Map stats',
          'GET /genes': 'List all genes',
          'POST /api/telemetry': 'Report anonymous discoveries',
        },
        docs: 'https://github.com/adrianhihi/helix',
        npm: 'npm install @helix-agent/core',
        python: 'pip install helix-agent-sdk',
      });
    }

    // CORS preflight
    if (req.method === 'OPTIONS') {
      res.writeHead(204, {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
      });
      return res.end();
    }

    // GET /health
    if (path === '/health' && req.method === 'GET') {
      const health: any = { status: 'ok', version: '2.6.0', schemaVersion: getSchemaVersion(geneMap.database), targetSchemaVersion: CURRENT_SCHEMA_VERSION, uptime: process.uptime() };
      if (betaEnabled) {
        health.vialos = { runtime: 'embedded', engine: 'pcec-v6', geneMapSchema: CURRENT_SCHEMA_VERSION, compatibility: '@vial-agent/runtime@0.3.x' };
        health.beta = true;
      }
      return json(res, health);
    }

    // GET /schema
    if (path === '/schema' && req.method === 'GET') {
      return json(res, needsMigration(geneMap.database));
    }

    // GET /status
    if (path === '/status' && req.method === 'GET') {
      const stats = engine.getStats();
      const health = geneMap.health();
      return json(res, {
        status: 'running',
        mode,
        geneCount: health.totalGenes,
        avgQValue: health.avgQValue,
        totalRepairs: stats.repairs,
        immuneHits: stats.immuneHits,
        savedRevenue: stats.savedRevenue,
        platforms: health.platforms,
        uptime: process.uptime(),
      });
    }

    // GET /genes
    if (path === '/genes' && req.method === 'GET') {
      const genes = geneMap.list();
      const summary = genes.map(g => ({
        failureCode: g.failureCode, category: g.category,
        strategy: g.strategy, qValue: g.qValue,
        qVariance: g.qVariance, successCount: g.successCount,
        platforms: g.platforms, reasoning: g.reasoning, scores: g.scores || {},
      }));
      return json(res, { genes: summary, total: summary.length });
    }

    // POST /repair
    if (path === '/repair' && req.method === 'POST') {
      try {
        const body = JSON.parse(await readBody(req));
        const { error: errorMsg, errorType, agentId, platform, context } = body;

        if (errorMsg === undefined || errorMsg === null) {
          return json(res, { success: false, error: 'error field is required' }, 400);
        }

        const err = new Error(errorMsg);
        if (errorType) err.name = errorType;

        const startMs = Date.now();
        const result = await engine.repair(err, {
          agentId: agentId || 'rest-api',
          platform: platform || 'generic',
          ...context,
        });
        const repairMs = Date.now() - startMs;

        const strategy = result.winner?.strategy ?? result.gene?.strategy;
        return json(res, {
          success: true,
          failure: {
            code: result.failure.code,
            category: result.failure.category,
            severity: result.failure.severity,
            platform: result.failure.platform,
            rootCause: result.failure.rootCauseHint,
          },
          strategy: strategy ? {
            name: strategy,
            action: mapStrategyToAction(strategy),
            params: result.commitOverrides ?? {},
          } : null,
          repairMs,
          immune: result.immune, scores: (result as any).scores || {},
          candidates: result.candidates.slice(0, 5).map(c => ({
            strategy: c.strategy, score: c.score, source: c.source,
          })),
          predictions: result.predictions,
        });
      } catch (e) {
        return json(res, { success: false, error: String(e) }, 500);
      }
    }

    // POST /dream — trigger Gene Dream
    if (path === '/dream' && req.method === 'POST') {
      try {
        const body = JSON.parse(await readBody(req));
        const stats = await dream.dream(body.force ?? true);
        return json(res, { success: true, stats });
      } catch (e: any) {
        return json(res, { error: e.message }, 400);
      }
    }

    // GET /dream/status
    if (path === '/dream/status' && req.method === 'GET') {
      const check = dream.shouldDream();
      return json(res, { ...check, lastDream: dream.lastDreamStats() });
    }

    // GET /dashboard — interactive HTML dashboard
    if (path === '/dashboard' && req.method === 'GET') {
      try {
        const __dir = dirname(fileURLToPath(import.meta.url));
        const paths = [join(__dir, '../static/dashboard.html'), join(__dir, '../../static/dashboard.html')];
        let html = '';
        for (const p of paths) { try { html = readFileSync(p, 'utf-8'); break; } catch { /* next */ } }
        if (!html) throw new Error('not found');
        if (betaEnabled) {
          html = html.replace('</body>', `<div style="position:fixed;bottom:16px;right:16px;background:rgba(245,158,11,0.08);border:1px solid rgba(245,158,11,0.2);border-radius:8px;padding:6px 14px;font-family:'JetBrains Mono',monospace;font-size:11px;color:rgba(245,240,232,0.5);backdrop-filter:blur(8px);z-index:1000;display:flex;align-items:center;gap:8px"><span style="width:6px;height:6px;border-radius:50%;background:#f59e0b;display:inline-block"></span>Powered by VialOS Runtime · Beta</div></body>`);
        }
        res.writeHead(200, { 'Content-Type': 'text/html', 'Access-Control-Allow-Origin': '*' });
        return res.end(html);
      } catch {
        return json(res, { error: 'Dashboard not found' }, 404);
      }
    }

    // GET /dashboard/evolution-tree
    if (path === '/dashboard/evolution-tree' && req.method === 'GET') {
      try {
        const __dir = dirname(fileURLToPath(import.meta.url));
        const paths = [join(__dir, '../static/evolution-tree.html'), join(__dir, '../../static/evolution-tree.html')];
        let html = '';
        for (const p of paths) { try { html = readFileSync(p, 'utf-8'); break; } catch { /* next */ } }
        if (!html) throw new Error('not found');
        res.writeHead(200, { 'Content-Type': 'text/html', 'Access-Control-Allow-Origin': '*' });
        return res.end(html);
      } catch {
        return json(res, { error: 'Evolution tree page not found' }, 404);
      }
    }

    // GET /api/gene-scores — all genes with 6D scores
    if (path === '/api/gene-scores' && req.method === 'GET') {
      const db = geneMap.database;
      const genes = db.prepare('SELECT * FROM genes ORDER BY q_value DESC').all() as any[];
      const scored = genes.map((g: any) => {
        const sc = g.success_count ?? 0;
        const fc = g.consecutive_failures ?? 0;
        const platforms: string[] = (() => { try { return JSON.parse(g.platforms || '[]'); } catch { return []; } })();
        const ms = g.avg_repair_ms ?? 0;
        const scores = {
          accuracy: Math.min(1, sc / Math.max(1, sc + fc) * 1.1),
          cost: ms < 10 ? 0.95 : ms < 100 ? 0.7 : 0.4,
          latency: ms < 5 ? 1.0 : ms < 50 ? 0.8 : ms < 500 ? 0.5 : 0.2,
          safety: fc === 0 ? 1.0 : fc < 3 ? 0.7 : 0.3,
          transferability: Math.min(1, platforms.length / 4),
          reliability: Math.min(1, sc / 10),
        };
        const composite = Math.round((scores.accuracy * 0.25 + scores.cost * 0.15 + scores.latency * 0.15 + scores.safety * 0.25 + scores.transferability * 0.1 + scores.reliability * 0.1) * 100) / 100;
        return { id: g.id, failureCode: g.failure_code, category: g.category, strategy: g.strategy, qValue: g.q_value, successCount: sc, consecutiveFailures: fc, avgRepairMs: ms, platforms, scores, composite };
      });
      const avg = scored.length > 0 ? Math.round(scored.reduce((s: number, g: any) => s + g.composite, 0) / scored.length * 100) / 100 : 0;
      return json(res, { total: scored.length, avgComposite: avg, genes: scored });
    }

    // GET /api/safety-constraints
    if (path === '/api/safety-constraints' && req.method === 'GET') {
      const { SafetyVerifier } = await import('./engine/safety-verifier.js');
      return json(res, { constraints: new SafetyVerifier().getConstraints() });
    }

    // POST /api/verify-safety
    if (path === '/api/verify-safety' && req.method === 'POST') {
      try {
        const body = JSON.parse(await readBody(req));
        const { SafetyVerifier } = await import('./engine/safety-verifier.js');
        return json(res, new SafetyVerifier().verify(body.strategy || '', body.overrides || {}, {
          mode: body.mode || 'auto', originalArgs: body.originalArgs || [], strategy: body.strategy || '',
          overrides: body.overrides || {}, costCeiling: body.costCeiling, allowedStrategies: body.allowedStrategies,
          blockedStrategies: body.blockedStrategies, addressWhitelist: body.addressWhitelist,
        }));
      } catch (e) { return json(res, { error: String(e) }, 500); }
    }

    // POST /api/federated/round
    if (path === '/api/federated/round' && req.method === 'POST') {
      try {
        let body: any = {};
        try { body = JSON.parse(await readBody(req)); } catch { /* empty body OK */ }
        const { FederatedLearner } = await import('./engine/federated.js');
        return json(res, await new FederatedLearner(geneMap.database, body?.epsilon || 1.0).federatedRound());
      } catch (e) { return json(res, { error: String(e) }, 500); }
    }

    // GET /api/federated/stats
    if (path === '/api/federated/stats' && req.method === 'GET') {
      const { FederatedLearner } = await import('./engine/federated.js');
      return json(res, new FederatedLearner(geneMap.database).getStats());
    }

    // GET /api/federated/gradients
    if (path === '/api/federated/gradients' && req.method === 'GET') {
      const { FederatedLearner } = await import('./engine/federated.js');
      const g = new FederatedLearner(geneMap.database).computeGradients();
      return json(res, { gradients: g, count: g.length });
    }

    // POST /api/discover-adapters
    if (path === '/api/discover-adapters' && req.method === 'POST') {
      const { AdapterDiscovery } = await import('./engine/adapter-discovery.js');
      return json(res, new AdapterDiscovery(geneMap.database).runDiscovery());
    }
    // GET /api/evolution-tree — data for phylogenetic tree visualization
    if (path === '/api/evolution-tree' && req.method === 'GET') {
      const genes = geneMap.list();
      const nodes: any[] = [];
      const links: any[] = [];

      nodes.push({ id: 'helix', label: 'Helix', group: 'core', events: genes.length });

      const categorySet = new Map<string, any>();
      const platformSet = new Map<string, any>();
      const strategySet = new Map<string, any>();

      for (const gene of genes) {
        const catId = `cat-${gene.category}`;
        if (!categorySet.has(catId)) categorySet.set(catId, { id: catId, label: gene.category, events: 0, group: 'category' });
        categorySet.get(catId)!.events += (gene.successCount || 0);

        const platArr = Array.isArray(gene.platforms) ? gene.platforms : (typeof gene.platforms === 'string' ? JSON.parse(gene.platforms || '[]') : []);
        for (const plat of platArr) {
          const platId = `plat-${plat}`;
          if (!platformSet.has(platId)) platformSet.set(platId, { id: platId, label: plat, events: 0, group: 'platform' });
          platformSet.get(platId)!.events++;
        }

        const stratId = `strat-${gene.strategy}`;
        if (!strategySet.has(stratId)) strategySet.set(stratId, { id: stratId, label: gene.strategy, count: 0, successes: 0, group: 'strategy' });
        const s = strategySet.get(stratId)!;
        s.count++;
        s.successes += gene.successCount || 0;
      }

      for (const cat of categorySet.values()) nodes.push(cat);
      for (const plat of platformSet.values()) nodes.push(plat);
      for (const strat of strategySet.values()) nodes.push(strat);

      for (const cat of categorySet.values()) links.push({ source: 'helix', target: cat.id, type: 'core-cat' });
      for (const plat of platformSet.values()) links.push({ source: 'helix', target: plat.id, type: 'core-plat' });

      const seen = new Set<string>();
      for (const gene of genes) {
        const catId = `cat-${gene.category}`;
        const stratId = `strat-${gene.strategy}`;
        const k1 = `${catId}→${stratId}`;
        if (!seen.has(k1) && categorySet.has(catId) && strategySet.has(stratId)) { seen.add(k1); links.push({ source: catId, target: stratId, type: 'cat-strat' }); }

        const platArr = Array.isArray(gene.platforms) ? gene.platforms : (typeof gene.platforms === 'string' ? JSON.parse(gene.platforms || '[]') : []);
        for (const plat of platArr) {
          const platId = `plat-${plat}`;
          const k2 = `${platId}→${catId}`;
          if (!seen.has(k2) && platformSet.has(platId) && categorySet.has(catId)) { seen.add(k2); links.push({ source: platId, target: catId, type: 'plat-cat' }); }
        }
      }

      let oid = 0;
      for (const gene of genes) {
        const stratId = `strat-${gene.strategy}`;
        for (let i = 0; i < Math.min(gene.successCount || 0, 3); i++) {
          const id = `out-${oid++}`;
          nodes.push({ id, group: 'outcome', result: 'success' });
          links.push({ source: stratId, target: id, type: 'strat-out' });
        }
      }

      const totalOutcomes = nodes.filter(n => n.group === 'outcome').length;
      const healed = nodes.filter(n => n.result === 'success').length;
      const stratCounts: Record<string, number> = {};
      for (const s of strategySet.values()) stratCounts[s.label] = s.count;
      const totalStrat = Object.values(stratCounts).reduce((a: number, b: number) => a + b, 0) || 1;
      let shannonH = 0;
      Object.values(stratCounts).forEach((c: number) => { const p = c / totalStrat; if (p > 0) shannonH -= p * Math.log(p); });
      const richness = strategySet.size;
      const evenness = richness > 1 ? shannonH / Math.log(richness) : 1;
      const avgQ = genes.length > 0 ? genes.reduce((sum, g) => sum + (g.qValue || 0), 0) / genes.length : 0;

      return json(res, {
        nodes, links,
        stats: { totalGenes: genes.length, totalOutcomes, healed, failed: totalOutcomes - healed, successRate: totalOutcomes > 0 ? (healed / totalOutcomes * 100).toFixed(1) : '0', avgQ: avgQ.toFixed(3) },
        health: { shannonH: shannonH.toFixed(3), richness, evenness: evenness.toFixed(3) },
        strategyDistribution: Object.entries(stratCounts).sort((a, b) => (b[1] as number) - (a[1] as number)),
        platforms: Array.from(platformSet.values()).map(p => ({ name: p.label, events: p.events })),
      });
    }

    // GET /api/prompt-stats
    if (path === '/api/prompt-stats' && req.method === 'GET') {
      const { PromptOptimizer } = await import('./engine/prompt-optimizer.js');
      return json(res, new PromptOptimizer(geneMap.database).getStats());
    }
    // GET /api/prompt-examples
    if (path === '/api/prompt-examples' && req.method === 'GET') {
      const { PromptOptimizer } = await import('./engine/prompt-optimizer.js');
      return json(res, { examples: new PromptOptimizer(geneMap.database).getBestExamples() });
    }
    // GET /api/adapter-suggestions
    if (path === '/api/adapter-suggestions' && req.method === 'GET') {
      const { AdapterDiscovery } = await import('./engine/adapter-discovery.js');
      return json(res, { suggestions: new AdapterDiscovery(geneMap.database).getSuggestions() });
    }
    // GET /api/adapter-drafts
    if (path === '/api/adapter-drafts' && req.method === 'GET') {
      const { AdapterDiscovery } = await import('./engine/adapter-discovery.js');
      return json(res, { drafts: new AdapterDiscovery(geneMap.database).getDrafts() });
    }
    // POST /api/draft-adapter/:platform
    if (path.startsWith('/api/draft-adapter/') && req.method === 'POST') {
      const { AdapterDiscovery } = await import('./engine/adapter-discovery.js');
      const d = new AdapterDiscovery(geneMap.database).draftAdapter(decodeURIComponent(path.split('/')[3]));
      return d ? json(res, d) : json(res, { error: 'No suggestion found' }, 404);
    }

    // GET /api/weights
    if (path === '/api/weights' && req.method === 'GET') {
      const { AdaptiveWeights } = await import('./engine/adaptive-weights.js');
      const aw = new AdaptiveWeights(geneMap.database);
      return json(res, { weights: aw.getAllWeights(), defaults: aw.getDefaults() });
    }
    if (path.startsWith('/api/weights/') && !path.includes('history') && req.method === 'GET') {
      const { AdaptiveWeights } = await import('./engine/adaptive-weights.js');
      return json(res, { category: decodeURIComponent(path.split('/')[3]), weights: new AdaptiveWeights(geneMap.database).getWeights(decodeURIComponent(path.split('/')[3])) });
    }
    if (path === '/api/weights-history' && req.method === 'GET') {
      const { AdaptiveWeights } = await import('./engine/adaptive-weights.js');
      return json(res, { history: new AdaptiveWeights(geneMap.database).getHistory() });
    }

    // POST /api/generate-strategies
    if (path === '/api/generate-strategies' && req.method === 'POST') {
      try {
        let body: any = {}; try { body = JSON.parse(await readBody(req)); } catch {}
        const { StrategyGenerator } = await import('./engine/strategy-generator.js');
        return json(res, await new StrategyGenerator(geneMap.database).runCycle(Math.min(body?.max || 3, 10)));
      } catch (e) { return json(res, { error: String(e) }, 500); }
    }

    // GET /api/generated-strategies
    if (path === '/api/generated-strategies' && req.method === 'GET') {
      const { StrategyGenerator } = await import('./engine/strategy-generator.js');
      return json(res, { strategies: new StrategyGenerator(geneMap.database).getStrategies() });
    }

    // GET /api/strategy-gaps
    if (path === '/api/strategy-gaps' && req.method === 'GET') {
      const { StrategyGenerator } = await import('./engine/strategy-generator.js');
      return json(res, { gaps: new StrategyGenerator(geneMap.database).analyzeGaps() });
    }

    // POST /api/self-play
    if (path === '/api/self-play' && req.method === 'POST') {
      try {
        const body = JSON.parse(await readBody(req));
        const { SelfPlayEngine } = await import('./engine/self-play.js');
        const sp = new SelfPlayEngine(geneMap.database);
        return json(res, await sp.runSession(Math.min(body.rounds || 5, 50)));
      } catch (e) { return json(res, { error: String(e) }, 500); }
    }

    // GET /api/self-play/stats
    if (path === '/api/self-play/stats' && req.method === 'GET') {
      const { SelfPlayEngine } = await import('./engine/self-play.js');
      return json(res, new SelfPlayEngine(geneMap.database).getStats());
    }

    // GET /api/self-play/history
    if (path === '/api/self-play/history' && req.method === 'GET') {
      const { SelfPlayEngine } = await import('./engine/self-play.js');
      return json(res, { history: new SelfPlayEngine(geneMap.database).getHistory() });
    }

    // GET /api/adversarial-stats
    if (path === '/api/adversarial-stats' && req.method === 'GET') {
      const { AdversarialDefense } = await import('./engine/adversarial.js');
      return json(res, new AdversarialDefense(geneMap.database).getStats());
    }

    // GET /api/reputation/:agentId
    if (path.startsWith('/api/reputation/') && req.method === 'GET') {
      const { AdversarialDefense } = await import('./engine/adversarial.js');
      return json(res, new AdversarialDefense(geneMap.database).getReputation(decodeURIComponent(path.split('/')[3])));
    }

    // GET /api/meta-patterns
    if (path === '/api/meta-patterns' && req.method === 'GET') {
      const { MetaLearner } = await import('./engine/meta-learner.js');
      return json(res, { patterns: new MetaLearner(geneMap.database).getPatterns() });
    }

    // POST /api/meta-learn
    if (path === '/api/meta-learn' && req.method === 'POST') {
      const { MetaLearner } = await import('./engine/meta-learner.js');
      return json(res, { newPatterns: new MetaLearner(geneMap.database).learnFromGeneMap() });
    }

    // GET /api/causal-graph
    if (path === '/api/causal-graph' && req.method === 'GET') {
      const { CausalGraph } = await import('./engine/causal-graph.js');
      return json(res, new CausalGraph(geneMap.database).getFullGraph());
    }

    // GET /api/anti-patterns
    if (path === '/api/anti-patterns' && req.method === 'GET') {
      const { NegativeKnowledge } = await import('./engine/negative-knowledge.js');
      const nk = new NegativeKnowledge(geneMap.database);
      return json(res, { antiPatterns: nk.getAll(), total: nk.count() });
    }

    // ── Gene Collector Endpoints ──

    // POST /api/telemetry — receive anonymous discoveries
    if (path === '/api/telemetry' && req.method === 'POST') {
      try {
        const body = JSON.parse(await readBody(req));
        const events = body.events;
        if (!Array.isArray(events) || events.length === 0) {
          return json(res, { error: 'events array required' }, 400);
        }
        const batch = events.slice(0, 100);
        const ins = collectorDb.prepare(`INSERT INTO gene_discoveries (error_pattern, code, category, severity, strategy, q_value, source, reasoning, llm_provider, platform, helix_version, reported_at, avg_q) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) ON CONFLICT(code, category, strategy, platform) DO UPDATE SET report_count = report_count + 1, avg_q = (avg_q * report_count + excluded.q_value) / (report_count + 1), updated_at = unixepoch(), reasoning = COALESCE(excluded.reasoning, reasoning), helix_version = excluded.helix_version`);
        collectorDb.transaction(() => {
          for (const e of batch) {
            ins.run(e.errorPattern, e.code, e.category, e.severity ?? 'medium', e.strategy, e.qValue ?? 0.5, e.source ?? 'unknown', e.reasoning, e.llmProvider, e.platform ?? 'generic', e.helixVersion, e.timestamp ?? Date.now(), e.qValue ?? 0.5);
          }
        })();
        return json(res, { received: batch.length });
      } catch (e) {
        return json(res, { error: String(e) }, 500);
      }
    }

    // GET /api/discoveries — list discoveries for review
    if (path === '/api/discoveries' && req.method === 'GET') {
      if (!requireAdmin(req, res)) return;
      const approved = url.searchParams.get('approved') === 'true';
      const rows = approved
        ? collectorDb.prepare('SELECT * FROM gene_discoveries WHERE approved = 1 ORDER BY created_at DESC').all()
        : collectorDb.prepare('SELECT * FROM gene_discoveries WHERE reviewed = 0 ORDER BY created_at DESC LIMIT 100').all();
      return json(res, rows);
    }

    // POST /api/discoveries/:id/approve
    if (path.startsWith('/api/discoveries/') && path.endsWith('/approve') && req.method === 'POST') {
      if (!requireAdmin(req, res)) return;
      const id = path.split('/')[3];
      collectorDb.prepare('UPDATE gene_discoveries SET reviewed = 1, approved = 1 WHERE id = ?').run(id);
      return json(res, { approved: true });
    }

    // POST /api/discoveries/:id/reject
    if (path.startsWith('/api/discoveries/') && path.endsWith('/reject') && req.method === 'POST') {
      if (!requireAdmin(req, res)) return;
      const id = path.split('/')[3];
      collectorDb.prepare('UPDATE gene_discoveries SET reviewed = 1, approved = 0 WHERE id = ?').run(id);
      return json(res, { rejected: true });
    }

    json(res, { error: 'Not found' }, 404);
  });

  return {
    start: () => new Promise<void>((resolve) => {
      server.listen(port, () => {
        console.log(`\n  \x1b[36m╔═══════════════════════════════════════╗\x1b[0m`);
        console.log(`  \x1b[36m║\x1b[0m  \x1b[1mHELIX API SERVER\x1b[0m                      \x1b[36m║\x1b[0m`);
        console.log(`  \x1b[36m╚═══════════════════════════════════════╝\x1b[0m`);
        console.log(`  http://localhost:${port}`);
        console.log(`  Mode: ${mode} | Genes: ${geneMap.health().totalGenes}`);
        console.log(`\n  POST /repair  — diagnose + repair`);
        console.log(`  GET  /health  — healthcheck`);
        console.log(`  GET  /status  — Gene Map stats`);
        console.log(`  GET  /genes   — list all genes`);
        if (betaEnabled) {
          console.log(`\n  \x1b[33m🧪 Beta mode: VialOS integration enabled\x1b[0m`);
          console.log(`  GET  /vial/status  — VialOS runtime info`);
          console.log(`  Dashboard: "Powered by VialOS Runtime" badge`);
        }
        console.log();
        resolve();
      });
    }),
    stop: () => new Promise<void>((resolve) => {
      geneMap.close();
      vialGeneMap.close();
      server.close(() => resolve());
    }),
    server,
    engine,
    geneMap,
    vialGeneMap,
  };
}
