// Static file server for the Brain Dashboard. Serves the repo root over
// localhost so dashboard.html can fetch() episodes-*.jsonl (file:// cannot).
// Run: node tools/brain-dashboard-server.mjs   (npm run brain:dashboard)
import { createServer as httpCreateServer } from 'node:http';
import { readFileSync, existsSync, statSync, readdirSync } from 'node:fs';
import { join, resolve, extname, sep } from 'node:path';
import { fileURLToPath } from 'node:url';

const REPO_ROOT = resolve(fileURLToPath(import.meta.url), '..', '..');
const PORT = Number(process.env.BRAIN_DASHBOARD_PORT) || 7700;

const MIME = {
  '.html': 'text/html; charset=utf-8',
  '.js': 'text/javascript; charset=utf-8',
  '.css': 'text/css; charset=utf-8',
  '.json': 'application/json; charset=utf-8',
  '.jsonl': 'application/x-ndjson; charset=utf-8',
  '.svg': 'image/svg+xml',
};

export function contentType(ext) {
  return MIME[ext] || 'application/octet-stream';
}

export function listEpisodeFiles(root) {
  const dir = join(root, 'docs', 'observer');
  if (!existsSync(dir)) return [];
  return readdirSync(dir)
    .filter((f) => /^episodes-\d{4}-\d{2}\.jsonl$/.test(f))
    .sort();
}

// Resolve a URL path to an absolute path inside root; null if it escapes root.
export function resolveStaticPath(urlPath, root) {
  const clean = decodeURIComponent(urlPath.split('?')[0]).replace(/^\/+/, '');
  // Use resolve for the traversal check (canonicalizes both sides consistently)
  const normRoot = resolve(root);
  const abs = resolve(normRoot, clean);
  if (abs !== normRoot && !abs.startsWith(normRoot + sep)) return null;
  // Return join-based path so callers get root-relative path with root's own separators
  return join(root, clean);
}

export function createServer(root = REPO_ROOT) {
  return httpCreateServer((req, res) => {
    const url = req.url || '/';
    if (url.split('?')[0] === '/api/episodes') {
      res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
      res.end(JSON.stringify(listEpisodeFiles(root)));
      return;
    }
    let path = url.split('?')[0];
    if (path === '/') {
      // Redirect (not rewrite) so the browser's base URL becomes /docs/observer/,
      // which makes relative <script src="dashboard.js"> and ../automation-graph-data.js resolve correctly.
      res.writeHead(302, { Location: '/docs/observer/dashboard.html' });
      res.end();
      return;
    }
    const abs = resolveStaticPath(path, root);
    if (!abs || !existsSync(abs) || !statSync(abs).isFile()) {
      res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
      res.end('404');
      return;
    }
    res.writeHead(200, { 'Content-Type': contentType(extname(abs)) });
    res.end(readFileSync(abs));
  });
}

if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
  createServer().listen(PORT, '127.0.0.1', () => {
    console.log(`Brain Dashboard: http://localhost:${PORT}/  (Ctrl+C to stop)`);
  });
}
