/**
 * Security Center API routes
 *
 * Endpoints (all under /api/v1/security):
 *
 *   GET    /events                    — Query events (filters: source_type, source_host,
 *                                       severity, outcome, actor, action, since, until,
 *                                       target; pagination via limit/offset)
 *   GET    /events/stats              — Aggregations (top actors, top targets,
 *                                       counts by source/severity/host)
 *   GET    /events/:id                — Single event by id
 *   GET    /events/stream             — Server-Sent Events live tail (auth required)
 *
 *   POST   /events/ingest             — Single ingest (auth: Bearer host-api-key)
 *   POST   /events/batch              — Batch ingest (auth: Bearer host-api-key)
 *
 *   GET    /hosts                     — List registered hosts
 *   POST   /hosts                     — Register new host
 *   GET    /hosts/:id                 — Host details
 *   PATCH  /hosts/:id                 — Update host (label, type, enabled, meta)
 *   DELETE /hosts/:id                 — Deregister host
 *   GET    /hosts/:id/health          — Host health summary
 *
 *   POST   /hosts/:id/rotate-key      — Rotate host api_key
 *
 * Most endpoints require TOTP/JWT/API-key auth like the rest of the dashboard.
 * The /events/ingest and /events/batch endpoints accept per-host Bearer tokens
 * AND must be added to the PUBLIC_ROUTES allowlist in middleware.js so they
 * don't require TOTP. Per-host auth replaces TOTP for those endpoints.
 */

const express = require('express');
// DC-063: use the canonical `errorResponse(res, statusCode, message, extras)`
// shape — alias `error: errorResponse` used here previously was message-first
// which silently mis-called every callsite (15 endpoints surfaced as 500 HTML
// panics instead of the intended 4xx JSON).
const { ok, errorResponse } = require('../src/utils/responses');
const { getStore } = require('../src/security/event-store');
const { getRegistry } = require('../src/security/host-registry');
const platformPaths = require('../platform-paths');

