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;

/// Cross-Site Scripting (XSS) testing agent
pub struct XssAgent {
    http: reqwest::Client,
}

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

    fn payloads(&self) -> Vec<(&str, &str)> {
        vec![
            ("<script>alert(1)</script>", "basic script injection"),
            ("<img src=x onerror=alert(1)>", "event handler injection"),
            ("<svg/onload=alert(1)>", "svg event handler"),
            ("javascript:alert(1)", "javascript protocol"),
            ("'\"><script>alert(1)</script>", "attribute breakout"),
            ("<body onload=alert(1)>", "body event handler"),
        ]
    }
}

impl DastAgent for XssAgent {
    fn name(&self) -> &str {
        "xss"
    }

    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());

        for endpoint in &context.endpoints {
            if endpoint.parameters.is_empty() {
                continue;
            }

            for param in &endpoint.parameters {
                for (payload, technique) in self.payloads() {
                    let test_url = if endpoint.method == "GET" {
                        format!("{}?{}={}", endpoint.url, param.name, payload)
                    } else {
                        endpoint.url.clone()
                    };

                    let request = if endpoint.method == "POST" {
                        self.http
                            .post(&endpoint.url)
                            .form(&[(param.name.as_str(), payload)])
                    } else {
                        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 if payload is reflected in response without encoding
                    if body.contains(payload) {
                        let snippet = body.chars().take(500).collect::<String>();

                        let evidence = DastEvidence {
                            request_method: endpoint.method.clone(),
                            request_url: test_url.clone(),
                            request_headers: None,
                            request_body: if endpoint.method == "POST" {
                                Some(format!("{}={}", param.name, payload))
                            } else {
                                None
                            },
                            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::Xss,
                            format!("Reflected XSS ({technique}) in parameter '{}'", param.name),
                            format!(
                                "Cross-site scripting vulnerability detected in parameter '{}' at {}. \
                                 The injected payload was reflected in the response without proper encoding.",
                                param.name, endpoint.url
                            ),
                            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-79".to_string());
                        finding.remediation = Some(
                            "Encode all user input before rendering in HTML context. \
                             Use Content-Security-Policy headers to mitigate impact."
                                .to_string(),
                        );

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

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