'use strict';

/* ── Error handling middleware ─────────────────────────────────────────────
   requestId  — attach X-Request-Id to every request (use early in middleware chain)
   errorHandler — 4-arg Express error handler (use as last app.use)
   ──────────────────────────────────────────────────────────────────────── */

const crypto = require('crypto');
const logger = require('../utils/logger');
let _errorLogStmt = null;
function getErrorLogStmt() {
  if (!_errorLogStmt) {
    try {
      const db = require('../db/db');
      _errorLogStmt = db.prepare(
        'INSERT INTO error_log (level, message, stack, route, method, user_id) VALUES (?, ?, ?, ?, ?, ?)'
      );
    } catch {}
  }
  return _errorLogStmt;
}

/**
 * Attaches a unique request ID to req.requestId and sets X-Request-Id header.
 * Honour an incoming X-Request-Id from trusted proxies/gateways when present.
 */
function requestId(req, res, next) {
  const id = crypto.randomUUID();
  req.requestId = id;
  res.setHeader('X-Request-Id', id);
  next();
}

/**
 * Global error handler — must be registered AFTER all routes.
 *
 * Classifies errors:
 *   operational  (4xx) — expected client errors → warn level, message returned as-is
 *   programmer   (5xx) — unexpected bugs        → error level, stack logged, message hidden in prod
 */
function errorHandler(err, req, res, _next) {
  const status = err.status || err.statusCode || 500;
  const isOperational = status >= 400 && status < 500;
  const isProd = process.env.NODE_ENV === 'production';

  const meta = {
    requestId: req.requestId,
    method:    req.method,
    path:      req.path,
    status,
    userId:    req.user?.id,
    role:      req.user?.role,
  };

  if (isOperational) {
    logger.warn(err.message || 'Client error', meta);
  } else {
    logger.error(err.message || 'Unhandled error', {
      ...meta,
      stack: !isProd ? err.stack : undefined,
    });
    // Persist to error_log table for admin dashboard
    try {
      const s = getErrorLogStmt();
      if (s) s.run('error', (err.message || 'Unknown').slice(0, 1000), (err.stack || '').slice(0, 4000), req.path, req.method, req.user?.id || null);
    } catch {}
  }

  const message = isProd && !isOperational
    ? 'Internal server error'
    : (err.message || 'Server error');

  if (res.headersSent) return;
  res.status(status).json({ error: message, requestId: req.requestId });
}

module.exports = { requestId, errorHandler };
