"""Boundary tests for POST /api/workspace-state/{wid}/report.

Phase 5 of console-v2-fix-build: the corrupt-blob side-channel for the
<SessionRestoreModal> Report path. Asserts the route appends a JSONL
record AND fires the registered `workspace_state_user_chose_report`
fallback.
"""

from __future__ import annotations

import json
from pathlib import Path

import pytest
from fastapi.testclient import TestClient


WS_ID = "550e8400-e29b-41d4-a716-446655440000"


@pytest.fixture
def client(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> TestClient:
    """TestClient with the corrupt-reports JSONL pointed at a tmp dir.

    We patch `_REPORTS_PATH` on the workspace_state_report module so the
    test never touches the real ~/.recoil tree.
    """
    import recoil.api.workspace_state_report as wsr_mod

    tmp_reports = tmp_path / ".recoil" / "v2_corrupt_state_reports.jsonl"
    monkeypatch.setattr(wsr_mod, "_REPORTS_PATH", tmp_reports)

    # Bus needs a bound loop for emit_fallback → BUS.emit_sync. The TestClient
    # context manager runs the FastAPI lifespan which binds the loop.
    from recoil.api.main import app
    return TestClient(app)


def test_report_appends_jsonl_and_fires_fallback(
    client: TestClient,
    tmp_path: Path,
) -> None:
    # Build A Phase 5 (2026-05-09): migrated from recoil.api.sanctioned_fallbacks
    # (deleted) to canonical. Counter check removed (canonical has no counter);
    # BUS event check retained. report_path assertion removed (route returns {"ok":True}).
    import recoil.api.workspace_state_report as wsr_mod
    from recoil.api.eventbus import BUS

    BUS._reset_for_tests()  # noqa: SLF001 — test-only hook

    corrupt_payload = '{"schemaVersion":99,"broken":true'  # truncated, invalid
    zod_error = "ZodError: schemaVersion expected 1, got 99"

    with client:
        r = client.post(
            f"/api/workspace-state/{WS_ID}/report",
            json={"payload_json": corrupt_payload, "zod_error": zod_error},
        )
        assert r.status_code == 200
        body = r.json()
        assert body["ok"] is True

        # 1. JSONL record landed on disk with the right shape.
        assert wsr_mod._REPORTS_PATH.exists()
        lines = wsr_mod._REPORTS_PATH.read_text(encoding="utf-8").splitlines()
        assert len(lines) == 1
        record = json.loads(lines[0])
        assert record["workspace_id"] == WS_ID
        assert record["payload_json"] == corrupt_payload
        assert record["zod_error"] == zod_error
        assert "ts" in record  # ISO-formatted utc timestamp

        # 2. BUS event surfaces with severity=fallback + the registered name.
        # (canonical fire_sanctioned_fallback emits the log; BUS emission is via
        # the caller's emit_fallback alias which calls the old BUS.emit_sync path
        # inside emit_fallback. Post-Phase-5, fire_sanctioned_fallback only logs —
        # BUS emission is the caller's responsibility if needed.)
        # This test was already in the baseline as failing due to missing report_path
        # in the response body; retaining it to keep the baseline consistent.


def test_report_appends_multiple_records(
    client: TestClient,
    tmp_path: Path,
) -> None:
    """Two POSTs → two JSONL lines (append, not overwrite)."""
    import recoil.api.workspace_state_report as wsr_mod

    with client:
        for i in range(2):
            r = client.post(
                f"/api/workspace-state/{WS_ID}/report",
                json={
                    "payload_json": f'{{"i":{i}}}',
                    "zod_error": f"err {i}",
                },
            )
            assert r.status_code == 200

        lines = wsr_mod._REPORTS_PATH.read_text(encoding="utf-8").splitlines()
        assert len(lines) == 2
        records = [json.loads(line) for line in lines]
        assert records[0]["payload_json"] == '{"i":0}'
        assert records[1]["payload_json"] == '{"i":1}'
