//! Provisioning Detector Integration Service
//!
//! Handles calling the provisioning-detector binary and parsing results

use std::process::Command;

use anyhow::Context;
use serde::{Deserialize, Serialize};
use tracing::{debug, warn};

use crate::error::{ControlCenterError, Result};

/// Technology detected by provisioning-detector
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct DetectorTechnology {
    pub name: String,
    pub confidence: f32,
    pub evidence_paths: Vec<String>,
}

/// Requirement inferred from detections
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct DetectorRequirement {
    pub technology: String,
    pub reason: String,
    pub confidence: f32,
    pub required: bool,
}

/// Raw output from provisioning-detector
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct DetectorOutput {
    pub project_path: String,
    pub overall_confidence: f32,
    pub detections: Vec<DetectorTechnology>,
    pub requirements: Vec<DetectorRequirement>,
}

/// Detector service for running analysis
pub struct DetectorService {
    detector_path: String,
}

impl DetectorService {
    /// Create a new detector service
    pub fn new(detector_path: Option<String>) -> Self {
        let path = detector_path.unwrap_or_else(|| {
            // Default to common installation paths
            if cfg!(unix) {
                "/usr/local/bin/provisioning-detector".to_string()
            } else {
                "provisioning-detector.exe".to_string()
            }
        });

        Self {
            detector_path: path,
        }
    }

    /// Run detection analysis on a project directory
    pub fn analyze_project(&self, project_path: &str) -> Result<DetectorOutput> {
        debug!("Starting analysis of project: {}", project_path);

        // Call the detector binary
        let output = Command::new(&self.detector_path)
            .args(["detect", project_path, "--format", "json"])
            .output()
            .context("Failed to execute provisioning-detector binary")?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            warn!("Detector returned error: {}", stderr);
            return Err(ControlCenterError::AnalysisFailed(format!(
                "Detector failed: {}",
                stderr
            )));
        }

        // Parse JSON output
        let json_output =
            String::from_utf8(output.stdout).context("Detector output is not valid UTF-8")?;

        let detector_output: DetectorOutput =
            serde_json::from_str(&json_output).context("Failed to parse detector JSON output")?;

        debug!(
            "Analysis complete: {} detections, {} requirements",
            detector_output.detections.len(),
            detector_output.requirements.len()
        );

        Ok(detector_output)
    }

    /// Analyze and convert to detection result format
    pub fn analyze_and_convert(
        &self,
        project_path: &str,
    ) -> Result<crate::services::iac_detection::DetectionResult> {
        let detector_output = self.analyze_project(project_path)?;

        // Convert detector technologies to our format
        let technologies = detector_output
            .detections
            .into_iter()
            .map(|d| crate::services::iac_detection::DetectedTechnology {
                technology: d.name,
                confidence: d.confidence,
                evidence: d.evidence_paths,
                version: None, // TODO: Extract version if available
            })
            .collect();

        // Convert requirements to inferences for rules
        let inferences: Vec<crate::services::iac_rules::RuleInference> = detector_output
            .requirements
            .into_iter()
            .map(|r| crate::services::iac_rules::RuleInference {
                technology: r.technology,
                reason: r.reason,
                confidence: r.confidence,
                required: r.required,
            })
            .collect();

        // Create detection result
        let result = crate::services::iac_detection::DetectionResult {
            id: uuid::Uuid::new_v4().to_string(),
            project_path: project_path.to_string(),
            organization: "default".to_string(),
            technologies,
            completeness: detector_output.overall_confidence,
            confidence: detector_output.overall_confidence,
            metadata: serde_json::json!({
                "inferences": inferences,
                "detector_version": "1.0",
                "analysis_type": "full_stack_detection"
            }),
            created_at: chrono::Utc::now().to_rfc3339(),
            updated_at: chrono::Utc::now().to_rfc3339(),
        };

        Ok(result)
    }
}

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

    #[test]
    fn test_detector_service_creation() {
        let service = DetectorService::new(None);
        assert!(!service.detector_path.is_empty());
    }

    #[test]
    fn test_detector_service_custom_path() {
        let custom_path = "/custom/path/detector".to_string();
        let service = DetectorService::new(Some(custom_path.clone()));
        assert_eq!(service.detector_path, custom_path);
    }

    #[test]
    fn test_detector_output_parsing() {
        let json = r#"{
            "project_path": "/home/user/project",
            "overall_confidence": 0.85,
            "detections": [
                {
                    "name": "python",
                    "confidence": 0.95,
                    "evidence_paths": ["requirements.txt", "setup.py"]
                }
            ],
            "requirements": [
                {
                    "technology": "postgresql",
                    "reason": "Python apps typically need a database",
                    "confidence": 0.8,
                    "required": true
                }
            ]
        }"#;

        let output: DetectorOutput = serde_json::from_str(json).expect("JSON parsing failed");
        assert_eq!(output.detections.len(), 1);
        assert_eq!(output.requirements.len(), 1);
        assert_eq!(output.overall_confidence, 0.85);
    }
}
