use std::collections::HashMap;

use compliance_core::error::CoreError;
use tracing::info;

/// Result of reconnaissance scanning
#[derive(Debug, Clone)]
pub struct ReconResult {
    pub technologies: Vec<String>,
    pub interesting_headers: HashMap<String, String>,
    pub server: Option<String>,
    pub open_ports: Vec<u16>,
}

/// Agent that performs reconnaissance on a target
pub struct ReconAgent {
    http: reqwest::Client,
}

impl ReconAgent {
    pub fn new(http: reqwest::Client) -> Self {
        Self { http }
    }

    /// Perform reconnaissance on a target URL
    pub async fn scan(&self, base_url: &str) -> Result<ReconResult, CoreError> {
        let mut result = ReconResult {
            technologies: Vec::new(),
            interesting_headers: HashMap::new(),
            server: None,
            open_ports: Vec::new(),
        };

        // HTTP header fingerprinting
        let response = self
            .http
            .get(base_url)
            .send()
            .await
            .map_err(|e| CoreError::Dast(format!("Failed to connect to target: {e}")))?;

        let headers = response.headers();

        // Extract server info
        if let Some(server) = headers.get("server") {
            let server_str = server.to_str().unwrap_or("unknown").to_string();
            result.server = Some(server_str.clone());
            result.technologies.push(server_str);
        }

        // Detect technologies from headers
        let security_headers = [
            "x-powered-by",
            "x-aspnet-version",
            "x-frame-options",
            "x-xss-protection",
            "x-content-type-options",
            "strict-transport-security",
            "content-security-policy",
            "x-generator",
        ];

        for header_name in &security_headers {
            if let Some(value) = headers.get(*header_name) {
                let value_str = value.to_str().unwrap_or("").to_string();
                result
                    .interesting_headers
                    .insert(header_name.to_string(), value_str.clone());

                if *header_name == "x-powered-by" || *header_name == "x-generator" {
                    result.technologies.push(value_str);
                }
            }
        }

        // Check for missing security headers
        let missing_security = [
            "strict-transport-security",
            "x-content-type-options",
            "x-frame-options",
        ];
        for header in &missing_security {
            if !headers.contains_key(*header) {
                result
                    .interesting_headers
                    .insert(format!("missing:{header}"), "Not present".to_string());
            }
        }

        // Detect technology from response body
        let body = response
            .text()
            .await
            .map_err(|e| CoreError::Dast(format!("Failed to read response: {e}")))?;

        self.detect_technologies_from_body(&body, &mut result);

        info!(
            url = base_url,
            technologies = ?result.technologies,
            "Reconnaissance complete"
        );

        Ok(result)
    }

    fn detect_technologies_from_body(&self, body: &str, result: &mut ReconResult) {
        let patterns = [
            ("React", r#"react"#),
            ("Angular", r#"ng-version"#),
            ("Vue.js", r#"vue"#),
            ("jQuery", r#"jquery"#),
            ("WordPress", r#"wp-content"#),
            ("Django", r#"csrfmiddlewaretoken"#),
            ("Rails", r#"csrf-token"#),
            ("Laravel", r#"laravel"#),
            ("Express", r#"express"#),
            ("Next.js", r#"__NEXT_DATA__"#),
            ("Nuxt.js", r#"__NUXT__"#),
        ];

        let body_lower = body.to_lowercase();
        for (tech, pattern) in &patterns {
            if body_lower.contains(&pattern.to_lowercase())
                && !result.technologies.contains(&tech.to_string())
            {
                result.technologies.push(tech.to_string());
            }
        }
    }
}
