import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { GoogleGenAI } from '@google/genai';

export interface GeminiGenerateOptions {
  model?: string;
  systemPrompt?: string;
  temperature?: number;
  maxTokens?: number;
}

export interface GeminiChatMessage {
  role: 'user' | 'model';
  content: string;
}

/**
 * Gemini AI Service
 *
 * Provides AI-powered text generation using Google Gemini API.
 * This service is globally available when ENABLE_GEMINI=true.
 *
 * @example
 * ```typescript
 * // Simple text generation
 * const response = await geminiService.generateText('Write a poem about coding');
 *
 * // With options
 * const response = await geminiService.generateText('Translate to Turkish', {
 *   temperature: 0.7,
 *   systemPrompt: 'You are a professional translator',
 * });
 *
 * // Chat conversation
 * const messages = [
 *   { role: 'user', content: 'Hello!' },
 *   { role: 'model', content: 'Hi there!' },
 *   { role: 'user', content: 'What is 2+2?' },
 * ];
 * const response = await geminiService.chat(messages);
 * ```
 */
@Injectable()
export class GeminiService implements OnModuleInit {
  private readonly logger = new Logger(GeminiService.name);
  private client: GoogleGenAI | null = null;
  private isEnabled = false;
  private defaultModel: string;

  constructor(private readonly configService: ConfigService) {
    this.isEnabled = this.configService.get<boolean>('gemini.enabled', false);
    this.defaultModel = this.configService.get<string>(
      'gemini.defaultModel',
      'gemini-2.5-flash',
    );
  }

  onModuleInit() {
    if (!this.isEnabled) {
      this.logger.log(
        'Gemini AI is disabled. Set ENABLE_GEMINI=true to enable.',
      );
      return;
    }

    const apiKey = this.configService.get<string>('gemini.apiKey');
    if (!apiKey) {
      this.logger.warn(
        'GOOGLE_API_KEY is not set. Gemini features will not work.',
      );
      return;
    }

    try {
      this.client = new GoogleGenAI({ apiKey });
      this.logger.log('✅ Gemini AI initialized successfully');
    } catch (error) {
      this.logger.error('Failed to initialize Gemini AI', error);
    }
  }

  /**
   * Check if Gemini is available and properly configured
   */
  isAvailable(): boolean {
    return this.isEnabled && this.client !== null;
  }

  /**
   * Generate text content from a prompt
   *
   * @param prompt - The text prompt to send to the AI
   * @param options - Optional configuration for the generation
   * @returns Generated text response
   */
  async generateText(
    prompt: string,
    options: GeminiGenerateOptions = {},
  ): Promise<{ text: string; usage?: any }> {
    if (!this.isAvailable()) {
      throw new Error('Gemini AI is not available. Check your configuration.');
    }

    const model = options.model || this.defaultModel;

    try {
      const contents: any[] = [];

      // Add system prompt if provided
      if (options.systemPrompt) {
        contents.push({
          role: 'user',
          parts: [{ text: options.systemPrompt }],
        });
        contents.push({
          role: 'model',
          parts: [{ text: 'Understood. I will follow these instructions.' }],
        });
      }

      contents.push({
        role: 'user',
        parts: [{ text: prompt }],
      });

      const response = await this.client!.models.generateContent({
        model,
        contents,
        config: {
          temperature: options.temperature,
          maxOutputTokens: options.maxTokens,
        },
      });

      return {
        text: (response.text || '').trim(),
        usage: response.usageMetadata,
      };
    } catch (error) {
      this.logger.error('Gemini generation failed', error);
      throw error;
    }
  }

  /**
   * Have a multi-turn chat conversation
   *
   * @param messages - Array of chat messages
   * @param options - Optional configuration for the generation
   * @returns Generated text response
   */
  async chat(
    messages: GeminiChatMessage[],
    options: GeminiGenerateOptions = {},
  ): Promise<{ text: string; usage?: any }> {
    if (!this.isAvailable()) {
      throw new Error('Gemini AI is not available. Check your configuration.');
    }

    const model = options.model || this.defaultModel;

    try {
      const contents = messages.map((msg) => ({
        role: msg.role,
        parts: [{ text: msg.content }],
      }));

      // Prepend system prompt if provided
      if (options.systemPrompt) {
        contents.unshift(
          {
            role: 'user',
            parts: [{ text: options.systemPrompt }],
          },
          {
            role: 'model',
            parts: [{ text: 'Understood. I will follow these instructions.' }],
          },
        );
      }

      const response = await this.client!.models.generateContent({
        model,
        contents,
        config: {
          temperature: options.temperature,
          maxOutputTokens: options.maxTokens,
        },
      });

      return {
        text: (response.text || '').trim(),
        usage: response.usageMetadata,
      };
    } catch (error) {
      this.logger.error('Gemini chat failed', error);
      throw error;
    }
  }

