/**
 * middleware/csrf.js
 * Double-submit cookie pattern (works without csurf's session pitfalls).
 * - Issues a CSRF token in session and a matching `ncsrf` cookie.
 * - On unsafe methods, compares header `x-csrf-token` (or body `_csrf`) to session.
 *
 * Exemptions (no CSRF check on unsafe methods):
 *   - GET / HEAD / OPTIONS
 *   - Public auth POSTs: /auth/* and /2fa/auth/login/2fa
 *     (the login page can't get a session-bound token before it has a session)
 *   - Any request carrying an `Authorization: Bearer …` header.
 *     CSRF only matters for cookie-based auth; bearer-token callers
 *     (Agency REST API at /api/v1/*) authenticate out-of-band and CSRF
 *     is meaningless. CORS would block cross-origin browser attacks on
 *     these endpoints anyway, and the routes themselves check the token
 *     validity against the api_tokens table.
 */

'use strict';

const crypto = require('crypto');

function isBearerAuth(req) {
  const h = req.get && req.get('authorization');
  return !!h && /^Bearer\s+[A-Za-z0-9_\-]{8,}$/i.test(h);
}

function issue(req, res, next) {
  // Login/registration flows are public — issue cookie but don't enforce.
  // The 2FA verify endpoint is also exempt (called from the login page
  // before the user has a "real" session).
  const isPublicPost =
       req.path.startsWith('/auth/')
    || req.path === '/auth'
    || req.path === '/2fa/auth/login/2fa';

  if (req.session) {
    if (!req.session.csrfToken) {
      req.session.csrfToken = crypto.randomBytes(24).toString('hex');
    }
    // Always (re-)sync the cookie to whatever the session holds.
    res.cookie('ncsrf', req.session.csrfToken, {
      httpOnly: false,
      sameSite: 'lax',
      secure: process.env.NODE_ENV === 'production',
    });
  }

  const isSafeMethod = req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS';
  if (isSafeMethod || isPublicPost || isBearerAuth(req)) {
    return next();
  }
  if (!req.session || !req.session.csrfToken) {
    return res.status(403).json({ error: 'CSRF_NO_TOKEN' });
  }
  const header = req.get('x-csrf-token') || req.body && req.body._csrf;
  if (!header || header !== req.session.csrfToken) {
    return res.status(403).json({ error: 'CSRF_MISMATCH' });
  }
  next();
}

module.exports = issue;
