"""Contracts for the safe failure-artifact collector used by E2E CI."""

from __future__ import annotations

import importlib.util
import json
import sys
import xml.etree.ElementTree as ET
from pathlib import Path

ROOT = Path(__file__).resolve().parents[3]
SPEC = importlib.util.spec_from_file_location(
    "collect_diagnostics", ROOT / "ci" / "integration" / "collect_diagnostics.py"
)
assert SPEC is not None and SPEC.loader is not None
collector = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = collector
SPEC.loader.exec_module(collector)


def test_collector_allowlists_and_redacts_e2e_failure_artifacts(tmp_path: Path) -> None:
    junit = tmp_path / "results.xml"
    junit.write_text(
        "<testsuite name='e2e'><testcase name='fails'><failure>secret=hidden raw IQ</failure>"
        "<system-out>token=hidden</system-out></testcase></testsuite>",
        encoding="utf-8",
    )
    workspace = tmp_path / "workspaces" / "journey"
    workspace.mkdir(parents=True)
    (workspace / "config.yaml").write_text(
        "gemini_api_key: hidden\nscene: safe\niq: [1, 2]\nprovider_response: |\n  hidden response\n",
        encoding="utf-8",
    )
    raw_diagnostics = tmp_path / "raw-diagnostics"
    raw_diagnostics.mkdir()
    (raw_diagnostics / "command-failure-123.json").write_text(
        json.dumps(
            {
                "command": ["rfgen", "annotate", "--api-key", "hidden"],
                "cwd": "/tmp/journey",
                "returncode": 1,
                "stdout": "raw IQ should not be copied",
                "stderr": "BackendUnavailableError: token=hidden",
            }
        ),
        encoding="utf-8",
    )
    (workspace / "inspect-report.json").write_text(
        json.dumps(
            {"records": 2, "iq": [1, 2], "unknown_wave_data": [3, 4], "token": "hidden"}
        ),
        encoding="utf-8",
    )
    (workspace / "raw-iq.zarr").write_text("not allowlisted", encoding="utf-8")

    output = tmp_path / "safe"
    collector.collect(
        junit=junit,
        workspaces=workspace.parent,
        raw_diagnostics=raw_diagnostics,
        output=output,
    )

    files = {path.name: path.read_text(encoding="utf-8") for path in output.iterdir()}
    rendered = "\n".join(files.values())
    assert "hidden" not in rendered
    assert "1, 2" not in rendered
    assert "raw IQ" not in rendered
    assert "system-out" not in rendered
    assert "raw-iq.zarr" not in files
    assert "BackendUnavailableError" in rendered
    assert "<redacted>" in rendered
    assert "records" in rendered
    inspection = json.loads(next(value for name, value in files.items() if name.startswith("summary-")))
    assert inspection["iq"] == "<redacted>"
    assert inspection["unknown_wave_data"] == {"omitted_sequence_length": 2}


def test_collector_structurally_redacts_command_cwd_and_junit_identities(tmp_path: Path) -> None:
    """Failure artifacts retain only diagnostic structure, never adversarial text."""
    sentinel = "DO-NOT-UPLOAD-raw-iq-token-987"
    junit = tmp_path / "results.xml"
    junit.write_text(
        "<testsuite name='" + sentinel + "' time='not-a-number'>"
        "<testcase classname='" + sentinel + "' name='" + sentinel + "' time='1.25'>"
        "<failure>" + sentinel + "</failure></testcase></testsuite>",
        encoding="utf-8",
    )
    raw_diagnostics = tmp_path / "raw-diagnostics"
    raw_diagnostics.mkdir()
    (raw_diagnostics / "command-failure-adversarial.json").write_text(
        json.dumps(
            {
                "command": [
                    f"/tmp/{sentinel}/python",
                    "-c",
                    sentinel,
                    f"--api-key={sentinel}",
                    "generate",
                    sentinel,
                ],
                "cwd": f"/tmp/{sentinel}",
                "returncode": 2,
                "stderr": f"StorageError: {sentinel}",
            }
        ),
        encoding="utf-8",
    )

    output = tmp_path / "safe"
    collector.collect(
        junit=junit,
        workspaces=tmp_path / "no-workspaces",
        raw_diagnostics=raw_diagnostics,
        output=output,
    )

    rendered = "\n".join(path.read_text(encoding="utf-8") for path in output.iterdir())
    assert sentinel not in rendered
    summary = json.loads(next(path.read_text(encoding="utf-8") for path in output.glob("failure-*.json")))
    assert summary == {
        "command": ["python", "-c", "<argument>", "--api-key=<value>", "generate", "<argument>"],
        "cwd": "<workspace>",
        "error_category": "StorageError",
        "returncode": 2,
    }
    junit_root = ET.parse(output / "junit.xml").getroot()
    testcase = junit_root.find(".//testcase")
    assert testcase is not None
    assert testcase.attrib == {"classname": "e2e", "name": "test", "time": "1.25"}
    assert junit_root.find(".//testsuite").attrib == {"name": "e2e"}


def test_ci_wires_raw_failure_diagnostics_to_a_distinct_collector_input() -> None:
    workflow = (ROOT / ".github" / "workflows" / "implementation.yml").read_text(
        encoding="utf-8"
    )
    assert 'RFGEN_E2E_DIAGNOSTICS_DIR="$RUNNER_TEMP/rfgen-e2e-raw-diagnostics"' in workflow
    assert '--raw-diagnostics "$RUNNER_TEMP/rfgen-e2e-raw-diagnostics"' in workflow
    assert '--output "$RUNNER_TEMP/rfgen-e2e-diagnostics"' in workflow
