import { log } from './logger';

export enum ErrorType {
  NETWORK = 'NETWORK',
  AUTH = 'AUTH',
  VALIDATION = 'VALIDATION',
  PERMISSION = 'PERMISSION',
  GUACAMOLE = 'GUACAMOLE',
  ENCRYPTION = 'ENCRYPTION',
  UNKNOWN = 'UNKNOWN'
}

export interface AppError {
  type: ErrorType;
  message: string;
  code?: string;
  details?: unknown;
  timestamp: Date;
  userMessage: string;
  retryable: boolean;
}

interface ErrorData {
  detail?: string;
  message?: string;
  required_role?: string;
  [key: string]: unknown;
}

export class ErrorHandler {
  /**
   * Create standardized application error
   */
  static createError(
    type: ErrorType,
    message: string,
    userMessage?: string,
    details?: unknown,
    retryable: boolean = false
  ): AppError {
    const error: AppError = {
      type,
      message,
      userMessage: userMessage || this.getDefaultUserMessage(type),
      details,
      timestamp: new Date(),
      retryable
    };

    // Log error
    log.error('error-handler', `${type} error occurred`, {
      type,
      message,
      userMessage: error.userMessage,
      retryable,
      details
    });

    return error;
  }

  /**
   * Handle API request errors
   */
  static async handleApiError(error: unknown): Promise<AppError> {
    // Response object with HTTP error
    if (error instanceof Response) {
      const errorData = await this.parseErrorResponse(error);
      return this.handleHttpError(error, errorData);
    }

    // Network or other errors
    return this.handleNonHttpError(error);
  }

  /**
   * Parse error response from API
   */
  private static async parseErrorResponse(response: Response): Promise<ErrorData> {
    try {
      const contentType = response.headers.get('content-type');
      if (contentType?.includes('application/json')) {
        return await response.json() as ErrorData;
      }
      const text = await response.text();
      return { message: text };
    } catch (parseError) {
      log.warn('error-handler', 'Failed to parse error response', { parseError });
      return {};
    }
  }

  /**
   * Handle HTTP errors
   */
  private static handleHttpError(response: Response, errorData: ErrorData): AppError {

      switch (response.status) {
        case 401:
          return this.handle401Error(response, errorData);
        
        case 403:
          return this.handle403Error(response, errorData);
        
        case 404:
          return this.createError(
            ErrorType.NETWORK,
            'Resource not found',
            'The requested resource was not found.',
            { status: response.status, details: errorData }
          );
        
        case 422:
          return this.createError(
            ErrorType.VALIDATION,
            'Validation error',
            errorData.detail || 'Please check your input and try again.',
            { status: response.status, details: errorData }
          );
        
        case 429:
          return this.createError(
            ErrorType.NETWORK,
            'Too many requests',
            'Too many requests. Please wait a moment and try again.',
            { status: response.status, details: errorData },
            true
          );
        
        case 500:
        case 502:
        case 503:
        case 504:
          return this.createError(
            ErrorType.NETWORK,
            'Server error',
            'A server error occurred. Please try again later.',
            { status: response.status, details: errorData },
            true
          );
        
        default:
          return this.createError(
            ErrorType.NETWORK,
            `HTTP ${response.status}: ${response.statusText}`,
            'A network error occurred. Please try again.',
            { status: response.status, details: errorData },
            response.status >= 500
          );
      }
  }

  /**
   * Handle 401 Unauthorized errors
   */
  private static handle401Error(response: Response, errorData: ErrorData): AppError {
    const isLoginError = 
      errorData.detail?.includes('Invalid username or password') || 
      errorData.detail?.includes('Login blocked');
    
    const userMessage = isLoginError 
      ? errorData.detail! 
      : 'Your session has expired. Please log in again.';
    
    return this.createError(
      ErrorType.AUTH,
      'Unauthorized access',
      userMessage,
      { status: response.status, details: errorData }
    );
  }

