//! API integration layer for calling the syntaxis-api server
//!
//! Provides HTTP client functions for communicating with the REST API.
//! These functions are used by the web dashboard frontend.

use crate::filter::TaskFilterCriteria;
use crate::types::{
    AddChecklistItemRequest, AddCommentRequest, AuthTokenResponse, ChecklistItemDto,
    CreateFilterPresetRequest, CreateProjectRequest, FilterPresetDto, LoginRequest, ProjectDto,
    ProjectStatusDto, SecurityAssessmentDto, TaskAttachmentDto, TaskCommentDto, ToolDto,
    UpdateChecklistItemRequest, UpdateDeadlineRequest, UpdateProjectRequest,
};
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 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
pub 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()
        })
}

/// Get all projects
pub async fn list_projects() -> Result<Vec<ProjectDto>, String> {
    let client = reqwest::Client::new();
    let response = client
        .get(format!("{}/projects", get_api_base()))
        .send()
        .await
        .map_err(|e| format!("Failed to fetch projects: {}", e))?;

    response
        .json::<Vec<ProjectDto>>()
        .await
        .map_err(|e| format!("Failed to parse projects: {}", e))
}

/// Get a specific project
pub async fn get_project(id: String) -> Result<ProjectDto, String> {
    let client = reqwest::Client::new();
    let response = client
        .get(format!("{}/projects/{}", get_api_base(), id))
        .send()
        .await
        .map_err(|e| format!("Failed to fetch project: {}", e))?;

    if response.status().is_success() {
        response
            .json::<ProjectDto>()
            .await
            .map_err(|e| format!("Failed to parse project: {}", e))
    } else {
        Err("Project not found".to_string())
    }
}

/// Create a new project
pub async fn create_project(req: CreateProjectRequest) -> Result<ProjectDto, String> {
    let client = reqwest::Client::new();
    let response = client
        .post(format!("{}/projects", get_api_base()))
        .json(&req)
        .send()
        .await
        .map_err(|e| format!("Failed to create project: {}", e))?;

    response
        .json::<ProjectDto>()
        .await
        .map_err(|e| format!("Failed to parse response: {}", e))
}

/// Update a project
pub async fn update_project(id: String, req: UpdateProjectRequest) -> Result<ProjectDto, String> {
    let client = reqwest::Client::new();
    let response = client
        .put(format!("{}/projects/{}", get_api_base(), id))
        .json(&req)
        .send()
        .await
        .map_err(|e| format!("Failed to update project: {}", e))?;

    response
        .json::<ProjectDto>()
        .await
        .map_err(|e| format!("Failed to parse response: {}", e))
}

/// Get project status
pub async fn get_project_status(id: String) -> Result<ProjectStatusDto, String> {
    let client = reqwest::Client::new();
    let response = client
        .get(format!("{}/projects/{}/status", get_api_base(), id))
        .send()
        .await
        .map_err(|e| format!("Failed to fetch status: {}", e))?;

    response
        .json::<ProjectStatusDto>()
        .await
        .map_err(|e| format!("Failed to parse status: {}", e))
}

/// Get checklist items for a project
pub async fn get_checklists(project_id: String) -> Result<Vec<ChecklistItemDto>, String> {
    let client = reqwest::Client::new();
    let response = client
        .get(format!(
            "{}/projects/{}/checklists",
            get_api_base(),
            project_id
        ))
        .send()
        .await
        .map_err(|e| format!("Failed to fetch checklists: {}", e))?;

    response
        .json::<Vec<ChecklistItemDto>>()
        .await
        .map_err(|e| format!("Failed to parse checklists: {}", e))
}

/// Add a checklist item
pub async fn add_checklist_item(
    project_id: String,
    req: AddChecklistItemRequest,
) -> Result<ChecklistItemDto, String> {
    let client = reqwest::Client::new();
    let response = client
        .post(format!(
            "{}/projects/{}/checklists",
            get_api_base(),
            project_id
        ))
        .json(&req)
        .send()
        .await
        .map_err(|e| format!("Failed to add item: {}", e))?;

    response
        .json::<ChecklistItemDto>()
        .await
        .map_err(|e| format!("Failed to parse response: {}", e))
}

