import { logger } from '@goodgo/logger';
import express from 'express';

import { ErrorCode, getStatusFromErrorCode, isOperationalError } from '../errors/error-codes';
import { HttpError } from '../errors/http-error';

/**
 * EN: Global error handler middleware with enhanced error handling
 * VI: Middleware xử lý lỗi toàn cục với enhanced error handling
 */
export const errorHandler = (
  err: any,
  req: express.Request,
  res: express.Response,
  _next: express.NextFunction
): void => {
  let statusCode = 500;
  let errorCode = ErrorCode.INTERNAL_ERROR;
  let message = 'Internal server error / Lỗi máy chủ nội bộ';
  let details: any = undefined;
  let isOperational = false;

  // EN: Handle HttpError instances (our custom errors)
  // VI: Xử lý HttpError instances (custom errors của chúng ta)
  if (err instanceof HttpError) {
    statusCode = err.statusCode;
    errorCode = err.errorCode as ErrorCode;
    message = err.message;
    details = err.details;
    isOperational = err.isOperational;
  }
  // EN: Handle Prisma errors
  // VI: Xử lý Prisma errors
  else if (err.code && typeof err.code === 'string') {
    if (err.code === 'P2002') {
      // Unique constraint violation
      statusCode = 409;
      errorCode = ErrorCode.CONSTRAINT_VIOLATION;
      message = 'Resource already exists / Tài nguyên đã tồn tại';
      isOperational = true;
    } else if (err.code.startsWith('P1')) {
      // Database connection/query errors
      statusCode = 500;
      errorCode = ErrorCode.DATABASE_ERROR;
      message = 'Database operation failed / Thao tác database thất bại';
      isOperational = false;
    } else if (err.code.startsWith('P2')) {
      // Data validation errors
      statusCode = 422;
      errorCode = ErrorCode.VALIDATION_ERROR;
      message = 'Data validation failed / Validation dữ liệu thất bại';
      isOperational = true;
    }
  }
  // EN: Handle JWT errors
  // VI: Xử lý JWT errors
  else if (err.name === 'JsonWebTokenError') {
    statusCode = 401;
    errorCode = ErrorCode.INVALID_TOKEN;
    message = 'Invalid authentication token / Token xác thực không hợp lệ';
    isOperational = true;
  } else if (err.name === 'TokenExpiredError') {
    statusCode = 401;
    errorCode = ErrorCode.TOKEN_EXPIRED;
    message = 'Authentication token expired / Token xác thực đã hết hạn';
    isOperational = true;
  }
  // EN: Handle Zod validation errors
  // VI: Xử lý Zod validation errors
  else if (err.name === 'ZodError') {
    statusCode = 422;
    errorCode = ErrorCode.VALIDATION_ERROR;
    message = 'Validation failed / Validation thất bại';
    details = err.errors.map((e: any) => ({
      field: e.path.join('.'),
      message: e.message,
      code: e.code,
    }));
    isOperational = true;
  }
  // EN: Handle Express/Multer file upload errors
  // VI: Xử lý Express/Multer file upload errors
  else if (err.name === 'MulterError') {
    statusCode = 400;
    errorCode = ErrorCode.INVALID_FORMAT;
    message = 'File upload error / Lỗi upload file';
    isOperational = true;
  }
  // EN: Handle rate limiting errors
  // VI: Xử lý rate limiting errors
  else if (err.message && err.message.includes('Too many requests')) {
    statusCode = 429;
    errorCode = ErrorCode.RATE_LIMIT_EXCEEDED;
    message = 'Rate limit exceeded / Vượt quá giới hạn tốc độ';
    isOperational = true;
  }
  // EN: Handle generic errors
  // VI: Xử lý generic errors
  else {
    // EN: Try to map error message patterns
    // VI: Thử map error message patterns
    const errorMessage = err.message?.toLowerCase() || '';

    if (errorMessage.includes('not found')) {
      statusCode = 404;
      errorCode = ErrorCode.NOT_FOUND;
      message = err.message;
      isOperational = true;
    } else if (errorMessage.includes('unauthorized') || errorMessage.includes('not authenticated')) {
      statusCode = 401;
      errorCode = ErrorCode.UNAUTHORIZED;
      message = err.message;
      isOperational = true;
    } else if (errorMessage.includes('forbidden') || errorMessage.includes('not allowed')) {
      statusCode = 403;
      errorCode = ErrorCode.FORBIDDEN;
      message = err.message;
      isOperational = true;
    } else if (errorMessage.includes('validation') || errorMessage.includes('invalid')) {
      statusCode = 422;
      errorCode = ErrorCode.VALIDATION_ERROR;
      message = err.message;
      isOperational = true;
    }
  }

  // EN: Prepare error details for logging
  // VI: Chuẩn bị chi tiết lỗi để logging
  const errorDetails = {
    message: err.message,
    name: err.name,
    code: err.code,
    statusCode,
    errorCode,
    isOperational,
    stack: err.stack,
    url: req.url,
    method: req.method,
    userAgent: req.get('User-Agent'),
    ip: req.ip,
    userId: (req as any).user?.userId,
    details,
  };

  // EN: Log error with appropriate level
  // VI: Log lỗi với level phù hợp
  if (!isOperational || statusCode >= 500) {
    logger.error('Unhandled error occurred / Lỗi không mong muốn xảy ra', errorDetails);
  } else {
    logger.warn('Operational error occurred / Lỗi operational xảy ra', errorDetails);
  }

  // EN: Prepare response based on environment
  // VI: Chuẩn bị response dựa trên environment
  const isProduction = process.env.NODE_ENV === 'production';

  const response = {
    success: false,
    error: {
      code: errorCode,
      message: isProduction && statusCode >= 500 ? 'Internal server error / Lỗi máy chủ nội bộ' : message,
      ...(details && !isProduction && { details }),
    },
    timestamp: new Date().toISOString(),
  };

  res.status(statusCode).json(response);
};

/**
 * EN: 404 Not Found handler with enhanced error details
 * VI: Handler 404 Not Found với enhanced error details
 */
export const notFoundHandler = (
  req: express.Request,
  _res: express.Response,
  next: express.NextFunction
): void => {
  const error = new HttpError(
    `Route ${req.originalUrl} not found / Route ${req.originalUrl} không tìm thấy`,
    404,
    ErrorCode.NOT_FOUND
  );
  next(error);
};

/**
 * EN: Async error wrapper to catch promise rejections
 * VI: Async error wrapper để catch promise rejections
 */
export const asyncHandler = (fn: (req: express.Request, res: express.Response, next: express.NextFunction) => Promise<any> | any) => {
  return (req: express.Request, res: express.Response, next: express.NextFunction) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
};

/**
 * EN: Create HttpError from error code
 * VI: Tạo HttpError từ error code
 */
export const createHttpError = (errorCode: ErrorCode, message?: string, details?: any): HttpError => {
  const statusCode = getStatusFromErrorCode(errorCode);
  const isOperational = isOperationalError(errorCode);
  return new HttpError(message || `${errorCode}`, statusCode, errorCode, isOperational, details);
};