  /**
   * Handle 403 Forbidden errors
   */
  private static handle403Error(response: Response, errorData: ErrorData): AppError {
    let userMessage = 'You don\'t have permission to perform this action.';
    
    if (errorData.message?.includes('create_connections') || 
        errorData.required_role?.includes('privileges')) {
      userMessage = 'Your account has limited access (GUEST role). You can only view connections but cannot create new ones. Please contact your administrator to upgrade your account to USER role for full access.';
    } else if (errorData.message) {
      userMessage = `Access denied: ${errorData.message}`;
    }
    
    return this.createError(
      ErrorType.PERMISSION,
      'Forbidden access',
      userMessage,
      { status: response.status, details: errorData }
    );
  }

  /**
   * Handle non-HTTP errors (network, encryption, etc.)
   */
  private static handleNonHttpError(error: unknown): AppError {
    if (error instanceof Error) {
      if (error.name === 'TypeError' && error.message.includes('fetch')) {
        return this.createError(
          ErrorType.NETWORK,
          'Network connection failed',
          'Unable to connect to the server. Please check your internet connection.',
          { originalError: error.message },
          true // Retryable
        );
      }

      if (error.name === 'AbortError') {
        return this.createError(
          ErrorType.NETWORK,
          'Request timeout',
          'The request took too long. Please try again.',
          { originalError: error.message },
          true // Retryable
        );
      }

      // Encryption errors
      if (error.message.includes('encryption') || error.message.includes('crypto')) {
        return this.createError(
          ErrorType.ENCRYPTION,
          'Encryption error',
          'A security error occurred. Please try logging in again.',
          { originalError: error.message }
        );
      }

      // Guacamole errors
      if (error.message.includes('guacamole') || error.message.includes('connection')) {
        return this.createError(
          ErrorType.GUACAMOLE,
          'Connection error',
          'Failed to connect to the remote machine. Please check credentials and try again.',
          { originalError: error.message },
          true // Retryable
        );
      }
    }

    // Unknown error
    return this.createError(
      ErrorType.UNKNOWN,
      error instanceof Error ? error.message : String(error),
      'An unexpected error occurred. Please try again.',
      { originalError: error }
    );
  }

  /**
   * Check if operation should be retried
   */
  static shouldRetry(error: AppError, attempt: number, maxAttempts: number = 3): boolean {
    if (attempt >= maxAttempts) {
      return false;
    }

    return error.retryable;
  }

  /**
   * Get default user message
   */
  private static getDefaultUserMessage(type: ErrorType): string {
    switch (type) {
      case ErrorType.NETWORK:
        return 'A network error occurred. Please try again.';
      case ErrorType.AUTH:
        return 'Authentication failed. Please log in again.';
      case ErrorType.VALIDATION:
        return 'Please check your input and try again.';
      case ErrorType.PERMISSION:
        return 'You don\'t have permission to perform this action.';
      case ErrorType.GUACAMOLE:
        return 'Failed to connect to the remote machine. Please try again.';
      case ErrorType.ENCRYPTION:
        return 'A security error occurred. Please try again.';
      default:
        return 'An unexpected error occurred. Please try again.';
    }
  }

  /**
   * Format error for UI display
   */
  static formatErrorForUI(error: AppError): {
    title: string;
    message: string;
    type: 'error' | 'warning';
    action?: string;
  } {
    let title: string;
    let type: 'error' | 'warning' = 'error';
    let action: string | undefined;

    switch (error.type) {
      case ErrorType.AUTH:
        title = 'Authentication Error';
        action = 'Please log in again';
        break;
      case ErrorType.NETWORK:
        title = 'Connection Error';
        type = error.retryable ? 'warning' : 'error';
        action = error.retryable ? 'Retrying...' : undefined;
        break;
      case ErrorType.VALIDATION:
        title = 'Input Error';
        type = 'warning';
        break;
      case ErrorType.PERMISSION:
        title = 'Access Denied';
        break;
      case ErrorType.GUACAMOLE:
        title = 'Connection Failed';
        action = 'Check credentials and try again';
        break;
      case ErrorType.ENCRYPTION:
        title = 'Security Error';
        action = 'Please restart the application';
        break;
      default:
        title = 'Error';
    }

    return {
      title,
      message: error.userMessage,
      type,
      action
    };
  }
} 