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;

/// Authentication bypass testing agent
pub struct AuthBypassAgent {
    http: reqwest::Client,
}

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

impl DastAgent for AuthBypassAgent {
    fn name(&self) -> &str {
        "auth_bypass"
    }

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

        // Test 1: Access protected endpoints without authentication
        for endpoint in &context.endpoints {
            if !endpoint.requires_auth {
                continue;
            }

            // Try accessing without auth
            let response = match self.http.get(&endpoint.url).send().await {
                Ok(r) => r,
                Err(_) => continue,
            };

            let status = response.status().as_u16();

            // If we get 200 on a supposedly auth-required endpoint
            if status == 200 {
                let body = response.text().await.unwrap_or_default();
                let snippet = body.chars().take(500).collect::<String>();

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

                let mut finding = DastFinding::new(
                    String::new(),
                    target_id.clone(),
                    DastVulnType::AuthBypass,
                    format!("Authentication bypass on {}", endpoint.url),
                    format!(
                        "Protected endpoint {} returned HTTP 200 without authentication credentials.",
                        endpoint.url
                    ),
                    Severity::Critical,
                    endpoint.url.clone(),
                    "GET".to_string(),
                );
                finding.exploitable = true;
                finding.evidence = vec![evidence];
                finding.cwe = Some("CWE-287".to_string());
                finding.remediation = Some(
                    "Ensure all protected endpoints validate authentication tokens. \
                     Implement server-side authentication checks that cannot be bypassed."
                        .to_string(),
                );

                findings.push(finding);
            }
        }

        // Test 2: HTTP method tampering
        let methods = ["PUT", "PATCH", "DELETE", "OPTIONS"];
        for endpoint in &context.endpoints {
            if endpoint.method != "GET" && endpoint.method != "POST" {
                continue;
            }

            for method in &methods {
                let response = match self
                    .http
                    .request(
                        reqwest::Method::from_bytes(method.as_bytes())
                            .unwrap_or(reqwest::Method::GET),
                        &endpoint.url,
                    )
                    .send()
                    .await
                {
                    Ok(r) => r,
                    Err(_) => continue,
                };

                let status = response.status().as_u16();

                // If a non-standard method returns 200 when it shouldn't
                if status == 200 && *method == "DELETE" && !target.allow_destructive {
                    let evidence = DastEvidence {
                        request_method: method.to_string(),
                        request_url: endpoint.url.clone(),
                        request_headers: None,
                        request_body: None,
                        response_status: status,
                        response_headers: None,
                        response_snippet: None,
                        screenshot_path: None,
                        payload: None,
                        response_time_ms: None,
                    };

                    let mut finding = DastFinding::new(
                        String::new(),
                        target_id.clone(),
                        DastVulnType::AuthBypass,
                        format!(
                            "HTTP method tampering: {} accepted on {}",
                            method, endpoint.url
                        ),
                        format!(
                            "Endpoint {} accepts {} requests which may bypass access controls.",
                            endpoint.url, method
                        ),
                        Severity::Medium,
                        endpoint.url.clone(),
                        method.to_string(),
                    );
                    finding.evidence = vec![evidence];
                    finding.cwe = Some("CWE-288".to_string());

                    findings.push(finding);
                }
            }
        }

        // Test 3: Path traversal for auth bypass
        let traversal_paths = [
            "/../admin",
            "/..;/admin",
            "/%2e%2e/admin",
            "/admin%00",
            "/ADMIN",
            "/Admin",
        ];

        for path in &traversal_paths {
            let test_url = format!("{}{}", target.base_url.trim_end_matches('/'), path);
            let response = match self.http.get(&test_url).send().await {
                Ok(r) => r,
                Err(_) => continue,
            };

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

                // Check if response looks like an admin page
                let body_lower = body.to_lowercase();
                if body_lower.contains("admin")
                    || body_lower.contains("dashboard")
                    || body_lower.contains("management")
                {
                    let snippet = body.chars().take(500).collect::<String>();
                    let evidence = DastEvidence {
                        request_method: "GET".to_string(),
                        request_url: test_url.clone(),
                        request_headers: None,
                        request_body: None,
                        response_status: status,
                        response_headers: None,
                        response_snippet: Some(snippet),
                        screenshot_path: None,
                        payload: Some(path.to_string()),
                        response_time_ms: None,
                    };

                    let mut finding = DastFinding::new(
                        String::new(),
                        target_id.clone(),
                        DastVulnType::AuthBypass,
                        format!("Path traversal auth bypass: {path}"),
                        format!(
                            "Possible authentication bypass via path traversal. \
                             Accessing '{}' returned admin-like content.",
                            test_url
                        ),
                        Severity::High,
                        test_url,
                        "GET".to_string(),
                    );
                    finding.evidence = vec![evidence];
                    finding.cwe = Some("CWE-22".to_string());

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

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