use dioxus::prelude::*;
use serde::{Deserialize, Serialize};

// ── Response types ──

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatApiResponse {
    pub data: ChatResponseData,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatResponseData {
    pub message: String,
    #[serde(default)]
    pub sources: Vec<SourceRef>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SourceRef {
    pub file_path: String,
    pub qualified_name: String,
    pub start_line: u32,
    pub end_line: u32,
    pub language: String,
    pub snippet: String,
    pub score: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmbeddingStatusResponse {
    pub data: Option<EmbeddingBuildData>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmbeddingBuildData {
    pub repo_id: String,
    pub status: String,
    pub total_chunks: u32,
    pub embedded_chunks: u32,
    pub embedding_model: String,
    pub error_message: Option<String>,
    #[serde(default)]
    pub started_at: Option<serde_json::Value>,
    #[serde(default)]
    pub completed_at: Option<serde_json::Value>,
}

// ── Chat message history type ──

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatHistoryMessage {
    pub role: String,
    pub content: String,
}

// ── Server functions ──

#[server]
pub async fn send_chat_message(
    repo_id: String,
    message: String,
    history: Vec<ChatHistoryMessage>,
) -> Result<ChatApiResponse, ServerFnError> {
    // Chat uses a longer timeout because the LLM round-trip can be slow;
    // agent_request doesn't expose a per-call timeout so we layer one on.
    let resp = super::agent_client::agent_request(
        reqwest::Method::POST,
        &format!("/api/v1/chat/{repo_id}"),
    )
    .await?
    .timeout(std::time::Duration::from_secs(120))
    .json(&serde_json::json!({
        "message": message,
        "history": history,
    }))
    .send()
    .await
    .map_err(|e| ServerFnError::new(format!("Request failed: {e}")))?;

    let text = resp
        .text()
        .await
        .map_err(|e| ServerFnError::new(format!("Failed to read response: {e}")))?;

    let body: ChatApiResponse = serde_json::from_str(&text)
        .map_err(|e| ServerFnError::new(format!("Failed to parse response: {e} — body: {text}")))?;
    Ok(body)
}

#[server]
pub async fn trigger_embedding_build(repo_id: String) -> Result<(), ServerFnError> {
    super::agent_client::agent_request(
        reqwest::Method::POST,
        &format!("/api/v1/chat/{repo_id}/build-embeddings"),
    )
    .await?
    .send()
    .await
    .map_err(|e| ServerFnError::new(e.to_string()))?;
    Ok(())
}

#[server]
pub async fn fetch_embedding_status(
    repo_id: String,
) -> Result<EmbeddingStatusResponse, ServerFnError> {
    let resp = super::agent_client::agent_get(&format!("/api/v1/chat/{repo_id}/status"))
        .await?
        .send()
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;
    let body: EmbeddingStatusResponse = resp
        .json()
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;
    Ok(body)
}