/// Get security assessment
pub async fn get_security_assessment(project_id: String) -> Result<SecurityAssessmentDto, String> {
    let client = reqwest::Client::new();
    let response = client
        .get(format!(
            "{}/projects/{}/security",
            get_api_base(),
            project_id
        ))
        .send()
        .await
        .map_err(|e| format!("Failed to fetch assessment: {}", e))?;

    if response.status().is_success() {
        response
            .json::<SecurityAssessmentDto>()
            .await
            .map_err(|e| format!("Failed to parse assessment: {}", e))
    } else {
        Err("Assessment not found".to_string())
    }
}

/// Get project tools
pub async fn get_tools(project_id: String) -> Result<Vec<ToolDto>, String> {
    let client = reqwest::Client::new();
    let response = client
        .get(format!("{}/projects/{}/tools", get_api_base(), project_id))
        .send()
        .await
        .map_err(|e| format!("Failed to fetch tools: {}", e))?;

    response
        .json::<Vec<ToolDto>>()
        .await
        .map_err(|e| format!("Failed to parse tools: {}", e))
}

/// Get filtered and sorted checklists for a project
///
/// This function fetches checklists and applies filtering/sorting on the client side.
/// For better performance with large datasets, filtering could be moved to the server.
pub async fn get_filtered_checklists(
    project_id: String,
    criteria: TaskFilterCriteria,
) -> Result<Vec<ChecklistItemDto>, String> {
    let checklists = get_checklists(project_id).await?;
    Ok(criteria.apply(&checklists))
}

/// Update a checklist item's completion status
pub async fn update_checklist_item(
    project_id: String,
    item_id: String,
    req: UpdateChecklistItemRequest,
) -> Result<ChecklistItemDto, String> {
    let client = reqwest::Client::new();
    let response = client
        .patch(format!(
            "{}/projects/{}/checklists/{}",
            get_api_base(),
            project_id,
            item_id
        ))
        .json(&req)
        .send()
        .await
        .map_err(|e| format!("Failed to update checklist item: {}", e))?;

    response
        .json::<ChecklistItemDto>()
        .await
        .map_err(|e| format!("Failed to parse response: {}", e))
}

/// Login and get authentication token
pub async fn login(req: LoginRequest) -> Result<AuthTokenResponse, String> {
    let client = reqwest::Client::new();
    let response = client
        .post(format!("{}/auth/login", get_api_base()))
        .json(&req)
        .send()
        .await
        .map_err(|e| format!("Failed to login: {}", e))?;

    if response.status().is_success() {
        response
            .json::<AuthTokenResponse>()
            .await
            .map_err(|e| format!("Failed to parse response: {}", e))
    } else {
        Err("Invalid credentials".to_string())
    }
}

/// Get task comments
pub async fn get_task_comments(
    task_id: String,
    auth_token: Option<&str>,
) -> Result<Vec<TaskCommentDto>, String> {
    let client = reqwest::Client::new();
    let mut request = client.get(format!("{}/tasks/{}/comments", get_api_base(), task_id));

    if let Some(token) = auth_token {
        request = request.header("Authorization", format!("Bearer {}", token));
    }

    let response = request
        .send()
        .await
        .map_err(|e| format!("Failed to fetch comments: {}", e))?;

    response
        .json::<Vec<TaskCommentDto>>()
        .await
        .map_err(|e| format!("Failed to parse comments: {}", e))
}

