"""PC-08: report serialisation throughput.

``JsonReportWriter.write(report)`` is called once per suite run, but
for large suites the cost adds up. A 5 000-finding report (1 000
probes x 5 findings each, a realistic worst case for security-
verification suites against a many-endpoint surface) should
serialise in well under 100 ms — anything more and the harness has
a perceptible wall-clock tax on the report-emit step.

Pin: write(report) over a 5 000-finding synthetic report stays under
100 ms (~30x slack against a local measurement of ~3 ms).

Marker: ``perf``. Run via ``pytest -m perf``.
"""

from __future__ import annotations

import time
from datetime import UTC, datetime

import pytest

from mgf.apiprobe.core.finding import Finding, RootCause, Severity
from mgf.apiprobe.core.report import Report
from mgf.apiprobe.reporting.json_writer import JsonReportWriter

pytestmark = pytest.mark.perf

_FINDING_COUNT = 5_000
_WRITE_BUDGET_MS = 100.0


def _build_synthetic_report(n_findings: int) -> Report:
    """Build a Report with n synthetic findings.

    Findings cover a mix of severities, root causes, and evidence
    shapes so the writer exercises every code path (severity
    breakdown, root-cause breakdown, evidence serialisation).
    """
    severities = list(Severity)
    root_causes = list(RootCause)
    findings: list[Finding] = []
    for i in range(n_findings):
        findings.append(
            Finding(
                code=f"perf.synthetic.code-{i % 50}",
                severity=severities[i % len(severities)],
                root_cause=root_causes[i % len(root_causes)],
                message=(
                    f"Synthetic finding {i} for PC-08 serialisation "
                    f"throughput test — should not appear in real reports."
                ),
                evidence={
                    "expected": [200, 201],
                    "actual": 500 + (i % 4),
                    "url": f"https://example.invalid/api/v1/r-{i}",
                    "header_chunk": "X-Trace-Id: 0123456789abcdef" * 2,
                },
                remediation=(
                    "Synthetic remediation text designed to be roughly "
                    "the length of a real remediation string so the "
                    "serialisation cost is representative."
                ),
                probe_id=f"probe-{i % 1000}",
            )
        )

    now = datetime.now(UTC)
    return Report(
        findings=tuple(findings),
        started_at=now,
        finished_at=now,
        probe_count=1000,
        transport_errors=0,
    )


def test_write_5000_findings_under_budget() -> None:
    report = _build_synthetic_report(_FINDING_COUNT)
    writer = JsonReportWriter()

    # One warm-up run so import-time caches + type-resolution
    # overhead don't count.
    writer.write(report)

    start = time.perf_counter()
    payload = writer.write(report)
    elapsed_ms = (time.perf_counter() - start) * 1000.0

    # Sanity: the writer produced the expected shape.
    assert payload["probe_count"] == 1000
    assert len(payload["findings"]) == _FINDING_COUNT

    assert elapsed_ms < _WRITE_BUDGET_MS, (
        f"PC-08: JsonReportWriter.write took {elapsed_ms:.1f} ms for "
        f"{_FINDING_COUNT} findings (budget {_WRITE_BUDGET_MS:.0f} ms). "
        f"A regression in the per-finding render path or in the "
        f"breakdown aggregation would push this over. Investigate "
        f"src/mgf/apiprobe/reporting/json_writer.py."
    )
