use anyhow::Result;
use async_trait::async_trait;
use futures::stream::BoxStream;

use super::helpers;
use super::{ProviderResponse, ProviderStreamChunk};
use crate::{config::AppConfig, errors::AppError, models::UnifiedRequest};

pub struct DeepSeekProvider {
    client: reqwest::Client,
    config: crate::config::DeepSeekConfig,
    api_key: String,
    pricing: Vec<crate::config::ModelPricing>,
}

impl DeepSeekProvider {
    pub fn new(config: &crate::config::DeepSeekConfig, app_config: &AppConfig) -> Result<Self> {
        let api_key = app_config.get_api_key("deepseek")?;
        Self::new_with_key(config, app_config, api_key)
    }

    pub fn new_with_key(
        config: &crate::config::DeepSeekConfig,
        app_config: &AppConfig,
        api_key: String,
    ) -> Result<Self> {
        Ok(Self {
            client: reqwest::Client::new(),
            config: config.clone(),
            api_key,
            pricing: app_config.pricing.deepseek.clone(),
        })
    }
}

#[async_trait]
impl super::Provider for DeepSeekProvider {
    fn name(&self) -> &str {
        "deepseek"
    }

    fn supports_model(&self, model: &str) -> bool {
        model.starts_with("deepseek-") || model.contains("deepseek")
    }

    fn supports_multimodal(&self) -> bool {
        false
    }

    async fn chat_completion(&self, request: UnifiedRequest) -> Result<ProviderResponse, AppError> {
        let messages_json = helpers::messages_to_openai_json(&request.messages).await?;
        let body = helpers::build_openai_body(&request, messages_json, false);

        let response = self
            .client
            .post(format!("{}/chat/completions", self.config.base_url))
            .header("Authorization", format!("Bearer {}", self.api_key))
            .json(&body)
            .send()
            .await
            .map_err(|e| AppError::ProviderError(e.to_string()))?;

        if !response.status().is_success() {
            let error_text = response.text().await.unwrap_or_default();
            return Err(AppError::ProviderError(format!("DeepSeek API error: {}", error_text)));
        }

        let resp_json: serde_json::Value = response
            .json()
            .await
            .map_err(|e| AppError::ProviderError(e.to_string()))?;

        helpers::parse_openai_response(&resp_json, request.model)
    }

    fn estimate_tokens(&self, request: &UnifiedRequest) -> Result<u32> {
        Ok(crate::utils::tokens::estimate_request_tokens(&request.model, request))
    }

    fn calculate_cost(
        &self,
        model: &str,
        prompt_tokens: u32,
        completion_tokens: u32,
        registry: &crate::models::registry::ModelRegistry,
    ) -> f64 {
        helpers::calculate_cost_with_registry(
            model,
            prompt_tokens,
            completion_tokens,
            registry,
            &self.pricing,
            0.14,
            0.28,
        )
    }

    async fn chat_completion_stream(
        &self,
        request: UnifiedRequest,
    ) -> Result<BoxStream<'static, Result<ProviderStreamChunk, AppError>>, AppError> {
        // DeepSeek doesn't support images in streaming, use text-only
        let messages_json = helpers::messages_to_openai_json_text_only(&request.messages).await?;
        let body = helpers::build_openai_body(&request, messages_json, true);

        let es = reqwest_eventsource::EventSource::new(
            self.client
                .post(format!("{}/chat/completions", self.config.base_url))
                .header("Authorization", format!("Bearer {}", self.api_key))
                .json(&body),
        )
        .map_err(|e| AppError::ProviderError(format!("Failed to create EventSource: {}", e)))?;

        Ok(helpers::create_openai_stream(es, request.model, None))
    }
}
