use crate::llm::{LLMAdapter, LLMError, LLMRequest, LLMResponse, TokenUsage};
use async_trait::async_trait;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tracing::{debug, error, info};

/// Gemini API configuration
#[derive(Debug, Clone)]
pub struct GeminiConfig {
    pub api_key: String,
    pub model: String,
    pub base_url: String,
    pub timeout_seconds: u64,
}

impl Default for GeminiConfig {
    fn default() -> Self {
        Self {
            api_key: String::new(),
            model: "gemini-2.5-flash".to_string(), // Use available model
            base_url: "https://generativelanguage.googleapis.com/v1beta".to_string(),
            timeout_seconds: 30,
        }
    }
}

/// Gemini API request structure
#[derive(Debug, Serialize)]
struct GeminiRequest {
    contents: Vec<Content>,
    generation_config: GenerationConfig,
}

#[derive(Debug, Serialize, Deserialize)]
struct Content {
    parts: Vec<Part>,
    role: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Part {
    text: String,
}

#[derive(Debug, Serialize)]
struct GenerationConfig {
    temperature: f64,
    max_output_tokens: Option<u32>,
    stop_sequences: Vec<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    seed: Option<i32>, // Gemini API requires i32, not u64
}

/// Gemini API response structure
#[derive(Debug, Deserialize)]
struct GeminiResponse {
    candidates: Vec<Candidate>,
    usage_metadata: Option<UsageMetadata>,
}

#[derive(Debug, Deserialize)]
struct Candidate {
    content: Content,
    finish_reason: Option<String>,
}

#[derive(Debug, Deserialize)]
struct UsageMetadata {
    prompt_token_count: Option<u32>,
    candidates_token_count: Option<u32>,
    total_token_count: Option<u32>,
}

/// Gemini LLM adapter
pub struct GeminiAdapter {
    config: GeminiConfig,
    client: Client,
}

impl GeminiAdapter {
    pub fn new(api_key: String) -> Self {
        Self::with_config(GeminiConfig {
            api_key,
            ..Default::default()
        })
    }
    
    pub fn with_config(config: GeminiConfig) -> Self {
        let client = Client::builder()
            .timeout(std::time::Duration::from_secs(config.timeout_seconds))
            .connect_timeout(std::time::Duration::from_secs(10))
            .build()
            .expect("Failed to create HTTP client");
        
        Self { config, client }
    }
}

#[async_trait]
impl LLMAdapter for GeminiAdapter {
    fn name(&self) -> &str {
        "gemini"
    }
    
    async fn generate(&self, request: &LLMRequest) -> Result<LLMResponse, LLMError> {
        info!(
            "Generating with Gemini (model: {}, seed: {})",
            self.config.model, request.seed
        );
        
        // Build Gemini request
        let mut contents = Vec::new();
        
        // Add system prompt if provided
        if let Some(ref system_prompt) = request.system_prompt {
            contents.push(Content {
                parts: vec![Part {
                    text: system_prompt.clone(),
                }],
                role: "user".to_string(),
            });
        }
        
        // Add main prompt
        contents.push(Content {
            parts: vec![Part {
                text: request.prompt.clone(),
            }],
            role: "user".to_string(),
        });
        
        // Gemini API requires seed to be i32 (max 2,147,483,647)
        // Convert u64 seed to valid i32 range by taking modulo
        let gemini_seed = (request.seed % (i32::MAX as u64 + 1)) as i32;
        
        let generation_config = GenerationConfig {
            temperature: request.temperature,
            max_output_tokens: request.max_tokens,
            stop_sequences: request.stop_sequences.clone(),
            seed: Some(gemini_seed), // Use seed for determinism (constrained to i32)
        };
        
        let gemini_request = GeminiRequest {
            contents,
            generation_config,
        };
        
        // Make API call using header-based authentication
        let url = format!(
            "{}/models/{}:generateContent",
            self.config.base_url, self.config.model
        );
        
        debug!("Calling Gemini API: {} (model: {})", url, self.config.model);
        
        let response = self
            .client
            .post(&url)
            .header("Content-Type", "application/json")
            .header("X-goog-api-key", &self.config.api_key)
            .json(&gemini_request)
            .send()
            .await?;
        
        if !response.status().is_success() {
            let status = response.status();
            let error_text = response.text().await.unwrap_or_default();
            error!("Gemini API error: {} - {}", status, error_text);
            return Err(LLMError::APIError(format!(
                "HTTP {}: {}",
                status, error_text
            )));
        }
        
        let gemini_response: GeminiResponse = response.json().await?;
        
        // Extract response
        let candidate = gemini_response
            .candidates
            .first()
            .ok_or_else(|| LLMError::APIError("No candidates in response".to_string()))?;
        
        let text = candidate
            .content
            .parts
            .first()
            .map(|p| p.text.clone())
            .unwrap_or_default();
        
        // Extract usage
        let usage = gemini_response
            .usage_metadata
            .map(|u| TokenUsage {
                prompt_tokens: u.prompt_token_count.unwrap_or(0),
                completion_tokens: u.candidates_token_count.unwrap_or(0),
                total_tokens: u.total_token_count.unwrap_or(0),
            })
            .unwrap_or_else(|| TokenUsage {
                prompt_tokens: 0,
                completion_tokens: 0,
                total_tokens: 0,
            });
        
        Ok(LLMResponse {
            text,
            model: self.config.model.clone(),
            usage,
            finish_reason: candidate.finish_reason.clone(),
            metadata: HashMap::new(),
        })
    }
}

