import { ERROR_CODES, ERROR_MESSAGES } from './constants/errors';

/**
 * Result of Gemini error analysis
 */
export interface GeminiErrorResult {
  code: string;
  message: string;
  finishReason?: string | undefined;
  blockReason?: string | undefined;
  safetyCategories?: string[] | undefined;
  retryAfter?: number | undefined;
  httpStatus?: number | undefined;
  technicalDetails?: string | undefined;
}

/**
 * Safety rating from Gemini response
 */
interface SafetyRating {
  category?: string;
  probability?: string;
}

/**
 * Gemini response structure (partial)
 */
interface GeminiResponse {
  candidates?: Array<{
    finishReason?: string;
    finishMessage?: string;
    content?: {
      parts?: Array<{
        text?: string;
        inlineData?: { data?: string; mimeType?: string };
      }>;
    };
    safetyRatings?: SafetyRating[];
  }>;
  promptFeedback?: {
    blockReason?: string;
    blockReasonMessage?: string;
    safetyRatings?: SafetyRating[];
  };
  usageMetadata?: {
    promptTokenCount?: number;
    candidatesTokenCount?: number;
    totalTokenCount?: number;
  };
}

/**
 * Detector for Gemini AI specific errors
 * Provides detailed error classification for rate limits, safety blocks, timeouts, etc.
 */
export class GeminiErrorDetector {
  /**
   * Classify an API-level error (HTTP errors from Gemini)
   */
  static classifyApiError(error: unknown): GeminiErrorResult {
    const err = error as { status?: number; message?: string; details?: unknown };

    // Check for rate limit (HTTP 429)
    if (err.status === 429) {
      const retryAfter = this.extractRetryAfter(error);
      return {
        code: ERROR_CODES.GEMINI_RATE_LIMIT,
        message: retryAfter
          ? `${ERROR_MESSAGES.GEMINI_RATE_LIMIT}. Retry after ${retryAfter} seconds.`
          : `${ERROR_MESSAGES.GEMINI_RATE_LIMIT}. Please wait before retrying.`,
        httpStatus: 429,
        retryAfter,
        technicalDetails: err.message,
      };
    }

    // Check for authentication errors
    if (err.status === 401 || err.status === 403) {
      return {
        code: ERROR_CODES.GEMINI_API_ERROR,
        message: 'Gemini API authentication failed. Check API key.',
        httpStatus: err.status,
        technicalDetails: err.message,
      };
    }

    // Check for server errors
    if (err.status === 500 || err.status === 503) {
      return {
        code: ERROR_CODES.GEMINI_API_ERROR,
        message: 'Gemini API service temporarily unavailable.',
        httpStatus: err.status,
        technicalDetails: err.message,
      };
    }

    // Check for bad request
    if (err.status === 400) {
      return {
        code: ERROR_CODES.GEMINI_API_ERROR,
        message: `Gemini API invalid request: ${err.message || 'Unknown error'}`,
        httpStatus: 400,
        technicalDetails: err.message,
      };
    }

    // Generic API error
    return {
      code: ERROR_CODES.GEMINI_API_ERROR,
      message: err.message || ERROR_MESSAGES.GEMINI_API_ERROR,
      httpStatus: err.status,
      technicalDetails: err.message,
    };
  }