module.exports = function({ log }) {
  const router = express.Router();
  const store = getStore({ log });
  const registry = getRegistry({ log });

  // ===================== EVENTS =====================

  // GET /events — list/query
  router.get('/events', (req, res) => {
    const result = store.query({
      limit:       req.query.limit,
      offset:      req.query.offset,
      source_type: req.query.source_type,
      source_host: req.query.source_host,
      severity:    req.query.severity,
      outcome:     req.query.outcome,
      actor:       req.query.actor,
      actor_prefix: req.query.actor_prefix,
      action:      req.query.action,
      target:      req.query.target,
      since:       req.query.since,
      until:       req.query.until,
    });
    ok(res, result);
  });

  // GET /events/stats — aggregations
  router.get('/events/stats', (req, res) => {
    const stats = store.stats({
      since: req.query.since,
    });
    ok(res, stats);
  });

  // GET /events/stream — SSE live tail (must come BEFORE /events/:id!)
  router.get('/events/stream', (req, res) => {
    res.writeHead(200, {
      'Content-Type':  'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection':    'keep-alive',
      'X-Accel-Buffering': 'no',
    });
    // Initial sync — send last 20 events so the UI isn't empty
    const initial = store.query({ limit: 20 });
    res.write(`event: init\ndata: ${JSON.stringify(initial)}\n\n`);

    const onEvent = (ev) => {
      try { res.write(`event: security\ndata: ${JSON.stringify(ev)}\n\n`); }
      catch (_) { cleanup(); }
    };
    const heartbeat = setInterval(() => {
      try { res.write(`: heartbeat ${Date.now()}\n\n`); }
      catch (_) { cleanup(); }
    }, 30000);

    function cleanup() {
      store.off('event', onEvent);
      clearInterval(heartbeat);
    }

    store.on('event', onEvent);
    req.on('close', cleanup);
    req.on('aborted', cleanup);
  });

  // GET /events/:id — single event
  router.get('/events/:id', (req, res) => {
    const ev = store.get(req.params.id);
    if (!ev) return errorResponse(res, 404, 'event not found');
    ok(res, ev);
  });

  // ===================== INGEST =====================

  // POST /events/ingest — single event from an authenticated host
  router.post('/events/ingest', (req, res) => {
    const host = _authHost(req, res);
    if (!host) return;
    if (!req.body || typeof req.body !== 'object') {
      return errorResponse(res, 400, 'event body required');
    }
    try {
      const event = store.append({
        ...req.body,
        source_host: host.id,         // override — server is source of truth on host id
        source_type: req.body.source_type || 'agent',
      });
      ok(res, { id: event.id, accepted: true });
    } catch (e) {
      errorResponse(res, 400, e.message);
    }
  });

  // POST /events/batch — multiple events (more efficient for agents)
  router.post('/events/batch', (req, res) => {
    const host = _authHost(req, res);
    if (!host) return;
    const events = Array.isArray(req.body?.events) ? req.body.events : null;
    if (!events) return errorResponse(res, 400, 'events[] required');
    if (events.length > 500) return errorResponse(res, 413, 'batch too large (max 500)');

    const accepted = [];
    const errors = [];
    for (const ev of events) {
      try {
        const stored = store.append({
          ...ev,
          source_host: host.id,
          source_type: ev.source_type || 'agent',
        });
        accepted.push(stored.id);
      } catch (e) {
        errors.push({ error: e.message, event: ev });
      }
    }
    ok(res, { accepted: accepted.length, errors: errors.length, ids: accepted, error_details: errors });
  });

  // ===================== HOSTS =====================

  // GET /hosts — list
  router.get('/hosts', (req, res) => {
    ok(res, { hosts: registry.list() });
  });

  // POST /hosts — register new
  router.post('/hosts', (req, res) => {
    const { id, label, type, meta, enabled } = req.body || {};
    if (!id) return errorResponse(res, 400, 'id required');
    try {
      const { host, api_key } = registry.register({ id, label, type, meta, enabled });
      // api_key returned EXACTLY ONCE — caller must store it now
      ok(res, { host, api_key, notice: 'store this api_key now — it will not be shown again' });
    } catch (e) {
      errorResponse(res, 409, e.message);
    }
  });

  // GET /hosts/:id
  router.get('/hosts/:id', (req, res) => {
    const h = registry.get(req.params.id);
    if (!h) return errorResponse(res, 404, 'host not found');
    ok(res, h);
  });

  // PATCH /hosts/:id
  router.patch('/hosts/:id', (req, res) => {
    const updated = registry.update(req.params.id, req.body || {});
    if (!updated) return errorResponse(res, 404, 'host not found');
    ok(res, updated);
  });

  // DELETE /hosts/:id
  router.delete('/hosts/:id', (req, res) => {
    try {
      const ok_ = registry.remove(req.params.id);
      if (!ok_) return errorResponse(res, 404, 'host not found');
      ok(res, { removed: true });
    } catch (e) {
      errorResponse(res, 400, e.message);
    }
  });

  // GET /hosts/:id/health — last_seen, event rate, status
  router.get('/hosts/:id/health', (req, res) => {
    const host = registry.get(req.params.id);
    if (!host) return errorResponse(res, 404, 'host not found');

    const last24h = new Date(Date.now() - 24*60*60*1000).toISOString();
    const events24h = store.query({ source_host: req.params.id, since: last24h, limit: 1000 });
    const sev = events24h.events.reduce((acc, e) => {
      acc[e.severity] = (acc[e.severity] || 0) + 1;
      return acc;
    }, {});

    const lastEvent = events24h.events[0] || null;

    ok(res, {
      host,
      events_24h: events24h.total,
      severity_breakdown_24h: sev,
      last_event_at: lastEvent?.ts || null,
      last_event_id: lastEvent?.id || null,
      status: !host.enabled ? 'disabled'
            : !host.last_seen_at ? 'registered'
            : (Date.now() - Date.parse(host.last_seen_at) > 30*60*1000) ? 'stale'
            : 'online',
    });
  });

  // POST /hosts/:id/rotate-key — issue a new key, return it once
  // (Implementation note: rotate would need to keep _raw_key retrieval. For v1
  // we'll document this as "deferred — re-register instead". The endpoint
  // returns 501 with a clear message so callers don't get silently no-op'd.)
  router.post('/hosts/:id/rotate-key', (req, res) => {
    errorResponse(res, 501, 'rotate-key deferred in v1 — re-register the host to get a new key');
  });

  // ===================== HELPERS =====================

  function _authHost(req, res) {
    const auth = req.headers.authorization || '';
    const m = auth.match(/^Bearer\s+(.+)$/);
    if (!m) {
      errorResponse(res, 401, 'Bearer token required');
      return null;
    }
    const host = registry.authenticate(m[1]);
    if (!host) {
      errorResponse(res, 401, 'invalid or disabled host key');
      return null;
    }
    return host;
  }

  return router;
};