from civicshield_core.utils.http_client import safe_request


class VulnerabilityScanner:
    def __init__(self):
        self.payloads = {
            "SQLi": [
                "' OR 1=1--",
                '" OR "1"="1',
                "' UNION SELECT NULL--",
            ],
            "XSS": [
                "<script>alert(1)</script>",
                '\"><svg/onload=alert(1)>',
                "<img src=x onerror=alert(1)>",
            ],
        }
        self.stats = {
            "payloads_tested": 0,
            "requests_made": 0,
            "endpoints_tested": 0,
            "findings_count": 0,
        }
        self.sql_error_markers = [
            "sql syntax",
            "syntax error",
            "mysql",
            "postgres",
            "sqlite",
            "unclosed quotation mark",
            "odbc",
            "database error",
            "error in your sql",
        ]
        self.success_markers = ["welcome", "dashboard", "logout", "sign off"]

    def _request(self, method, url, **kwargs):
        self.stats["requests_made"] += 1
        return safe_request(method, url, timeout=(3, 5), **kwargs)

    def _detect_sqli(self, response_text: str, response_url: str):
        text = response_text.lower()
        if any(marker in text for marker in self.sql_error_markers):
            return True, "Server responded with SQL/database error indicators"
        if "union select" in text or "or 1=1" in text:
            return True, "Response reflected classic SQL injection syntax"
        if "login" not in response_url.lower() and any(marker in text for marker in self.success_markers):
            return True, "Potential authentication bypass or unexpected privileged response"
        return False, ""

    def _detect_xss(self, response_text: str, payload: str):
        lowered = response_text.lower()
        if payload.lower() in lowered:
            return True, "Payload reflected directly in the response"
        if "<script>alert(1)</script>" in lowered or "onerror=alert(1)" in lowered or "onload=alert(1)" in lowered:
            return True, "Response contains executable script markers"
        return False, ""

    def _build_finding(self, vuln: str, param: str, payload: str, evidence: str, url: str, method: str, signal: str):
        if vuln == "SQLi":
            risk = "CRITICAL"
            remediation = "Use parameterized queries, validate input server-side, and return generic database errors only."
        else:
            risk = "HIGH"
            remediation = "Apply output encoding, validate input, and use contextual escaping or templating protections."

        return {
            "vuln": vuln,
            "param": param,
            "payload": payload,
            "evidence": evidence[:240],
            "url": url,
            "method": method,
            "risk": risk,
            "signal": signal,
            "remediation": remediation,
        }

    def test_one(self, url, params):
        findings = []
        seen_signatures = set()
        self.stats["endpoints_tested"] += 1

        for param in params:
            for vuln, payloads in self.payloads.items():
                for payload in payloads:
                    data = {p: "1" for p in params}
                    data[param] = payload
                    self.stats["payloads_tested"] += 1

                    try:
                        print(f"[*] Testing {vuln} on {url} param {param}")
                        get_response = self._request("GET", url, params=data)

                        if vuln == "SQLi":
                            matched, signal = self._detect_sqli(get_response.text, str(get_response.url))
                        else:
                            matched, signal = self._detect_xss(get_response.text, payload)

                        if matched:
                            signature = (vuln, param, signal)
                            if signature not in seen_signatures:
                                seen_signatures.add(signature)
                                findings.append(
                                    self._build_finding(vuln, param, payload, get_response.text, url, "GET", signal)
                                )
                            continue

                        post_response = self._request("POST", url, data=data)
                        if vuln == "SQLi":
                            matched, signal = self._detect_sqli(post_response.text, str(post_response.url))
                        else:
                            matched, signal = self._detect_xss(post_response.text, payload)

                        if matched:
                            signature = (vuln, param, signal)
                            if signature not in seen_signatures:
                                seen_signatures.add(signature)
                                evidence = post_response.text
                                if signal.startswith("Potential authentication bypass"):
                                    evidence = f"Redirected to: {post_response.url}"
                                findings.append(
                                    self._build_finding(vuln, param, payload, evidence, url, "POST", signal)
                                )
                    except Exception as e:
                        print(f"[-] Tester timeout or error: {e}")

        self.stats["findings_count"] += len(findings)
        return url, findings

    def test(self, targets):
        results = {}

        for url, params in targets.items():
            _, findings = self.test_one(url, params)
            if findings:
                results[url] = findings

        return results