  /**
   * Analyze a Gemini response for errors (finishReason, blockReason)
   * Returns null if no error detected
   */
  static analyzeResponse(response: GeminiResponse): GeminiErrorResult | null {
    // Check promptFeedback for blocked prompts
    if (response.promptFeedback?.blockReason) {
      const safetyCategories = this.extractSafetyCategories(
        response.promptFeedback.safetyRatings
      );
      return {
        code: ERROR_CODES.GEMINI_CONTENT_BLOCKED,
        message:
          response.promptFeedback.blockReasonMessage ||
          `Prompt blocked: ${response.promptFeedback.blockReason}`,
        blockReason: response.promptFeedback.blockReason,
        safetyCategories,
        technicalDetails: `blockReason: ${response.promptFeedback.blockReason}`,
      };
    }

    // Check candidate finishReason
    const candidate = response.candidates?.[0];
    if (!candidate) {
      return {
        code: ERROR_CODES.GEMINI_NO_IMAGE,
        message: 'No response candidates from Gemini AI.',
        technicalDetails: 'response.candidates is empty or undefined',
      };
    }

    const finishReason = candidate.finishReason;

    // STOP is normal completion
    if (!finishReason || finishReason === 'STOP') {
      return null;
    }

    // Handle different finishReasons
    switch (finishReason) {
      case 'SAFETY':
      case 'IMAGE_SAFETY': {
        const safetyCategories = this.extractSafetyCategories(candidate.safetyRatings);
        return {
          code: ERROR_CODES.GEMINI_SAFETY_BLOCK,
          message: `Content blocked due to safety: ${safetyCategories.join(', ') || 'unspecified'}`,
          finishReason,
          safetyCategories,
          technicalDetails: `finishReason: ${finishReason}, safetyRatings: ${JSON.stringify(candidate.safetyRatings)}`,
        };
      }

      case 'NO_IMAGE':
        return {
          code: ERROR_CODES.GEMINI_NO_IMAGE,
          message: 'Gemini AI could not generate an image for this prompt. Try rephrasing.',
          finishReason,
          technicalDetails: `finishReason: ${finishReason}`,
        };

      case 'IMAGE_PROHIBITED_CONTENT':
        return {
          code: ERROR_CODES.GEMINI_CONTENT_BLOCKED,
          message: 'Image generation blocked due to prohibited content in prompt.',
          finishReason,
          technicalDetails: `finishReason: ${finishReason}`,
        };

      case 'MAX_TOKENS':
        return {
          code: ERROR_CODES.GEMINI_API_ERROR,
          message: 'Response exceeded maximum token limit. Try a shorter prompt.',
          finishReason,
          technicalDetails: `finishReason: ${finishReason}`,
        };

      case 'RECITATION':
      case 'IMAGE_RECITATION':
        return {
          code: ERROR_CODES.GEMINI_CONTENT_BLOCKED,
          message: 'Response blocked due to potential copyright concerns.',
          finishReason,
          technicalDetails: `finishReason: ${finishReason}`,
        };

      default:
        return {
          code: ERROR_CODES.GEMINI_API_ERROR,
          message: `Generation stopped unexpectedly: ${finishReason}`,
          finishReason,
          technicalDetails: `finishReason: ${finishReason}, finishMessage: ${candidate.finishMessage}`,
        };
    }
  }

  /**
   * Check if response has image data
   */
  static hasImageData(response: GeminiResponse): boolean {
    const parts = response.candidates?.[0]?.content?.parts;
    if (!parts) return false;
    return parts.some((part) => part.inlineData?.data);
  }

  /**
   * Format error result for logging
   */
  static formatForLogging(result: GeminiErrorResult): string {
    const parts = [`[${result.code}] ${result.message}`];

    if (result.finishReason) {
      parts.push(`finishReason=${result.finishReason}`);
    }
    if (result.blockReason) {
      parts.push(`blockReason=${result.blockReason}`);
    }
    if (result.httpStatus) {
      parts.push(`httpStatus=${result.httpStatus}`);
    }
    if (result.retryAfter) {
      parts.push(`retryAfter=${result.retryAfter}s`);
    }
    if (result.safetyCategories?.length) {
      parts.push(`safety=[${result.safetyCategories.join(', ')}]`);
    }

    return parts.join(' | ');
  }

  /**
   * Log Gemini response structure for debugging
   */
  static logResponseStructure(response: GeminiResponse, prefix: string = ''): void {
    const parts = response.candidates?.[0]?.content?.parts || [];
    const partTypes = parts.map((p) => {
      if (p.inlineData) return 'image';
      if (p.text) return 'text';
      return 'other';
    });

    console.log(`[ImageGenService]${prefix ? ` [${prefix}]` : ''} Gemini response:`, {
      hasCandidates: !!response.candidates?.length,
      candidateCount: response.candidates?.length || 0,
      finishReason: response.candidates?.[0]?.finishReason || null,
      blockReason: response.promptFeedback?.blockReason || null,
      partsCount: parts.length,
      partTypes,
      usageMetadata: response.usageMetadata || null,
    });
  }

  /**
   * Extract retry-after value from error
   */
  private static extractRetryAfter(error: unknown): number | undefined {
    const err = error as { headers?: { get?: (key: string) => string | null } };

    // Try to get from headers
    if (err.headers?.get) {
      const retryAfter = err.headers.get('retry-after');
      if (retryAfter) {
        const seconds = parseInt(retryAfter, 10);
        if (!isNaN(seconds)) return seconds;
      }
    }

    // Default retry after for rate limits
    return 60;
  }

  /**
   * Extract safety category names from ratings
   */
  private static extractSafetyCategories(ratings?: SafetyRating[]): string[] {
    if (!ratings || ratings.length === 0) return [];

    // Filter for high/medium probability ratings and extract category names
    return ratings
      .filter((r) => r.probability === 'HIGH' || r.probability === 'MEDIUM')
      .map((r) => r.category?.replace('HARM_CATEGORY_', '') || 'UNKNOWN')
      .filter((c) => c !== 'UNKNOWN');
  }
}
