"""Live integration test against ``httpbin.org``.

This is the v0.1.0 exit-criterion test from HANDOFF.md §7 Step 4:

    "a hand-written suite against httpbin runs live, exits non-zero
    on planted findings, coverage ≥ 80%."

Marked ``@pytest.mark.live`` so CI without network access can
deselect via ``pytest -m 'not live'``. The pytest configuration in
``pyproject.toml`` declares the marker.

Run locally with::

    pytest tests/integration/test_httpbin_live.py -m live

Or skip via::

    pytest -m 'not live'

The unit-test suite (``tests/unit/``) covers every code path with
respx-mocked httpx; this file exists to prove the wiring against a
real network endpoint.
"""

from __future__ import annotations

import pytest

from mgf.apiprobe.cli._default_suite import build_default_suite
from mgf.apiprobe.core.probe import Probe
from mgf.apiprobe.core.suite import Suite
from mgf.apiprobe.transport.httpx_transport import HttpxTransport


@pytest.mark.live
@pytest.mark.asyncio
async def test_bundled_suite_runs_against_real_httpbin() -> None:
    """The bundled httpbin suite executes end-to-end without crashing.

    httpbin.org is publicly reachable; if the test environment has no
    egress, this test is the canary. We check the report structure
    rather than specific findings — httpbin's response shapes change
    over time and we want the test to survive minor drift.
    """
    suite = build_default_suite()
    async with HttpxTransport(settings=suite.settings) as transport:
        report = await suite.run(transport=transport)

    # Structure: 5 probes ran (per build_default_suite).
    assert report.probe_count == 5

    # The /status/200 probe should NOT fire a contract.status
    # finding when status really is 200.
    status_findings = [
        f
        for f in report.findings
        if f.code == "contract.status.unexpected" and f.probe_id == "status-200"
    ]
    assert status_findings == []

    # The /status/404 probe explicitly expects 404, so the
    # contract.status check should be silent there too.
    not_found_findings = [
        f
        for f in report.findings
        if f.code == "contract.status.unexpected" and f.probe_id == "status-404"
    ]
    assert not_found_findings == []


@pytest.mark.live
@pytest.mark.asyncio
async def test_planted_finding_is_detected() -> None:
    """A deliberately-misconfigured probe MUST produce a Finding.

    Confirms the suite-exit policy works against real responses:
    expecting status 999 from httpbin's /status/200 endpoint MUST
    fire ``contract.status.unexpected``.
    """
    from mgf.apiprobe.checks.contract import StatusUnexpected

    bad_probe = (
        Probe.get("https://httpbin.org/status/200")
        .id("planted-bad-status")
        .expect_status(418)  # I'm a teapot — valid HTTP status, but /status/200 returns 200
    )
    suite = Suite(probes=[bad_probe], checks=[StatusUnexpected()])
    async with HttpxTransport() as transport:
        report = await suite.run(transport=transport)

    findings = [f for f in report.findings if f.code == "contract.status.unexpected"]
    assert len(findings) == 1
    assert findings[0].probe_id == "planted-bad-status"
    assert findings[0].evidence["actual"] == 200
    assert findings[0].evidence["expected"] == [418]
