// src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';
import { DatabaseError, UniqueConstraintError, ForeignKeyConstraintError, NotFoundError, ValidationError, ValidationIssue } from '../services/db/errors.db';
import crypto from 'crypto';

interface HttpError extends Error {
  status?: number;
}

export const errorHandler = (err: HttpError, req: Request, res: Response, next: NextFunction) => {
  // If the response headers have already been sent, we must delegate to the default Express error handler.
  if (res.headersSent) {
    return next(err);
  }

  // The pino-http middleware guarantees that `req.log` will be available.
  const log = req.log;

  // --- 1. Determine Final Status Code and Message ---
  let statusCode = err.status ?? 500;
  const message = err.message;
  let validationIssues: ValidationIssue[] | undefined;
  let errorId: string | undefined;

  // Refine the status code for known error types. Check for most specific types first.
  if (err instanceof UniqueConstraintError) {
    statusCode = 409; // Conflict
  } else if (err instanceof NotFoundError) {
    statusCode = 404;
  } else if (err instanceof ForeignKeyConstraintError) {
    statusCode = 400;
  } else if (err instanceof ValidationError) {
    statusCode = 400;
    validationIssues = err.validationErrors;
  } else if (err instanceof DatabaseError) {
    // This is a generic fallback for other database errors that are not the specific subclasses above.
    statusCode = err.status;
  } else if (err.name === 'UnauthorizedError') {
    statusCode = err.status || 401;
  }

  // --- 2. Log Based on Final Status Code ---
  // Log the full error details for debugging, especially for server errors.
  if (statusCode >= 500) {
    errorId = crypto.randomBytes(4).toString('hex');
    // The request-scoped logger already contains user, IP, and request_id.
    // We add the full error and the request object itself.
    // Pino's `redact` config will automatically sanitize sensitive fields in `req`.
    log.error({ err, errorId, req: { method: req.method, url: req.originalUrl, headers: req.headers, body: req.body } }, `Unhandled API Error (ID: ${errorId})`);
  } else {
    // For 4xx errors, log at a lower level (e.g., 'warn') to avoid flooding error trackers.
    // We include the validation errors in the log context if they exist.
    log.warn(
      {
        err,
        validationErrors: validationIssues, // Add validation issues to the log object
        statusCode,
      },
      `Client Error on ${req.method} ${req.path}: ${message}`
    );
  }

  // --- TEST ENVIRONMENT DEBUGGING ---
  if (process.env.NODE_ENV === 'test') {
    console.error('--- [TEST] UNHANDLED ERROR ---', err);
  }

  // --- 3. Send Response ---
  // In production, send a generic message for 5xx errors.
  // In dev/test, send the actual error message for easier debugging.
  const responseMessage = (statusCode >= 500 && process.env.NODE_ENV === 'production')
    ? `An unexpected server error occurred. Please reference error ID: ${errorId}`
    : message;

  res.status(statusCode).json({
    message: responseMessage,
    ...(validationIssues && { errors: validationIssues }), // Conditionally add the 'errors' array if it exists
  });
};