use anyhow::{Context, Result};
use reqwest::Client;
use serde::{Deserialize, Serialize};

pub struct Embedder {
    model: String,
    client: Client,
    base_url: String,
}

#[derive(Serialize)]
struct EmbedRequest {
    model: String,
    prompt: String,
}

#[derive(Deserialize, Debug)]
struct EmbedResponse {
    embedding: Vec<f32>,
}

impl Embedder {
    pub fn new(model: String) -> Self {
        Self {
            model,
            client: Client::new(),
            base_url: "http://localhost:11434".to_string(),
        }
    }

    pub async fn embed_text(&self, text: &str) -> Result<Vec<f32>> {
        self.embed_with_prefix(text, "").await
    }

    pub async fn embed_document(&self, text: &str) -> Result<Vec<f32>> {
        self.embed_with_prefix(text, "search_document: ").await
    }

    pub async fn embed_query(&self, text: &str) -> Result<Vec<f32>> {
        self.embed_with_prefix(text, "search_query: ").await
    }

    async fn embed_with_prefix(&self, text: &str, prefix: &str) -> Result<Vec<f32>> {
        let url = format!("{}/api/embeddings", self.base_url);
        let prompt = format!("{}{}", prefix, text);

        let response = self
            .client
            .post(&url)
            .json(&EmbedRequest {
                model: self.model.clone(),
                prompt,
            })
            .send()
            .await
            .context("Failed to send embedding request to Ollama")?;

        if !response.status().is_success() {
            let status = response.status();
            let body = response.text().await.unwrap_or_default();
            anyhow::bail!("Ollama API error ({}): {}", status, body);
        }

        let result: EmbedResponse = response
            .json()
            .await
            .context("Failed to parse Ollama response")?;

        Ok(result.embedding)
    }

    pub async fn embed_chunk_content(&self, chunk: &crate::core::chunk::Chunk) -> Result<Vec<f32>> {
        let text = serde_json::to_string(&chunk.content)?;
        self.embed_document(&text).await
    }

    pub fn dimension(&self) -> usize {
        768
    }
}
