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

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

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct GraphData {
    pub build: Option<serde_json::Value>,
    pub nodes: Vec<serde_json::Value>,
    pub edges: Vec<serde_json::Value>,
}

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

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CommunitiesResponse {
    pub data: Vec<serde_json::Value>,
    pub total: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct NodesResponse {
    pub data: Vec<serde_json::Value>,
    pub total: Option<u64>,
}

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

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FileContentData {
    pub content: String,
    pub path: String,
    pub language: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SearchResponse {
    pub data: Vec<serde_json::Value>,
    pub total: Option<u64>,
}

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

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

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

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

#[server]
pub async fn search_nodes(repo_id: String, query: String) -> Result<SearchResponse, ServerFnError> {
    let resp = super::agent_client::agent_get(&format!(
        "/api/v1/graph/{repo_id}/search?q={query}&limit=50"
    ))
    .await?
    .send()
    .await
    .map_err(|e| ServerFnError::new(e.to_string()))?;
    let body: SearchResponse = resp
        .json()
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;
    Ok(body)
}

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