use std::path::Path;

use serde::Deserialize;

/// Rust project detector
/// Detects: Rust, Cargo, frameworks (Axum, Tokio, etc.)
use super::helpers;
use crate::error::DetectionResult;
use crate::models::{Detection, Evidence, Technology};

#[derive(Debug, Deserialize)]
pub struct CargoToml {
    pub package: Option<CargoPackage>,
    pub dependencies: Option<std::collections::HashMap<String, toml::Value>>,
    #[serde(rename = "dev-dependencies")]
    pub dev_dependencies: Option<std::collections::HashMap<String, toml::Value>>,
}

#[derive(Debug, Deserialize)]
pub struct CargoPackage {
    pub name: Option<String>,
    pub version: Option<String>,
    pub edition: Option<String>,
}

pub struct RustDetector;

impl Default for RustDetector {
    fn default() -> Self {
        Self
    }
}

impl RustDetector {
    pub fn new() -> Self {
        Self
    }

    fn detect_cargo_toml(&self, project_path: &Path) -> DetectionResult<Option<Detection>> {
        if !helpers::file_exists(project_path, "Cargo.toml")? {
            return Ok(None);
        }

        let cargo_toml: CargoToml = helpers::read_toml(project_path, "Cargo.toml")?;

        let mut detection = Detection::new(Technology::Rust);

        // Add evidence: Cargo.toml exists
        detection = detection.add_evidence(Evidence::FileExists {
            path: project_path.join("Cargo.toml"),
            reason: "Cargo.toml is primary Rust indicator".into(),
        });

        // Extract version from package
        if let Some(pkg) = &cargo_toml.package {
            if let Some(version) = &pkg.version {
                detection = detection.with_version(version.clone());
            }
        }

        // Check for framework dependencies
        self.detect_frameworks(&cargo_toml, &mut detection);

        // Check for src/main.rs or src/lib.rs
        self.check_source_files(project_path, &mut detection)?;

        detection.calculate_confidence();
        Ok(Some(detection))
    }

    fn detect_frameworks(&self, cargo_toml: &CargoToml, detection: &mut Detection) {
        let frameworks = [
            ("tokio", "Async runtime"),
            ("axum", "Web framework"),
            ("actix-web", "Web framework"),
            ("rocket", "Web framework"),
            ("serde", "Serialization"),
            ("clap", "CLI argument parser"),
        ];

        if let Some(deps) = &cargo_toml.dependencies {
            for (name, description) in &frameworks {
                if deps.contains_key(*name) {
                    *detection = detection.clone().add_evidence(Evidence::Dependency {
                        name: name.to_string(),
                        version: deps.get(*name).and_then(|v| v.as_str()).map(String::from),
                        reason: description.to_string(),
                    });
                }
            }
        }
    }

    fn check_source_files(
        &self,
        project_path: &Path,
        detection: &mut Detection,
    ) -> DetectionResult<()> {
        if helpers::file_exists(project_path, "src/main.rs")? {
            *detection = detection.clone().add_evidence(Evidence::FileExists {
                path: project_path.join("src/main.rs"),
                reason: "Rust binary project".into(),
            });
        }

        if helpers::file_exists(project_path, "src/lib.rs")? {
            *detection = detection.clone().add_evidence(Evidence::FileExists {
                path: project_path.join("src/lib.rs"),
                reason: "Rust library project".into(),
            });
        }

        Ok(())
    }
}

impl super::Detector for RustDetector {
    fn name(&self) -> &str {
        "rust"
    }

    fn detect(&self, project_path: &Path) -> DetectionResult<Option<Detection>> {
        self.detect_cargo_toml(project_path)
    }

    fn priority(&self) -> u8 {
        85 // High priority - Cargo.toml is very reliable
    }
}

#[cfg(test)]
mod tests {
    use tempfile::TempDir;

    use super::*;
    use crate::detectors::Detector;

    #[test]
    fn test_detect_rust_with_cargo_toml() -> anyhow::Result<()> {
        let temp_dir = TempDir::new()?;
        let cargo_path = temp_dir.path().join("Cargo.toml");

        std::fs::write(
            &cargo_path,
            r#"[package]
name = "my-app"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1.0", features = ["full"] }
axum = "0.7"
"#,
        )?;

        // Create src/main.rs
        std::fs::create_dir(temp_dir.path().join("src"))?;
        std::fs::write(temp_dir.path().join("src/main.rs"), "fn main() {}")?;

        let detector = RustDetector::new();
        let result = detector.detect(temp_dir.path())?;

        assert!(result.is_some());
        let detection = result.unwrap();
        assert_eq!(detection.technology, Technology::Rust);
        assert!(detection.confidence > 0.0);

        Ok(())
    }
}
