// Error & validation middleware.
//
// Issue #101 — the previous handler echoed every error's `.message` straight
// to the client, leaking raw Postgres column names, schema details, and
// invalid UUID syntax errors to anyone hitting a malformed route.
//
// Issue #102 — every /:id route was hitting Postgres with the raw param,
// returning a 500 (with a PG error in the body) instead of a clean 400.
//
// Both are addressed here: `validateUuid` checks param shape before the
// route runs; `errorHandler` keeps detailed messages server-side and only
// surfaces a generic message + the response status to the client.

const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

export function validateUuid(paramName = 'id') {
  return (req, res, next) => {
    const v = req.params[paramName];
    if (!v || !UUID_RE.test(v)) {
      return res.status(400).json({ error: `Invalid ${paramName} — must be a UUID` });
    }
    next();
  };
}

// Patterns Postgres uses for its error codes that are operator-only noise.
const PG_LEAKY_CODES = new Set([
  '22P02', // invalid_text_representation (bad UUID, etc.)
  '23502', // not_null_violation
  '23503', // foreign_key_violation
  '23505', // unique_violation
  '42703', // undefined_column
  '42P01', // undefined_table
  '42601', // syntax_error
]);

const GENERIC_MESSAGES = {
  '22P02': 'Invalid input format',
  '23502': 'Required field missing',
  '23503': 'Referenced record not found',
  '23505': 'Record already exists',
  '42703': 'Internal database error',
  '42P01': 'Internal database error',
  '42601': 'Internal database error',
};

export const errorHandler = (err, req, res, next) => {
  // Log the full error server-side; operators get the detail.
  console.error('[error]', req.method, req.originalUrl, err);

  // Postgres errors carry a `.code` (string from SQLSTATE).
  if (err && err.code && PG_LEAKY_CODES.has(err.code)) {
    const generic = GENERIC_MESSAGES[err.code] || 'Database error';
    const status  = err.code === '22P02' || err.code === '23502' ? 400 : 409;
    return res.status(status).json({ error: generic, code: err.code });
  }

  const status = err.status || 500;

  // 5xx — never let a raw Error.message escape; clients get a stable shape.
  if (status >= 500) {
    return res.status(status).json({
      error: 'Internal Server Error',
      status,
    });
  }

  // 4xx — operator-authored messages are safe to surface.
  return res.status(status).json({
    error: err.message || 'Bad request',
    status,
  });
};
