/**
 * Global error handler for Fastify
 * Provides consistent error format and masks sensitive information in production
 */

import { FastifyError, FastifyRequest, FastifyReply } from 'fastify';
import { env, createLogger } from '@4runr/shared';
import { getCorrelationId } from './correlationId.js';

const logger = createLogger('Gateway:ErrorHandler');

/**
 * Standard error response interface
 */
export interface ErrorResponse {
  error: string;
  message: string;
  correlationId?: string;
  timestamp: string;
  path?: string;
  statusCode?: number;
  stack?: string;
}

/**
 * Mask sensitive error information in production
 */
function maskError(error: Error | FastifyError, isProduction: boolean): Partial<ErrorResponse> {
  if (!isProduction) {
    // Development: Show full error details
    return {
      error: error.name || 'Error',
      message: error.message,
      ...(error.stack && { stack: error.stack }),
    };
  }

  // Production: Mask sensitive details
  const statusCode = (error as FastifyError).statusCode || 500;
  
  if (statusCode >= 500) {
    // Server errors: Don't leak internal details
    return {
      error: 'Internal Server Error',
      message: 'An unexpected error occurred. Please try again later.',
    };
  }

  // Client errors: Safe to show message
  return {
    error: error.name || 'Error',
    message: error.message,
  };
}

/**
 * Global error handler
 */
export function errorHandler(
  error: FastifyError | Error,
  request: FastifyRequest,
  reply: FastifyReply
): void {
  const isProduction = env.NODE_ENV === 'production';
  const correlationId = getCorrelationId(request);
  const statusCode = (error as FastifyError).statusCode || 500;

  // Get masked error response
  const maskedError = maskError(error, isProduction);

  // Build error response - ensure all required fields are present
  const errorResponse: ErrorResponse = {
    error: maskedError.error || 'Error',
    message: maskedError.message || 'An error occurred',
    ...(correlationId && { correlationId }),
    timestamp: new Date().toISOString(),
    path: request.url,
    statusCode,
    ...(maskedError.stack && { stack: maskedError.stack }),
  };

  // Log error with full details (even in production, but sanitized)
  const route = (request as any).routeOptions?.url || request.url.split('?')[0] || 'unknown';
  const method = request.method;

  if (statusCode >= 500) {
    // Server errors: Log with full details for debugging
    logger.error('Server error', {
      correlationId,
      error: error.message,
      stack: error.stack,
      path: request.url,
      method,
      route,
      statusCode,
      userId: (request as any).apiKey || (request as any).user?.sub || 'anonymous',
    });
  } else {
    // Client errors: Log with less detail
    logger.warn('Client error', {
      correlationId,
      error: error.message,
      path: request.url,
      method,
      route,
      statusCode,
    });
  }

  // Send error response
  reply.status(statusCode).send(errorResponse);
}

/**
 * Handle schema validation errors specifically
 */
export function handleValidationError(
  error: FastifyError,
  request: FastifyRequest,
  reply: FastifyReply
): void {
  const correlationId = getCorrelationId(request);
  
  logger.warn('Validation error', {
    correlationId,
    error: error.message,
    path: request.url,
    method: request.method,
  });

  // Ensure correlationId is always included (even if undefined, we'll use empty string)
  const errorResponse: ErrorResponse = {
    error: 'Validation Error',
    message: error.message || 'Invalid request data',
    timestamp: new Date().toISOString(),
    path: request.url,
    statusCode: 400,
    ...(correlationId && { correlationId }),
  };

  reply.status(400).send(errorResponse);
}

/**
 * Handle 404 errors
 */
export function handleNotFound(
  request: FastifyRequest,
  reply: FastifyReply
): void {
  const correlationId = getCorrelationId(request);
  
  logger.debug('Route not found', {
    correlationId,
    path: request.url,
    method: request.method,
  });

  reply.status(404).send({
    error: 'Not Found',
    message: `Route ${request.method} ${request.url} not found`,
    correlationId,
    timestamp: new Date().toISOString(),
    path: request.url,
    statusCode: 404,
  });
}

