//! API client for communicating with the REST API server

use crate::types::*;
use anyhow::Result;
use once_cell::sync::Lazy;
use std::sync::Mutex;

/// API base URL (configurable at runtime)
static API_BASE_URL: Lazy<Mutex<String>> =
    Lazy::new(|| Mutex::new("http://localhost:3000/api".to_string()));

/// Set the API base URL
pub(crate) fn set_api_base(url: impl Into<String>) {
    if let Ok(mut guard) = API_BASE_URL.lock() {
        *guard = url.into();
    } else {
        tracing::warn!("Failed to acquire lock on API_BASE_URL");
    }
}

/// Get the current API base URL
fn get_api_base() -> String {
    API_BASE_URL
        .lock()
        .map(|guard| guard.clone())
        .unwrap_or_else(|_| {
            tracing::warn!("Failed to acquire lock on API_BASE_URL, using default");
            "http://localhost:3000/api".to_string()
        })
}

/// API client for the TUI
#[derive(Clone, Debug)]
pub(crate) struct ApiClient {
    client: reqwest::Client,
}

#[allow(dead_code)]
impl ApiClient {
    /// Create a new API client
    pub(crate) fn new() -> Self {
        Self {
            client: reqwest::Client::new(),
        }
    }

    /// Get all projects
    pub(crate) async fn list_projects(&self) -> Result<Vec<ProjectDto>> {
        let response = self
            .client
            .get(format!("{}/projects", get_api_base()))
            .send()
            .await?;

        Ok(response.json::<Vec<ProjectDto>>().await?)
    }

    /// Get a specific project
    pub(crate) async fn get_project(&self, id: &str) -> Result<ProjectDto> {
        let response = self
            .client
            .get(format!("{}/projects/{}", get_api_base(), id))
            .send()
            .await?;

        if response.status().is_success() {
            Ok(response.json::<ProjectDto>().await?)
        } else {
            anyhow::bail!("Project not found")
        }
    }

    /// Get project status
    pub(crate) async fn get_project_status(&self, id: &str) -> Result<ProjectStatusDto> {
        let response = self
            .client
            .get(format!("{}/projects/{}/status", get_api_base(), id))
            .send()
            .await?;

        Ok(response.json::<ProjectStatusDto>().await?)
    }

    /// Get security assessment
    pub(crate) async fn get_security_assessment(&self, id: &str) -> Result<SecurityAssessmentDto> {
        let response = self
            .client
            .get(format!("{}/projects/{}/security", get_api_base(), id))
            .send()
            .await?;

        if response.status().is_success() {
            Ok(response.json::<SecurityAssessmentDto>().await?)
        } else {
            anyhow::bail!("Assessment not found")
        }
    }

    /// Get checklist items
    pub(crate) async fn get_checklists(&self, project_id: &str) -> Result<Vec<ChecklistItemDto>> {
        let response = self
            .client
            .get(format!(
                "{}/projects/{}/checklists",
                get_api_base(),
                project_id
            ))
            .send()
            .await?;

        Ok(response.json::<Vec<ChecklistItemDto>>().await?)
    }

    /// Get project tools
    pub(crate) async fn get_tools(&self, project_id: &str) -> Result<Vec<ToolDto>> {
        let response = self
            .client
            .get(format!("{}/projects/{}/tools", get_api_base(), project_id))
            .send()
            .await?;

        Ok(response.json::<Vec<ToolDto>>().await?)
    }

    /// Create a new project
    pub(crate) async fn create_project(&self, req: CreateProjectRequest) -> Result<ProjectDto> {
        let response = self
            .client
            .post(format!("{}/projects", get_api_base()))
            .json(&req)
            .send()
            .await?;

        Ok(response.json::<ProjectDto>().await?)
    }
}

impl Default for ApiClient {
    fn default() -> Self {
        Self::new()
    }
}
