use compliance_core::error::CoreError;
use compliance_core::models::dast::{DastEvidence, DastFinding, DastTarget, DastVulnType};
use compliance_core::models::Severity;
use compliance_core::traits::dast_agent::{DastAgent, DastContext};
use tracing::info;

/// Server-Side Request Forgery (SSRF) testing agent
pub struct SsrfAgent {
    http: reqwest::Client,
}

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

    fn payloads(&self) -> Vec<(&str, &str)> {
        vec![
            ("http://127.0.0.1", "localhost IPv4"),
            ("http://[::1]", "localhost IPv6"),
            ("http://0.0.0.0", "zero address"),
            ("http://169.254.169.254/latest/meta-data/", "AWS metadata"),
            ("http://metadata.google.internal/", "GCP metadata"),
            ("http://127.0.0.1:22", "SSH port probe"),
            ("http://127.0.0.1:3306", "MySQL port probe"),
            ("http://localhost/admin", "localhost admin"),
        ]
    }

    fn internal_indicators(&self) -> Vec<&str> {
        vec![
            "ami-id",
            "instance-id",
            "local-hostname",
            "public-hostname",
            "iam/security-credentials",
            "computeMetadata",
            "OpenSSH",
            "mysql_native_password",
            "root:x:0:",
            "<!DOCTYPE html>",
        ]
    }
}

impl DastAgent for SsrfAgent {
    fn name(&self) -> &str {
        "ssrf"
    }

    async fn run(
        &self,
        target: &DastTarget,
        context: &DastContext,
    ) -> Result<Vec<DastFinding>, CoreError> {
        let mut findings = Vec::new();
        let target_id = target
            .id
            .map(|oid| oid.to_hex())
            .unwrap_or_else(|| "unknown".to_string());

        // Find endpoints with URL-like parameters
        for endpoint in &context.endpoints {
            let url_params: Vec<_> = endpoint
                .parameters
                .iter()
                .filter(|p| {
                    let name_lower = p.name.to_lowercase();
                    name_lower.contains("url")
                        || name_lower.contains("uri")
                        || name_lower.contains("link")
                        || name_lower.contains("src")
                        || name_lower.contains("redirect")
                        || name_lower.contains("callback")
                        || name_lower.contains("fetch")
                        || name_lower.contains("load")
                })
                .collect();

            if url_params.is_empty() {
                continue;
            }

            for param in &url_params {
                for (payload, technique) in self.payloads() {
                    let request = if endpoint.method == "POST" {
                        self.http
                            .post(&endpoint.url)
                            .form(&[(param.name.as_str(), payload)])
                    } else {
                        let test_url = format!("{}?{}={}", endpoint.url, param.name, payload);
                        self.http.get(&test_url)
                    };

                    let response = match request.send().await {
                        Ok(r) => r,
                        Err(_) => continue,
                    };

                    let status = response.status().as_u16();
                    let body = response.text().await.unwrap_or_default();

                    // Check for SSRF indicators
                    let body_lower = body.to_lowercase();
                    let is_vulnerable = self
                        .internal_indicators()
                        .iter()
                        .any(|indicator| body_lower.contains(&indicator.to_lowercase()));

                    if is_vulnerable {
                        let snippet = body.chars().take(500).collect::<String>();

                        let evidence = DastEvidence {
                            request_method: endpoint.method.clone(),
                            request_url: endpoint.url.clone(),
                            request_headers: None,
                            request_body: Some(format!("{}={}", param.name, payload)),
                            response_status: status,
                            response_headers: None,
                            response_snippet: Some(snippet),
                            screenshot_path: None,
                            payload: Some(payload.to_string()),
                            response_time_ms: None,
                        };

                        let mut finding = DastFinding::new(
                            String::new(),
                            target_id.clone(),
                            DastVulnType::Ssrf,
                            format!("SSRF ({technique}) via parameter '{}'", param.name),
                            format!(
                                "Server-side request forgery detected in parameter '{}' at {}. \
                                 The application made a request to an internal resource ({}).",
                                param.name, endpoint.url, payload
                            ),
                            Severity::High,
                            endpoint.url.clone(),
                            endpoint.method.clone(),
                        );
                        finding.parameter = Some(param.name.clone());
                        finding.exploitable = true;
                        finding.evidence = vec![evidence];
                        finding.cwe = Some("CWE-918".to_string());
                        finding.remediation = Some(
                            "Validate and sanitize all user-supplied URLs. \
                             Use allowlists for permitted domains and block internal IP ranges."
                                .to_string(),
                        );

                        findings.push(finding);
                        break;
                    }
                }
            }
        }

        info!(findings = findings.len(), "SSRF scan complete");
        Ok(findings)
    }
}
