/**
 * @fileoverview Converts various error types to MCP-compliant errors
 */

import { MCPError, MCPErrorCode, MCPErrorCategory, MCPErrorInfo } from './mcp-errors.js';
import { createServerError } from './mcp-error-factory.js';
import { handleApiError } from '../errors/handlers.js';
import { ClassifiedError } from '../errors/types.js';

/**
 * Converts a standard Error to MCPError
 */
function fromStandardError(error: Error, context?: string): MCPError {
  // Check for specific error types
  const message = error.message.toLowerCase();

  if (message.includes('authentication') || message.includes('unauthorized')) {
    return new MCPError({
      code: MCPErrorCode.AUTHENTICATION_ERROR,
      category: MCPErrorCategory.AUTHENTICATION_ERROR,
      message: error.message,
      cause: error,
      details: { context },
      retryable: false,
      userFriendly: true,
    });
  }

  if (message.includes('not found')) {
    return new MCPError({
      code: MCPErrorCode.RESOURCE_NOT_FOUND,
      category: MCPErrorCategory.RESOURCE_ERROR,
      message: error.message,
      cause: error,
      details: { context },
      retryable: false,
      userFriendly: true,
    });
  }

  if (message.includes('validation') || message.includes('invalid')) {
    return new MCPError({
      code: MCPErrorCode.VALIDATION_ERROR,
      category: MCPErrorCategory.VALIDATION_ERROR,
      message: error.message,
      cause: error,
      details: { context },
      retryable: false,
      userFriendly: true,
    });
  }

  if (message.includes('timeout')) {
    return new MCPError({
      code: MCPErrorCode.TIMEOUT_ERROR,
      category: MCPErrorCategory.NETWORK_ERROR,
      message: `Operation timed out: ${context || 'API call'}`,
      cause: error,
      details: { context, originalMessage: error.message },
      retryable: true,
      userFriendly: true,
    });
  }

  if (message.includes('rate limit')) {
    return new MCPError({
      code: MCPErrorCode.RATE_LIMITED,
      category: MCPErrorCategory.CLIENT_ERROR,
      message: error.message,
      cause: error,
      details: { context },
      retryable: true,
      userFriendly: true,
    });
  }

  // Check for network errors based on message content
  if (
    message.includes('network') ||
    message.includes('econnrefused') ||
    message.includes('fetch failed')
  ) {
    return new MCPError({
      code: MCPErrorCode.SERVER_ERROR,
      category: MCPErrorCategory.NETWORK_ERROR,
      message: `Network error: ${error.message}`,
      cause: error,
      details: { context },
      retryable: true,
      userFriendly: true,
    });
  }

  // Default to server error
  return createServerError(error.message, error, { context });
}

/**
 * Converts a DeepSource API error to MCPError
 */
function fromDeepSourceError(error: ClassifiedError): MCPError {
  const categoryMap: Record<string, MCPErrorCategory> = {
    AUTH: MCPErrorCategory.AUTHENTICATION_ERROR,
    NETWORK: MCPErrorCategory.NETWORK_ERROR,
    TIMEOUT: MCPErrorCategory.NETWORK_ERROR,
    RATE_LIMIT: MCPErrorCategory.CLIENT_ERROR,
    NOT_FOUND: MCPErrorCategory.RESOURCE_ERROR,
    CLIENT: MCPErrorCategory.CLIENT_ERROR,
    SERVER: MCPErrorCategory.SERVER_ERROR,
    SCHEMA: MCPErrorCategory.SERVER_ERROR,
    FORMAT: MCPErrorCategory.SERVER_ERROR,
    OTHER: MCPErrorCategory.SERVER_ERROR,
  };

  const codeMap: Record<string, MCPErrorCode> = {
    AUTH: MCPErrorCode.AUTHENTICATION_ERROR,
    NETWORK: MCPErrorCode.NETWORK_ERROR,
    TIMEOUT: MCPErrorCode.TIMEOUT_ERROR,
    RATE_LIMIT: MCPErrorCode.RATE_LIMITED,
    NOT_FOUND: MCPErrorCode.RESOURCE_NOT_FOUND,
    CLIENT: MCPErrorCode.CLIENT_ERROR,
    SERVER: MCPErrorCode.INTERNAL_ERROR,
    SCHEMA: MCPErrorCode.INTERNAL_ERROR,
    FORMAT: MCPErrorCode.INTERNAL_ERROR,
    OTHER: MCPErrorCode.INTERNAL_ERROR,
  };

  const mcpErrorInfo: MCPErrorInfo = {
    code: codeMap[error.category] || MCPErrorCode.INTERNAL_ERROR,
    category: categoryMap[error.category] || MCPErrorCategory.SERVER_ERROR,
    message: error.message,
    retryable: ['NETWORK', 'TIMEOUT', 'RATE_LIMIT', 'SERVER'].includes(error.category),
    userFriendly: ['AUTH', 'NOT_FOUND', 'RATE_LIMIT', 'TIMEOUT'].includes(error.category),
  };

  if (error.originalError instanceof Error) {
    mcpErrorInfo.cause = error.originalError;
  }

  if (error.metadata) {
    mcpErrorInfo.details = error.metadata;
  }

  return new MCPError(mcpErrorInfo);
}

/**
 * Converts any error to an MCPError
 */
export function toMCPError(error: unknown, context?: string): MCPError {
  // Already an MCPError
  if (error instanceof MCPError) {
    return error;
  }

  // Standard Error
  if (error instanceof Error) {
    return fromStandardError(error, context);
  }

  // String error
  if (typeof error === 'string') {
    return createServerError(error);
  }

  // Unknown error type
  return createServerError('Unknown error occurred', undefined, {
    errorType: typeof error,
    originalError: String(error),
    context,
  });
}

/**
 * Converts DeepSource API errors to MCP errors
 */
export function fromApiError(error: unknown, context?: string): MCPError {
  // Use the API error handler to classify the error
  const classifiedError = handleApiError(error);
  const mcpError = fromDeepSourceError(classifiedError);

  // Add context if provided
  if (context && mcpError.details) {
    mcpError.details.context = context;
  }

  return mcpError;
}

// For backward compatibility, export a namespace-like object
export const MCPErrorConverter = {
  toMCPError,
  fromApiError,
  fromStandardError,
  fromDeepSourceError,
};