  /**
   * Generate structured JSON output
   *
   * @param prompt - The prompt describing what JSON to generate
   * @param schema - JSON schema description for the expected output
   * @param options - Optional configuration for the generation
   * @returns Parsed JSON object
   */
  async generateJSON<T = any>(
    prompt: string,
    schema: string,
    options: GeminiGenerateOptions = {},
  ): Promise<{ data: T; usage?: any }> {
    const fullPrompt = `${prompt}

Output the result as valid JSON that matches this schema:
${schema}

IMPORTANT: Only output valid JSON, no markdown code blocks or other text.`;

    const response = await this.generateText(fullPrompt, options);

    try {
      // Try to extract JSON from the response
      let jsonStr = response.text;

      // Remove potential markdown code blocks
      const jsonMatch = jsonStr.match(/```(?:json)?\s*([\s\S]*?)```/);
      if (jsonMatch) {
        jsonStr = jsonMatch[1].trim();
      }

      const data = JSON.parse(jsonStr) as T;
      return { data, usage: response.usage };
    } catch (error) {
      this.logger.error('Failed to parse JSON response', error);
      throw new Error('Failed to parse AI response as JSON');
    }
  }

  // ── Görsel Üretim (Gemini Image Generation) ─────────────────────────

  /**
   * Gemini Image Generation API ile görsel üret.
   * 3 katmanlı fallback mimarisi:
   *   1) gemini-2.5-flash-image (Nano Banana — hızlı, stabil)
   *   2) gemini-3.1-flash-image-preview (Nano Banana 2 — en yeni)
   *   3) Imagen 4 Fast (generateImages API)
   *
   * Raspberry Pi 5 bellek koruması için buffer olarak döner.
   *
   * @param prompt - İngilizce görsel açıklaması
   * @param aspectRatio - Görsel en-boy oranı (16:9, 9:16, 1:1)
   * @returns Base64 decoded image buffer ve mime type
   */
  async generateImage(
    prompt: string,
    aspectRatio: '16:9' | '9:16' | '1:1' = '16:9',
  ): Promise<{ buffer: Buffer; mimeType: string } | null> {
    if (!this.isAvailable()) {
      throw new Error('Gemini AI is not available. Check your configuration.');
    }

    // Güncel model sıralaması (Nisan 2026):
    // - gemini-2.5-flash-image: Nano Banana — stabil, hızlı
    // - gemini-3.1-flash-image-preview: Nano Banana 2 — en yeni, yüksek kalite
    const primaryModel = 'gemini-2.5-flash-image';
    const fallbackModel = 'gemini-3.1-flash-image-preview';

    try {
      this.logger.log(`🎨 Görsel üretiliyor: "${prompt.substring(0, 100)}..." [${aspectRatio}]`);

      // En-boy oranına göre yönlendirmeyi zorla
      const orientation = aspectRatio === '9:16' ? '(VERTICAL / PORTRAIT)' : aspectRatio === '16:9' ? '(HORIZONTAL / LANDSCAPE)' : '(SQUARE)';
      const enhancedPrompt = `Generate a high-quality, photorealistic image. Description: ${prompt}. Aspect ratio and framing: EXACTLY ${aspectRatio} ${orientation}. Style: cinematic lighting, detailed, professional. IMPORTANT: Return the generated image only, no text.`;

      // ── Katman 1: gemini-2.5-flash-image (Nano Banana) — 2 deneme ──
      for (let attempt = 1; attempt <= 2; attempt++) {
        try {
          this.logger.log(`🔄 Katman 1 (deneme ${attempt}/2): ${primaryModel}`);
          const result = await this.tryGenerateContentImage(primaryModel, enhancedPrompt);
          if (result && result.buffer.length > 0) {
            this.logger.log(`✅ Görsel üretildi (${primaryModel}): ${(result.buffer.length / 1024).toFixed(1)} KB`);
            return { buffer: result.buffer, mimeType: result.mimeType };
          }
          
          const reason = result?.errorReason || 'null response';
          this.logger.warn(`⚠️ ${primaryModel} deneme ${attempt}: görsel döndürmedi (${reason})`);
          
          if (['IMAGE_OTHER', 'SAFETY', 'PROHIBITED_CONTENT'].includes(reason)) {
            this.logger.warn(`🚫 Güvenlik/Politika filtresi tetiklendi (${reason}). Denemeler iptal ediliyor.`);
            break; // Fail fast for safety blocks
          }
          
          if (attempt < 2) await this.sleep(2000);
        } catch (err1: any) {
          this.logger.warn(`⚠️ ${primaryModel} deneme ${attempt} hata: ${err1.message?.substring(0, 200)}`);
          if (attempt < 2) await this.sleep(2000);
        }
      }

      // ── Katman 2: gemini-3.1-flash-image-preview (Nano Banana 2) ──
      try {
        this.logger.log(`🔄 Katman 2: ${fallbackModel}`);
        const result = await this.tryGenerateContentImage(fallbackModel, enhancedPrompt);
        if (result && result.buffer.length > 0) {
          this.logger.log(`✅ Görsel üretildi (${fallbackModel}): ${(result.buffer.length / 1024).toFixed(1)} KB`);
          return { buffer: result.buffer, mimeType: result.mimeType };
        }
        this.logger.warn(`⚠️ ${fallbackModel}: görsel döndürmedi (${result?.errorReason || 'null response'})`);
        
        if (['IMAGE_OTHER', 'SAFETY', 'PROHIBITED_CONTENT'].includes(result?.errorReason || '')) {
           this.logger.warn(`🚫 Katman 2 Güvenlik/Politika filtresi tetiklendi. Katman 3'e geçiliyor.`);
        }
      } catch (err2: any) {
        this.logger.warn(`⚠️ ${fallbackModel} hata: ${err2.message?.substring(0, 200)}`);
      }

      // ── Katman 3: Imagen 4 Fast (generateImages API) ──
      try {
        this.logger.log(`🔄 Katman 3: Imagen 4 Fast`);
        const response = await this.client!.models.generateImages({
          model: 'imagen-4.0-fast-generate-001',
          prompt: enhancedPrompt,
          config: {
            numberOfImages: 1,
            aspectRatio: aspectRatio,
            outputMimeType: 'image/jpeg',
          },
        });

        if (response.generatedImages?.[0]?.image?.imageBytes) {
          const buffer = Buffer.from(response.generatedImages[0].image.imageBytes, 'base64');
          const mimeType = 'image/jpeg';
          this.logger.log(`✅ Görsel üretildi (Imagen 4): ${(buffer.length / 1024).toFixed(1)} KB`);
          return { buffer, mimeType };
        }
        this.logger.warn(`⚠️ Imagen 4: görsel döndürmedi. Üretilen görsel sayısı: ${response.generatedImages?.length || 0}`);
      } catch (err3: any) {
        this.logger.warn(`⚠️ Imagen 4 hata: ${err3.message?.substring(0, 200)}`);
      }

      this.logger.error('❌ Tüm görsel üretim katmanları başarısız oldu');
      return null;
    } catch (error) {
      this.logger.error(`Gemini görsel üretim hatası: ${error instanceof Error ? error.message : error}`);
      throw error;
    }
  }

