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

use compliance_core::models::Finding;

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

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FindingsQuery {
    pub page: u64,
    pub severity: String,
    pub scan_type: String,
    pub status: String,
    pub repo_id: String,
    pub q: String,
    pub sort_by: String,
    pub sort_order: String,
}

#[server]
pub async fn fetch_findings(query: FindingsQuery) -> Result<FindingsListResponse, ServerFnError> {
    let state: super::server_state::ServerState =
        dioxus_fullstack::FullstackContext::extract().await?;

    let mut url = format!(
        "{}/api/v1/findings?page={}&limit=20",
        state.agent_api_url, query.page
    );
    if !query.severity.is_empty() {
        url.push_str(&format!("&severity={}", query.severity));
    }
    if !query.scan_type.is_empty() {
        url.push_str(&format!("&scan_type={}", query.scan_type));
    }
    if !query.status.is_empty() {
        url.push_str(&format!("&status={}", query.status));
    }
    if !query.repo_id.is_empty() {
        url.push_str(&format!("&repo_id={}", query.repo_id));
    }
    if !query.q.is_empty() {
        url.push_str(&format!(
            "&q={}",
            url::form_urlencoded::byte_serialize(query.q.as_bytes()).collect::<String>()
        ));
    }
    if !query.sort_by.is_empty() {
        url.push_str(&format!("&sort_by={}", query.sort_by));
    }
    if !query.sort_order.is_empty() {
        url.push_str(&format!("&sort_order={}", query.sort_order));
    }

    let resp = reqwest::get(&url)
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;
    let body: FindingsListResponse = resp
        .json()
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;
    Ok(body)
}

#[server]
pub async fn fetch_finding_detail(id: String) -> Result<Finding, ServerFnError> {
    let state: super::server_state::ServerState =
        dioxus_fullstack::FullstackContext::extract().await?;
    let url = format!("{}/api/v1/findings/{id}", state.agent_api_url);

    let resp = reqwest::get(&url)
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;
    let body: serde_json::Value = resp
        .json()
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;
    let finding: Finding = serde_json::from_value(body["data"].clone())
        .map_err(|e| ServerFnError::new(e.to_string()))?;
    Ok(finding)
}

#[server]
pub async fn update_finding_status(id: String, status: String) -> Result<(), ServerFnError> {
    let state: super::server_state::ServerState =
        dioxus_fullstack::FullstackContext::extract().await?;
    let url = format!("{}/api/v1/findings/{id}/status", state.agent_api_url);

    let client = reqwest::Client::new();
    client
        .patch(&url)
        .json(&serde_json::json!({ "status": status }))
        .send()
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;

    Ok(())
}

#[server]
pub async fn bulk_update_finding_status(
    ids: Vec<String>,
    status: String,
) -> Result<(), ServerFnError> {
    let state: super::server_state::ServerState =
        dioxus_fullstack::FullstackContext::extract().await?;
    let url = format!("{}/api/v1/findings/bulk-status", state.agent_api_url);

    let client = reqwest::Client::new();
    client
        .patch(&url)
        .json(&serde_json::json!({ "ids": ids, "status": status }))
        .send()
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;

    Ok(())
}

#[server]
pub async fn update_finding_feedback(id: String, feedback: String) -> Result<(), ServerFnError> {
    let state: super::server_state::ServerState =
        dioxus_fullstack::FullstackContext::extract().await?;
    let url = format!("{}/api/v1/findings/{id}/feedback", state.agent_api_url);

    let client = reqwest::Client::new();
    client
        .patch(&url)
        .json(&serde_json::json!({ "feedback": feedback }))
        .send()
        .await
        .map_err(|e| ServerFnError::new(e.to_string()))?;

    Ok(())
}
