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");
    }
  }
}