  /**
   * generateContent API ile görsel üretim denemesi.
   * responseModalities: ['IMAGE', 'TEXT'] kullanarak inlineData içinden resim çıkarır.
   */
  private async tryGenerateContentImage(
    model: string,
    prompt: string,
  ): Promise<{ buffer: Buffer; mimeType: string; errorReason?: string } | null> {
    const response = await this.client!.models.generateContent({
      model,
      contents: prompt,
      config: {
        responseModalities: ['IMAGE', 'TEXT'],
      },
    });

    const candidate = response.candidates?.[0];

    // Safety filter veya boş yanıt kontrolü
    if (!candidate?.content?.parts || candidate.content.parts.length === 0) {
      const finishReason = candidate?.finishReason || 'UNKNOWN';
      this.logger.warn(`⚠️ ${model}: boş yanıt (finishReason: ${finishReason})`);
      return { buffer: Buffer.from([]), mimeType: '', errorReason: finishReason };
    }

    const imagePart = candidate.content.parts.find(
      (p: any) => p.inlineData?.mimeType?.startsWith('image/'),
    );

    if (imagePart?.inlineData?.data) {
      const buffer = Buffer.from(imagePart.inlineData.data, 'base64');
      const mimeType = imagePart.inlineData.mimeType || 'image/png';
      return { buffer, mimeType };
    }

    // Text-only response geldi (görsel yok)
    const textParts = candidate.content.parts.filter((p: any) => p.text);
    if (textParts.length > 0) {
      this.logger.warn(`⚠️ ${model}: sadece text döndü, görsel yok. Text: "${textParts[0].text?.substring(0, 100)}"`);
      return { buffer: Buffer.from([]), mimeType: '', errorReason: 'TEXT_ONLY' };
    }

    return { buffer: Buffer.from([]), mimeType: '', errorReason: 'NO_IMAGE_DATA' };
  }

  /** Basit uyku fonksiyonu — retry aralarında kullanılır */
  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * Sahne bazlı görsel üret — visualPrompt ve video stili kullanarak.
   *
   * @param visualPrompt - Sahnenin İngilizce görsel açıklaması
   * @param style - Video stili (cinematic, documentary, educational vb.)
   * @param aspectRatio - En-boy oranı
   * @returns Buffer ve mimeType
   */
  async generateImageForScene(
    visualPrompt: string,
    style: string = 'cinematic',
    aspectRatio: '16:9' | '9:16' | '1:1' = '16:9',
  ): Promise<{ buffer: Buffer; mimeType: string } | null> {
    const enhancedPrompt = `${visualPrompt}. Style: ${style}, professional production quality, volumetric lighting, sharp details, 8K resolution.`;
    return this.generateImage(enhancedPrompt, aspectRatio);
  }

  /**
   * Video için thumbnail görsel üret — proje başlığı ve açıklamasından.
   *
   * @param title - Video başlığı
   * @param description - Video açıklaması
   * @returns Buffer ve mimeType
   */
  async generateThumbnail(
    title: string,
    description: string,
  ): Promise<{ buffer: Buffer; mimeType: string } | null> {
    const prompt = `Create a compelling YouTube video thumbnail for a video titled "${title}". ${description}. Make it eye-catching with bold, dynamic composition. No text overlay needed.`;
    return this.generateImage(prompt, '16:9');
  }
}

