/// IaC (Infrastructure-from-Code) API Client
///
/// Provides functions to interact with infrastructure detection, rules, and
/// deployment endpoints.
use gloo_net::http::Request;
use serde::{Deserialize, Serialize};

use crate::config::{endpoints, use_config};

// ============================================================================
// Detection Types
// ============================================================================

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectedTechnology {
    pub technology: String,
    pub confidence: f32,
    pub evidence: Vec<String>,
    pub version: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionResult {
    pub id: String,
    pub project: String,
    pub timestamp: String,
    pub detections: Vec<DetectedTechnology>,
    pub completeness: f32,
    pub confidence: f32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PagedDetectionsResponse {
    pub items: Vec<DetectionResult>,
    pub total: u32,
    pub page: u32,
    pub page_size: u32,
}

// ============================================================================
// Deployment Types
// ============================================================================

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DeploymentTask {
    pub id: String,
    pub task_type: String,
    pub name: String,
    pub description: String,
    pub depends_on: Vec<String>,
    pub parameters: serde_json::Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentPlan {
    pub id: String,
    pub name: String,
    pub description: String,
    pub organization: String,
    pub tasks: Vec<DeploymentTask>,
    pub created_at: String,
    pub scheduled_at: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PagedDeploymentsResponse {
    pub items: Vec<DeploymentPlan>,
    pub total: u32,
    pub page: u32,
    pub page_size: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubmissionResult {
    pub workflow_id: String,
    pub status: String,
    pub message: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentStatus {
    pub plan_id: String,
    pub workflow_id: Option<String>,
    pub status: String,
    pub progress_percentage: u32,
    pub completed_tasks: u32,
    pub total_tasks: u32,
    pub started_at: Option<String>,
    pub completed_at: Option<String>,
}

// ============================================================================
// Detection API Functions
// ============================================================================

/// Fetch all detection results
pub async fn fetch_detections(
    page: Option<u32>,
    page_size: Option<u32>,
) -> Result<PagedDetectionsResponse, String> {
    let config = use_config();
    let page = page.unwrap_or(1);
    let page_size = page_size.unwrap_or(20);
    let url = format!(
        "{}{}?page={}&page_size={}",
        config.api.control_center_url,
        endpoints::DETECTIONS,
        page,
        page_size
    );

    match Request::get(&url).send().await {
        Ok(response) => {
            if response.ok() {
                match response.json::<PagedDetectionsResponse>().await {
                    Ok(data) => Ok(data),
                    Err(e) => Err(format!("Failed to parse detections response: {}", e)),
                }
            } else {
                Err(format!("Failed to fetch detections: {}", response.status()))
            }
        }
        Err(e) => Err(format!("Request failed: {}", e)),
    }
}

/// Fetch a single detection result by ID
pub async fn fetch_detection(id: &str) -> Result<DetectionResult, String> {
    let config = use_config();
    let url = format!(
        "{}{}",
        config.api.control_center_url,
        endpoints::DETECTIONS_BY_ID.replace(":id", id)
    );

    match Request::get(&url).send().await {
        Ok(response) => {
            if response.ok() {
                match response.json::<DetectionResult>().await {
                    Ok(data) => Ok(data),
                    Err(e) => Err(format!("Failed to parse detection response: {}", e)),
                }
            } else {
                Err(format!("Failed to fetch detection: {}", response.status()))
            }
        }
        Err(e) => Err(format!("Request failed: {}", e)),
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyzeProjectRequest {
    pub project_path: String,
    pub organization: Option<String>,
}

/// Analyze a project for technologies
pub async fn analyze_project(
    project_path: String,
    organization: Option<String>,
) -> Result<DetectionResult, String> {
    let config = use_config();
    let url = format!(
        "{}{}",
        config.api.control_center_url,
        endpoints::ANALYZE_PROJECT
    );

    let request_body = AnalyzeProjectRequest {
        project_path,
        organization,
    };

    match Request::post(&url)
        .json(&request_body)
        .map_err(|e| format!("Failed to serialize request: {}", e))?
        .send()
        .await
    {
        Ok(response) => {
            if response.ok() {
                match response.json::<DetectionResult>().await {
                    Ok(data) => Ok(data),
                    Err(e) => Err(format!("Failed to parse analysis response: {}", e)),
                }
            } else {
                Err(format!("Analysis failed: {}", response.status()))
            }
        }
        Err(e) => Err(format!("Request failed: {}", e)),
    }
}

// ============================================================================
// Deployment API Functions
// ============================================================================

/// Fetch all deployment plans
pub async fn fetch_deployments(
    page: Option<u32>,
    page_size: Option<u32>,
) -> Result<PagedDeploymentsResponse, String> {
    let config = use_config();
    let page = page.unwrap_or(1);
    let page_size = page_size.unwrap_or(20);
    let url = format!(
        "{}{}?page={}&page_size={}",
        config.api.control_center_url,
        endpoints::DEPLOYMENTS,
        page,
        page_size
    );

    match Request::get(&url).send().await {
        Ok(response) => {
            if response.ok() {
                match response.json::<PagedDeploymentsResponse>().await {
                    Ok(data) => Ok(data),
                    Err(e) => Err(format!("Failed to parse deployments response: {}", e)),
                }
            } else {
                Err(format!(
                    "Failed to fetch deployments: {}",
                    response.status()
                ))
            }
        }
        Err(e) => Err(format!("Request failed: {}", e)),
    }
}

/// Fetch a single deployment plan by ID
pub async fn fetch_deployment(id: &str) -> Result<DeploymentPlan, String> {
    let config = use_config();
    let url = format!(
        "{}{}",
        config.api.control_center_url,
        endpoints::DEPLOYMENTS_BY_ID.replace(":id", id)
    );

    match Request::get(&url).send().await {
        Ok(response) => {
            if response.ok() {
                match response.json::<DeploymentPlan>().await {
                    Ok(data) => Ok(data),
                    Err(e) => Err(format!("Failed to parse deployment response: {}", e)),
                }
            } else {
                Err(format!("Failed to fetch deployment: {}", response.status()))
            }
        }
        Err(e) => Err(format!("Request failed: {}", e)),
    }
}

/// Submit a deployment plan to the orchestrator
pub async fn submit_deployment(id: &str) -> Result<SubmissionResult, String> {
    let config = use_config();
    let url = format!(
        "{}{}",
        config.api.control_center_url,
        endpoints::DEPLOYMENTS_SUBMIT.replace(":id", id)
    );

    match Request::post(&url).send().await {
        Ok(response) => {
            if response.ok() {
                match response.json::<SubmissionResult>().await {
                    Ok(data) => Ok(data),
                    Err(e) => Err(format!("Failed to parse submission response: {}", e)),
                }
            } else {
                Err(format!("Submission failed: {}", response.status()))
            }
        }
        Err(e) => Err(format!("Request failed: {}", e)),
    }
}

/// Fetch deployment execution status
pub async fn fetch_deployment_status(id: &str) -> Result<DeploymentStatus, String> {
    let config = use_config();
    let url = format!(
        "{}{}",
        config.api.control_center_url,
        endpoints::DEPLOYMENTS_STATUS.replace(":id", id)
    );

    match Request::get(&url).send().await {
        Ok(response) => {
            if response.ok() {
                match response.json::<DeploymentStatus>().await {
                    Ok(data) => Ok(data),
                    Err(e) => Err(format!("Failed to parse status response: {}", e)),
                }
            } else {
                Err(format!("Failed to fetch status: {}", response.status()))
            }
        }
        Err(e) => Err(format!("Request failed: {}", e)),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_detection_result_creation() {
        let detection = DetectionResult {
            id: "test-id".to_string(),
            project: "test-project".to_string(),
            timestamp: "2025-11-14T00:00:00Z".to_string(),
            detections: vec![],
            completeness: 0.8,
            confidence: 0.9,
        };

        assert_eq!(detection.id, "test-id");
        assert_eq!(detection.completeness, 0.8);
    }

    #[test]
    fn test_deployment_plan_creation() {
        let plan = DeploymentPlan {
            id: "plan-id".to_string(),
            name: "Test Plan".to_string(),
            description: "Test deployment".to_string(),
            organization: "default".to_string(),
            tasks: vec![],
            created_at: "2025-11-14T00:00:00Z".to_string(),
            scheduled_at: None,
        };

        assert_eq!(plan.name, "Test Plan");
        assert_eq!(plan.tasks.len(), 0);
    }
}
