import { GoogleGenerativeAI } from '@google/generative-ai';
import {
  AIProvider,
  ProviderOptions,
  ProviderResult,
  StreamChunk,
  ProviderConfig,
  ProviderError,
  ProviderUnavailableError,
  ToolDefinition,
  ToolCall,
} from './provider.js';

export class GeminiProvider implements AIProvider {
  readonly name = 'Gemini';
  readonly type = 'gemini' as const;
  
  private client: GoogleGenerativeAI;
  private config: ProviderConfig;
  private defaultModel: string;

  constructor(config: ProviderConfig = {}) {
    this.config = config;
    this.defaultModel = config.model || process.env.GEMINI_MODEL || 'gemini-pro';
    
    this.client = new GoogleGenerativeAI(
      config.apiKey || process.env.GOOGLE_API_KEY || ''
    );
  }

  async execute(prompt: string, options?: ProviderOptions): Promise<ProviderResult> {
    if (!this.isAvailable()) {
      throw new ProviderUnavailableError(this.name, 'API key not configured');
    }

    try {
      const model = this.client.getGenerativeModel({
        model: options?.model || this.defaultModel,
      });

      const result = await model.generateContent({
        contents: [{ role: 'user', parts: [{ text: prompt }] }],
        generationConfig: {
          maxOutputTokens: options?.maxTokens || 4096,
          temperature: options?.temperature,
          stopSequences: options?.stopSequences,
        },
        systemInstruction: options?.systemPrompt
          ? { parts: [{ text: options.systemPrompt }] }
          : undefined,
      });

      return this.parseResponse(result);
    } catch (error) {
      throw this.handleError(error);
    }
  }

  async *stream(prompt: string, options?: ProviderOptions): AsyncIterable<StreamChunk> {
    if (!this.isAvailable()) {
      throw new ProviderUnavailableError(this.name, 'API key not configured');
    }

    try {
      const model = this.client.getGenerativeModel({
        model: options?.model || this.defaultModel,
      });

      const result = await model.generateContentStream({
        contents: [{ role: 'user', parts: [{ text: prompt }] }],
        generationConfig: {
          maxOutputTokens: options?.maxTokens || 4096,
          temperature: options?.temperature,
          stopSequences: options?.stopSequences,
        },
        systemInstruction: options?.systemPrompt
          ? { parts: [{ text: options.systemPrompt }] }
          : undefined,
      });

      let inputTokens = 0;
      let outputTokens = 0;

      for await (const chunk of result.stream) {
        const text = chunk.text();
        if (text) {
          yield { type: 'text', delta: text };
        }

        if (chunk.usageMetadata) {
          inputTokens = chunk.usageMetadata.promptTokenCount;
          outputTokens = chunk.usageMetadata.candidatesTokenCount;
        }
      }

      yield { type: 'usage', usage: { inputTokens, outputTokens } };
    } catch (error) {
      throw this.handleError(error);
    }
  }

  isAvailable(): boolean {
    return !!(this.config.apiKey || process.env.GOOGLE_API_KEY);
  }

  async getModels(): Promise<string[]> {
    return [
      'gemini-pro',
      'gemini-pro-vision',
      'gemini-1.5-pro',
      'gemini-1.5-flash',
    ];
  }

  getDefaultModel(): string {
    return this.defaultModel;
  }

  private parseResponse(result: Awaited<ReturnType<GoogleGenerativeAI['getGenerativeModel'] extends (...args: any) => any ? ReturnType<ReturnType<GoogleGenerativeAI['getGenerativeModel']>['generateContent']> : never>): ProviderResult {
    const response = result.response;
    const content = response.text();

    return {
      content,
      usage: response.usageMetadata
        ? {
            inputTokens: response.usageMetadata.promptTokenCount,
            outputTokens: response.usageMetadata.candidatesTokenCount,
          }
        : undefined,
      stopReason: response.candidates?.[0]?.finishReason as ProviderResult['stopReason'],
    };
  }

  private handleError(error: unknown): Error {
    if (error instanceof Error) {
      if (error.message.includes('429') || error.message.includes('quota')) {
        return new ProviderError(error.message, this.name, 'RATE_LIMIT', 429);
      }
      return new ProviderError(error.message, this.name);
    }
    return new ProviderError('Unknown error', this.name);
  }
}

// Note: Tool use support in Gemini is more limited than Claude/OpenAI
// Full tool/function calling requires additional configuration
