/**
 * The ONLY place in the codebase that writes an error response body
 * (PLANNING.md §5). Controllers/services throw AppError; everything else that
 * reaches here is treated as an unexpected 500 and its detail is logged, never
 * sent.
 *
 * Two envelopes, chosen by `AppError.envelope`:
 *   'plain'    -> { error, fields?, requestId }   (default — health, gallery, admin)
 *   'ok-false' -> { ok: false, error, fields? }   (POST /enquiries, so the
 *                 frontend's enquiryResponseSchema discriminated union parses)
 */
import type { NextFunction, Request, Response } from 'express';
import { AppError } from '../lib/AppError';
import { logger } from '../lib/logger';

export function errorHandler(
  err: unknown,
  req: Request,
  res: Response,
  // Express only treats a middleware as an error handler if it declares 4 params.
  _next: NextFunction,
): void {
  const isAppError = err instanceof AppError;
  const status = isAppError ? err.status : 500;

  if (status >= 500) {
    logger.error({ err, requestId: req.id, method: req.method, path: req.path }, 'unhandled error');
  } else {
    logger.warn(
      { requestId: req.id, method: req.method, path: req.path, status },
      isAppError ? err.message : 'client error',
    );
  }

  if (res.headersSent) return;

  const message = isAppError && err.expose ? err.message : 'Internal server error';

  if (isAppError && err.envelope === 'ok-false') {
    const body: { ok: false; error: string; fields?: Record<string, string> } = {
      ok: false,
      error: message,
    };
    if (err.fields) body.fields = err.fields;
    res.status(status).json(body);
    return;
  }

  const body: { error: string; fields?: Record<string, string>; requestId: string } = {
    error: message,
    requestId: req.id,
  };
  if (isAppError && err.fields) body.fields = err.fields;
  res.status(status).json(body);
}