/// Add a comment to a task
pub async fn add_task_comment(
    task_id: String,
    req: AddCommentRequest,
    auth_token: &str,
) -> Result<TaskCommentDto, String> {
    let client = reqwest::Client::new();
    let response = client
        .post(format!("{}/tasks/{}/comments", get_api_base(), task_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .json(&req)
        .send()
        .await
        .map_err(|e| format!("Failed to add comment: {}", e))?;

    response
        .json::<TaskCommentDto>()
        .await
        .map_err(|e| format!("Failed to parse response: {}", e))
}

/// Delete a task comment
pub async fn delete_task_comment(
    task_id: String,
    comment_id: String,
    auth_token: &str,
) -> Result<(), String> {
    let client = reqwest::Client::new();
    let response = client
        .delete(format!(
            "{}/tasks/{}/comments/{}",
            get_api_base(),
            task_id,
            comment_id
        ))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .map_err(|e| format!("Failed to delete comment: {}", e))?;

    if response.status().is_success() {
        Ok(())
    } else {
        Err("Failed to delete comment".to_string())
    }
}

/// Get task attachments
pub async fn get_task_attachments(
    task_id: String,
    auth_token: Option<&str>,
) -> Result<Vec<TaskAttachmentDto>, String> {
    let client = reqwest::Client::new();
    let mut request = client.get(format!("{}/tasks/{}/attachments", get_api_base(), task_id));

    if let Some(token) = auth_token {
        request = request.header("Authorization", format!("Bearer {}", token));
    }

    let response = request
        .send()
        .await
        .map_err(|e| format!("Failed to fetch attachments: {}", e))?;

    response
        .json::<Vec<TaskAttachmentDto>>()
        .await
        .map_err(|e| format!("Failed to parse attachments: {}", e))
}

/// Delete a task attachment
pub async fn delete_task_attachment(
    task_id: String,
    attachment_id: String,
    auth_token: &str,
) -> Result<(), String> {
    let client = reqwest::Client::new();
    let response = client
        .delete(format!(
            "{}/tasks/{}/attachments/{}",
            get_api_base(),
            task_id,
            attachment_id
        ))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .map_err(|e| format!("Failed to delete attachment: {}", e))?;

    if response.status().is_success() {
        Ok(())
    } else {
        Err("Failed to delete attachment".to_string())
    }
}

/// Get filter presets for current user
pub async fn get_filter_presets(auth_token: &str) -> Result<Vec<FilterPresetDto>, String> {
    let client = reqwest::Client::new();
    let response = client
        .get(format!("{}/filter-presets", get_api_base()))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .map_err(|e| format!("Failed to fetch presets: {}", e))?;

    response
        .json::<Vec<FilterPresetDto>>()
        .await
        .map_err(|e| format!("Failed to parse presets: {}", e))
}

/// Create a filter preset
pub async fn create_filter_preset(
    req: CreateFilterPresetRequest,
    auth_token: &str,
) -> Result<FilterPresetDto, String> {
    let client = reqwest::Client::new();
    let response = client
        .post(format!("{}/filter-presets", get_api_base()))
        .header("Authorization", format!("Bearer {}", auth_token))
        .json(&req)
        .send()
        .await
        .map_err(|e| format!("Failed to create preset: {}", e))?;

    response
        .json::<FilterPresetDto>()
        .await
        .map_err(|e| format!("Failed to parse response: {}", e))
}

/// Delete a filter preset
pub async fn delete_filter_preset(preset_id: String, auth_token: &str) -> Result<(), String> {
    let client = reqwest::Client::new();
    let response = client
        .delete(format!("{}/filter-presets/{}", get_api_base(), preset_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .map_err(|e| format!("Failed to delete preset: {}", e))?;

    if response.status().is_success() {
        Ok(())
    } else {
        Err("Failed to delete preset".to_string())
    }
}

/// Update task deadline
pub async fn update_task_deadline(
    task_id: String,
    req: UpdateDeadlineRequest,
    auth_token: &str,
) -> Result<(), String> {
    let client = reqwest::Client::new();
    let response = client
        .put(format!("{}/tasks/{}/deadline", get_api_base(), task_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .json(&req)
        .send()
        .await
        .map_err(|e| format!("Failed to update deadline: {}", e))?;

    if response.status().is_success() {
        Ok(())
    } else {
        Err("Failed to update deadline".to_string())
    }
}

/// Delete task deadline
pub async fn delete_task_deadline(task_id: String, auth_token: &str) -> Result<(), String> {
    let client = reqwest::Client::new();
    let response = client
        .delete(format!("{}/tasks/{}/deadline", get_api_base(), task_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .map_err(|e| format!("Failed to delete deadline: {}", e))?;

    if response.status().is_success() {
        Ok(())
    } else {
        Err("Failed to delete deadline".to_string())
    }
}
