/**
 * Centralized error handling utility for API routes
 *
 * Standard error format:
 * {
 *   error: 'ErrorType',
 *   message: 'Human-readable description',
 *   field: 'optional-field-name',
 *   code: 'machine-readable-code'
 * }
 */

import type { Req, Res, Next } from '../types/http';

class ApiError extends Error {
  code: string;
  status: number;
  details: any;
  field?: string;

  constructor(code: string, message: string, status = 500, details: any = {}) {
    super(message);
    this.name = 'ApiError';
    this.code = code;
    this.status = status;
    this.details = details;

    // Extract field name from details if provided
    if (details.field) {
      this.field = details.field;
    }
  }
}

/**
 * Create a standardized validation error
 */
function ValidationError(message: string, field?: string, code = 'VALIDATION_ERROR'): ApiError {
  return new ApiError(code, message, 400, { field });
}

/**
 * Create a standardized authentication error
 */
function AuthError(message = 'Authentication required', code = 'AUTH_ERROR'): ApiError {
  return new ApiError(code, message, 401);
}

/**
 * Create a standardized authorization error
 */
function ForbiddenError(message = 'Access denied', code = 'FORBIDDEN'): ApiError {
  return new ApiError(code, message, 403);
}

/**
 * Create a standardized not found error
 */
function NotFoundError(
  message = 'Resource not found',
  field?: string,
  code = 'NOT_FOUND',
): ApiError {
  return new ApiError(code, message, 404, { field });
}

/**
 * Create a standardized conflict error
 */
function ConflictError(message = 'Resource conflict', field?: string, code = 'CONFLICT'): ApiError {
  return new ApiError(code, message, 409, { field });
}

/**
 * Create a standardized rate limit error
 */
function RateLimitError(message = 'Too many requests', code = 'RATE_LIMITED'): ApiError {
  return new ApiError(code, message, 429);
}

/**
 * Format an error for JSON response
 * This ensures consistent error format across all routes
 */
function formatError(err: any): { error: string; message: string; code: string; field?: string } {
  if (err instanceof ApiError) {
    // `error` and `code` are both the machine code (the client reads `code`);
    // `error` is kept for backward compatibility.
    const output: { error: string; message: string; code: string; field?: string } = {
      error: err.code,
      message: err.message,
      code: err.code,
    };
    if (err.field) output.field = err.field;
    return output;
  }

  // Non-ApiError: hide internals on any 5xx (missing status is treated as 500).
  const status = err.status || 500;
  const code = err.code || 'INTERNAL_ERROR';
  const safeMessage = status >= 500 ? 'Internal server error' : err.message || 'An error occurred';

  return {
    error: code,
    message: safeMessage,
    code,
  };
}

/**
 * Express middleware to handle errors centrally
 */
function errorHandler(err: any, req: Req, res: Res, next: Next): void {
  const formatted = formatError(err);
  const status = err.status || (err instanceof ApiError ? 500 : 500);

  // Only log non-500 errors to avoid exposing sensitive info
  if (status !== 500) {
    console.error(`[error] ${formatted.error}: ${formatted.message}`);
  }

  if (!res.headersSent) {
    res.status(status).json(formatted);
  }
}

module.exports = {
  ApiError,
  ValidationError,
  AuthError,
  ForbiddenError,
  NotFoundError,
  ConflictError,
  RateLimitError,
  formatError,
  errorHandler,
};
