"""Suite 4 — Edge-case body / value tests."""

from __future__ import annotations
import asyncio
from typing import Any

import httpx

from .http_client import send_request
from .payloads import EDGE_CASE_BODIES, EDGE_CASE_BODIES_QUICK


async def run_edge_case_tests(
    client: httpx.AsyncClient,
    url: str,
    method: str,
    headers: dict[str, str] | None,
    body: Any | None,
    mode: str = "full",
) -> list:
    from .runner import TestResult

    results: list[TestResult] = []
    quick = mode == "quick"
    cases = EDGE_CASE_BODIES_QUICK if quick else EDGE_CASE_BODIES

    sem = asyncio.Semaphore(10)

    async def _test_one(idx: int, payload: Any):
        label = f"body_{idx}"
        async with sem:
            r = await send_request(client, url=url, method=method, headers=headers, body=payload)
            code = r["status_code"]
            lat = r["latency_ms"]
            if code is None:
                return TestResult(
                    test=f"Edge: {label}", status="WARN",
                    detail=f"No response: {r['error'][:60]}" if r["error"] else "No response",
                    latency_ms=lat, category="edge_cases",
                    severity="MEDIUM", cwe="CWE-20",
                )
            if code >= 500:
                return TestResult(
                    test=f"Edge: {label}", status="CRASH",
                    detail=f"Server crashed ({code})",
                    latency_ms=lat, category="edge_cases",
                    severity="HIGH", cwe="CWE-20",
                )
            if code == 200:
                return TestResult(
                    test=f"Edge: {label}", status="WARN",
                    detail="Accepted edge-case body without error",
                    latency_ms=lat, category="edge_cases",
                    severity="MEDIUM", cwe="CWE-20",
                )
            return TestResult(
                test=f"Edge: {label}", status="PASS",
                detail=f"Handled with {code}",
                latency_ms=lat, category="edge_cases",
                severity="INFO", cwe="CWE-20",
            )

    coros = [_test_one(i, payload) for i, payload in enumerate(cases)]
    results.extend(await asyncio.gather(*coros))

    # Extra: empty body on POST/PUT/PATCH
    if method.upper() in ("POST", "PUT", "PATCH"):
        r = await send_request(client, url=url, method=method, headers=headers, body=None)
        code = r["status_code"]
        if code and code >= 500:
            results.append(TestResult(
                test="Edge: empty body", status="CRASH",
                detail=f"Server crashed on empty body ({code})",
                latency_ms=r["latency_ms"], category="edge_cases",
                severity="HIGH", cwe="CWE-20",
            ))
        else:
            results.append(TestResult(
                test="Edge: empty body", status="PASS",
                detail=f"Handled empty body ({code})",
                latency_ms=r["latency_ms"], category="edge_cases",
                severity="INFO", cwe="CWE-20",
            ))

    # Extra: massive URL (4KB+ path)
    long_url = url + "/" + "A" * 4096
    r = await send_request(client, url=long_url, method="GET", headers=headers, body=None)
    code = r["status_code"]
    if code and code >= 500:
        results.append(TestResult(
            test="Edge: 4KB URL path", status="CRASH",
            detail=f"Server crashed on oversized URL ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="HIGH", cwe="CWE-20",
        ))
    else:
        results.append(TestResult(
            test="Edge: 4KB URL path", status="PASS",
            detail=f"Handled long URL ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="INFO", cwe="CWE-20",
        ))

    if quick:
        return results

    # Extra: 16KB URL path (nginx default limit)
    very_long_url = url + "/" + "B" * 16384
    r = await send_request(client, url=very_long_url, method="GET", headers=headers, body=None)
    code = r["status_code"]
    if code and code >= 500:
        results.append(TestResult(
            test="Edge: 16KB URL path", status="CRASH",
            detail=f"Server crashed on mega URL ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="HIGH", cwe="CWE-400",
        ))
    else:
        results.append(TestResult(
            test="Edge: 16KB URL path", status="PASS",
            detail=f"Handled ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="INFO", cwe="CWE-400",
        ))

    # Extra: Oversized header value (8KB+)
    big_header = dict(headers or {})
    big_header["X-Custom-Data"] = "X" * 8192
    r = await send_request(client, url=url, method=method, headers=big_header, body=body)
    code = r["status_code"]
    if code and code >= 500:
        results.append(TestResult(
            test="Edge: 8KB header value", status="CRASH",
            detail=f"Server crashed on oversized header ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="HIGH", cwe="CWE-400",
        ))
    else:
        results.append(TestResult(
            test="Edge: 8KB header value", status="PASS",
            detail=f"Handled oversized header ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="INFO", cwe="CWE-400",
        ))

    # Extra: Many headers (50+)
    many_headers = dict(headers or {})
    for i in range(50):
        many_headers[f"X-Test-Header-{i}"] = f"value-{i}"
    r = await send_request(client, url=url, method=method, headers=many_headers, body=body)
    code = r["status_code"]
    if code and code >= 500:
        results.append(TestResult(
            test="Edge: 50 custom headers", status="CRASH",
            detail=f"Server crashed on header flood ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="HIGH", cwe="CWE-400",
        ))
    else:
        results.append(TestResult(
            test="Edge: 50 custom headers", status="PASS",
            detail=f"Handled header flood ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="INFO", cwe="CWE-400",
        ))

    # Extra: Binary-like body
    r = await send_request(
        client, url=url, method=method, headers=headers,
        raw_body="\x00\x01\x02\xff\xfe\xfd" * 500,
    )
    code = r["status_code"]
    if code and code >= 500:
        results.append(TestResult(
            test="Edge: binary body", status="CRASH",
            detail=f"Server crashed on binary payload ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="HIGH", cwe="CWE-20",
        ))
    else:
        results.append(TestResult(
            test="Edge: binary body", status="PASS",
            detail=f"Handled binary payload ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="INFO", cwe="CWE-20",
        ))

    # Extra: Rapid content-type switching
    types = ["application/json", "text/plain", "application/xml", "multipart/form-data"]
    type_errors = 0
    for ct in types:
        h = dict(headers or {})
        h["Content-Type"] = ct
        r = await send_request(client, url=url, method=method, headers=h, body=body)
        if r["status_code"] and r["status_code"] >= 500:
            type_errors += 1
    if type_errors > 0:
        results.append(TestResult(
            test="Edge: content-type switching", status="WARN",
            detail=f"{type_errors}/{len(types)} content types caused 5xx",
            latency_ms=0, category="edge_cases",
            severity="MEDIUM", cwe="CWE-20",
        ))
    else:
        results.append(TestResult(
            test="Edge: content-type switching", status="PASS",
            detail="Handled all content types gracefully",
            latency_ms=0, category="edge_cases",
            severity="INFO", cwe="CWE-20",
        ))

    # Extra: Send request with no User-Agent
    no_ua = dict(headers or {})
    no_ua.pop("User-Agent", None)
    no_ua.pop("user-agent", None)
    r = await send_request(client, url=url, method=method, headers=no_ua, body=body)
    code = r["status_code"]
    if code and code >= 500:
        results.append(TestResult(
            test="Edge: no User-Agent", status="CRASH",
            detail=f"Server crashed without User-Agent ({code})",
            latency_ms=r["latency_ms"], category="edge_cases",
            severity="MEDIUM", cwe="CWE-20",
        ))

    return results
