use async_trait::async_trait;
use serde_json::json;
use crate::traits::{Embedder, Result, CerebroError};

/// Embedder utilizing the standard OpenAI REST APIs (e.g., text-embedding-3-small).
pub struct OpenAIEmbedder {
    api_key: String,
    model: String,
    client: reqwest::Client,
}

impl OpenAIEmbedder {
    pub fn new(api_key: impl Into<String>, model: &str) -> Self {
        Self {
            api_key: api_key.into(),
            model: model.to_string(),
            client: reqwest::Client::new(),
        }
    }
}

#[async_trait]
impl Embedder for OpenAIEmbedder {
    async fn embed(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>> {
        let url = "https://api.openai.com/v1/embeddings";
        let payload = json!({ "input": texts, "model": self.model });

        let resp = self.client.post(url)
            .bearer_auth(&self.api_key)
            .json(&payload)
            .send().await
            .map_err(|e| CerebroError::EmbeddingError(e.to_string()))?;

        if !resp.status().is_success() {
            let err_text = resp.text().await.unwrap_or_default();
            return Err(CerebroError::EmbeddingError(format!("OpenAI API Error: {}", err_text)));
        }

        let data: serde_json::Value = resp.json().await
            .map_err(|e| CerebroError::EmbeddingError(e.to_string()))?;

        let mut results = Vec::new();
        if let Some(data_array) = data["data"].as_array() {
            for item in data_array {
                if let Some(embedding) = item["embedding"].as_array() {
                    let vec_f32: Vec<f32> = embedding.iter()
                        .filter_map(|v| v.as_f64().map(|f| f as f32))
                        .collect();
                    results.push(vec_f32);
                }
            }
        }
        Ok(results)
    }
}
