/**
 * middleware/timeout.js
 *
 * Global request-timeout middleware. Every route gets a hard
 * deadline — if the handler doesn't call res.end() within the
 * budget, the middleware tears down the response with a clean
 * 504 Gateway Timeout and a JSON error envelope.
 *
 * Defaults: 30s for normal routes, 2 min for /scan/* and
 * /cases/:id/discover* (deep discovery can be slow).
 *
 * Why: a hung handler (DB lock, Scrape.do hang, infinite loop in
 * a regex) used to leave the request hanging forever, blocking
 * the connection and never returning a response. With this
 * middleware, the worst case is a clean 504.
 */

'use strict';

const DEFAULT_TIMEOUT_MS = parseInt(process.env.REQUEST_TIMEOUT_MS, 10) || 30 * 1000;

const LONG_ROUTES = [
  { prefix: '/scan/',           timeout: 120 * 1000 },
  { prefix: '/cases/',          timeout: 180 * 1000 },
  { prefix: '/findings/',       timeout: 90 * 1000  },
  { prefix: '/admin/production-pending/', timeout: 60 * 1000 },
  { prefix: '/admin/keys',      timeout: 60 * 1000  },
  { prefix: '/admin/users',     timeout: 60 * 1000  },
];

function pickTimeout(path) {
  for (const r of LONG_ROUTES) {
    if (path === r.prefix.replace(/\/$/, '') || path.startsWith(r.prefix)) return r.timeout;
  }
  return DEFAULT_TIMEOUT_MS;
}

function timeout(req, res, next) {
  // Skip for long-polling or SSE (we don't have any yet, but be safe)
  if (req.path === '/health/live' || req.path === '/health') return next();

  const ms = pickTimeout(req.path);
  let done = false;

  const timer = setTimeout(() => {
    if (done) return;
    done = true;
    if (!res.headersSent) {
      res.setHeader('X-Timeout-Ms', String(ms));
      res.setHeader('Content-Type', 'application/json');
      res.status(504).json({
        error: 'REQUEST_TIMEOUT',
        timeout_ms: ms,
        path: req.path,
        message: 'The request exceeded the server-side timeout. Try again, or contact support with this ref.',
      });
    }
    try { res.end(); } catch (_) {}
  }, ms);

  // Clear the timer once the response is sent
  const done1 = res.end.bind(res);
  res.end = function(...args) {
    if (done) return;
    done = true;
    clearTimeout(timer);
    return done1(...args);
  };
  res.on('close', () => { if (!done) { done = true; clearTimeout(timer); } });

  next();
}

module.exports = { timeout, DEFAULT_TIMEOUT_MS, pickTimeout };
