import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';
import {
  ErrorTrackingService,
  ErrorCategory,
  ErrorSeverity,
} from '../services/error-tracking.service';
import { ErrorResponseService } from '../services/error-response.service';
import { ErrorCode } from '../constants/error-codes';

/**
 * Global Exception Filter
 *
 * Catches all exceptions and:
 * 1. Logs them appropriately
 * 2. Sends them to Sentry
 * 3. Returns user-friendly localized error responses with error codes
 */
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger('GlobalExceptionFilter');

  constructor(
    private readonly errorTracking: ErrorTrackingService,
    private readonly errorResponse: ErrorResponseService,
  ) {}

  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const message =
      exception instanceof HttpException
        ? exception.message
        : 'Internal server error';

    // Build error context
    const context = {
      userId: (request as any).user?.id,
      requestId: (request as any).id,
      endpoint: request.url,
      method: request.method,
      userAgent: request.headers['user-agent'],
      ipAddress: request.ip,
    };

    // Determine error category, severity, and error code
    const { category, severity, errorCode } = this.categorizeError(
      exception,
      status,
    );

    // Log error
    if (status >= 500) {
      this.logger.error(
        `[${category}] [${errorCode}] ${message}`,
        exception.stack,
        JSON.stringify(context),
      );
    } else if (status >= 400) {
      this.logger.warn(
        `[${category}] [${errorCode}] ${message}`,
        JSON.stringify(context),
      );
    }

    // Send to Sentry (only for errors, not client errors)
    if (status >= 500) {
      this.errorTracking.captureError(exception, {
        category,
        severity,
        context,
        tags: {
          http_status: status.toString(),
          error_type: exception.constructor.name,
          error_code: errorCode,
        },
        fingerprint: [category, request.url],
      });
    }

    // Extract user locale from Accept-Language header
    const locale = this.errorResponse.extractLocale(
      request.headers['accept-language'],
    );

    // Get error code from exception if available, otherwise use determined code
    const finalErrorCode = (exception as any).errorCode || errorCode;

    // Build localized error response
    const errorResponseDto = this.errorResponse.createErrorResponse(
      finalErrorCode,
      status,
      locale,
      request.url,
      status < 500 ? exception.response?.details : undefined,
    );

    response.status(status).json(errorResponseDto);
  }

  /**
   * Categorize error for tracking and assign error code
   */
  private categorizeError(
    exception: any,
    status: number,
  ): {
    category: ErrorCategory;
    severity: ErrorSeverity;
    errorCode: ErrorCode;
  } {
    // Database errors
    if (exception.name === 'QueryFailedError') {
      const errorCode = exception.message.includes('timeout')
        ? ErrorCode.DB_QUERY_TIMEOUT
        : ErrorCode.DB_CONNECTION_ERROR;
      return {
        category: ErrorCategory.DATABASE_QUERY_TIMEOUT,
        severity: ErrorSeverity.ERROR,
        errorCode,
      };
    }

    // Auth errors
    if (status === 401) {
      return {
        category: ErrorCategory.AUTH_FAILED,
        severity: ErrorSeverity.WARNING,
        errorCode: ErrorCode.AUTH_INVALID_TOKEN,
      };
    }

    // Forbidden
    if (status === 403) {
      return {
        category: ErrorCategory.AUTH_FAILED,
        severity: ErrorSeverity.WARNING,
        errorCode: ErrorCode.AUTH_INSUFFICIENT_PERMISSIONS,
      };
    }

    // Not found
    if (status === 404) {
      return {
        category: ErrorCategory.API_VALIDATION_ERROR,
        severity: ErrorSeverity.INFO,
        errorCode: ErrorCode.GENERAL_NOT_FOUND,
      };
    }

    // Validation errors
    if (status === 400) {
      return {
        category: ErrorCategory.API_VALIDATION_ERROR,
        severity: ErrorSeverity.INFO,
        errorCode: ErrorCode.VALIDATION_INVALID_INPUT,
      };
    }

    // Rate limiting
    if (status === 429) {
      return {
        category: ErrorCategory.API_RATE_LIMIT,
        severity: ErrorSeverity.WARNING,
        errorCode: ErrorCode.RATE_LIMIT_EXCEEDED,
      };
    }

    // Server errors
    if (status >= 500) {
      return {
        category: ErrorCategory.SERVICE_UNAVAILABLE,
        severity: ErrorSeverity.ERROR,
        errorCode: ErrorCode.GENERAL_INTERNAL_ERROR,
      };
    }

    return {
      category: ErrorCategory.API_VALIDATION_ERROR,
      severity: ErrorSeverity.INFO,
      errorCode: ErrorCode.GENERAL_INTERNAL_ERROR,
    };
  }
}
