from __future__ import annotations

import json
import shutil
import subprocess
import sys
from importlib.util import module_from_spec, spec_from_file_location
from pathlib import Path

import pytest

REPO_ROOT = Path(__file__).resolve().parents[1]

CONFORMANCE_SUITE_FILES = (
    "tests/test_conformance_legal.py",
    "tests/test_conformance_construction.py",
    "tests/test_conformance_schema.py",
    "tests/test_conformance_supply_chain.py",
    "tests/test_conformance_iam.py",
    "tests/test_conformance_lab.py",
    "tests/test_conformance_robotics.py",
    "tests/test_conformance_agent_guardrails.py",
    "tests/test_conformance_event_pipeline.py",
    "tests/test_conformance_dnd.py",
)

EXAMPLE_SUMMARY_FILES = (
    "legal_case_summary.json",
    "construction_summary.json",
    "schema_contract_summary.json",
    "supply_chain_summary.json",
    "iam_summary.json",
    "lab_sample_summary.json",
    "robotics_summary.json",
    "agent_guardrails_summary.json",
    "event_pipeline_summary.json",
    "dnd_rules_cutover_summary.json",
    "dnd_trustless_dice_proposal_summary.json",
    "dnd_combat_lite_summary.json",
    "dnd_character_sheet_lite_summary.json",
)

IDENTIFIER_KEYS = (
    "case_id",
    "component_id",
    "record_id",
    "batch_id",
    "request_id",
    "sample_id",
    "robot_id",
    "event_id",
    "encounter_id",
    "character_id",
)


def _suite_paths() -> tuple[Path, ...]:
    return tuple(REPO_ROOT / relative_path for relative_path in CONFORMANCE_SUITE_FILES)


def test_conformance_suite_inventory_is_stable() -> None:
    suite_paths = _suite_paths()
    missing = [str(path.relative_to(REPO_ROOT)) for path in suite_paths if not path.exists()]
    assert not missing, f"Missing conformance suite file(s): {', '.join(missing)}"


def test_conformance_suites_import_and_define_tests() -> None:
    for relative_path in CONFORMANCE_SUITE_FILES:
        path = REPO_ROOT / relative_path
        spec = spec_from_file_location(path.stem, path)
        assert spec is not None and spec.loader is not None, f"Unable to load {relative_path}"
        module = module_from_spec(spec)
        spec.loader.exec_module(module)
        test_symbols = [name for name in vars(module) if name.startswith("test_")]
        assert test_symbols, f"{relative_path} does not expose any test_* symbols."


def test_conformance_umbrella_executes_suite_group() -> None:
    basetemp_root = REPO_ROOT / ".tmp_conformance_runner"
    shutil.rmtree(basetemp_root, ignore_errors=True)
    basetemp_root.parent.mkdir(parents=True, exist_ok=True)

    command = [
        sys.executable,
        "-m",
        "pytest",
        "-q",
        *CONFORMANCE_SUITE_FILES,
        "--basetemp",
        str(basetemp_root),
    ]
    try:
        completed = subprocess.run(
            command,
            cwd=REPO_ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        if completed.returncode != 0:
            details = "\n".join(
                [
                    "Conformance umbrella run failed.",
                    f"command: {' '.join(command)}",
                    f"stdout:\n{completed.stdout}",
                    f"stderr:\n{completed.stderr}",
                ]
            )
            pytest.fail(details)
    finally:
        shutil.rmtree(basetemp_root, ignore_errors=True)


def test_example_output_shape_checks_when_artifacts_exist() -> None:
    out_dir = REPO_ROOT / "examples" / "out"
    if not out_dir.exists():
        pytest.skip("examples/out does not exist; skipping optional shape checks.")

    present_artifacts = [
        filename for filename in EXAMPLE_SUMMARY_FILES if (out_dir / filename).exists()
    ]
    if not present_artifacts:
        pytest.skip("No summary artifacts present; skipping optional shape checks.")

    for filename in present_artifacts:
        path = out_dir / filename
        payload = json.loads(path.read_text(encoding="utf-8"))
        assert isinstance(payload, dict), f"{filename} must contain a JSON object."
        assert payload, f"{filename} must not be an empty JSON object."
        assert any(key in payload for key in IDENTIFIER_KEYS), (
            f"{filename} should contain at least one domain identifier key."
        )
