"""M0 contract tests — the schemas, the validator, and the version-aware reader."""

from __future__ import annotations

import json
from importlib.resources import files
from pathlib import Path

import pytest

from mgf.fedres import SyncState, read_file, validate_payload
from mgf.fedres._schemas import DOC_KINDS, identify
from mgf.fedres.states import SYNC_STATES, TEST_STATUS_VALUES
from mgf.fedres.validate import validate_file

_PKG = Path(str(files("mgf.fedres")))
EXAMPLES_DIR = _PKG / "examples"
SCHEMAS_DIR = _PKG / "schemas"


def _example_files() -> list[Path]:
    return sorted(p for p in EXAMPLES_DIR.iterdir() if p.suffix in (".json", ".ndjson"))


def _status_example() -> dict:
    return json.loads((EXAMPLES_DIR / "status.json").read_text(encoding="utf-8"))


# --- the validator passes every shipped example (the contract self-check) ---


@pytest.mark.contract
@pytest.mark.parametrize("path", _example_files(), ids=lambda p: p.name)
def test_example_validates(path: Path) -> None:
    assert validate_file(path) == []


def test_every_doc_kind_has_an_example() -> None:
    example_kinds = {
        json.loads(p.read_text(encoding="utf-8"))["kind"]
        for p in _example_files()
        if p.suffix == ".json"
    }
    assert example_kinds >= DOC_KINDS, f"kinds without an example: {DOC_KINDS - example_kinds}"


def test_every_doc_kind_has_a_schema() -> None:
    present = {p.name.removesuffix(".schema.json") for p in SCHEMAS_DIR.iterdir()}
    assert present >= DOC_KINDS, f"kinds without a schema: {DOC_KINDS - present}"


# --- fail-closed: nothing dubious passes ---


def test_missing_required_fails() -> None:
    assert validate_payload({"schema_version": "0.1", "kind": "status", "project": "x"})


def test_unknown_kind_fails() -> None:
    assert validate_payload({"schema_version": "0.1", "kind": "not_a_kind"})


def test_unknown_schema_version_fails() -> None:
    errs = validate_payload({"schema_version": "9.9", "kind": "status"})
    assert errs and "unsupported schema_version" in errs[0]


def test_unknown_wire_version_fails() -> None:
    event = {"v": "9.9", "session": "s", "seq": 1, "ts": "2026-01-01T00:00:00Z", "kind": "log"}
    errs = validate_payload(event)
    assert errs and "unsupported wire version" in errs[0]


def test_additional_property_fails() -> None:
    payload = _status_example()
    payload["surprise"] = 1
    assert validate_payload(payload)


def test_bad_enum_fails() -> None:
    payload = _status_example()
    payload["tasks"][0]["state"] = "frobnicated"
    assert validate_payload(payload)


def test_blocked_task_requires_reason() -> None:
    payload = _status_example()
    blocked = next(t for t in payload["tasks"] if t["state"] == "blocked")
    del blocked["reason"]
    assert validate_payload(payload), "a blocked task without a reason must fail"


def test_unidentifiable_payload_fails() -> None:
    assert validate_payload({"hello": "world"})
    assert validate_payload([1, 2, 3])


# --- live-event discrimination (session vs run_id) ---


def test_live_event_discrimination() -> None:
    fed = {"v": "0.1", "session": "s", "seq": 1, "ts": "2026-01-01T00:00:00Z", "kind": "log"}
    test = {"v": "0.1", "run_id": "r", "seq": 1, "ts": "2026-01-01T00:00:00Z", "kind": "log"}
    assert identify(fed).schema_id and "live_event_fed" in identify(fed).schema_id
    assert identify(test).schema_id and "live_event_test" in identify(test).schema_id
    ambiguous = {"v": "0.1", "seq": 1, "ts": "2026-01-01T00:00:00Z", "kind": "log"}
    assert not identify(ambiguous).ok


# --- the version-aware reader: honest-absence, never a guess ---


def test_reader_reads_valid_example() -> None:
    result = read_file(EXAMPLES_DIR / "manifest.json")
    assert result.ok and result.kind == "manifest" and result.state is None


def test_reader_missing_file_is_unknown() -> None:
    result = read_file(EXAMPLES_DIR / "nope.json")
    assert not result.ok and result.state is SyncState.UNKNOWN


def test_reader_corrupt_json_is_unreadable(tmp_path: Path) -> None:
    bad = tmp_path / "x.json"
    bad.write_text("{ not json", encoding="utf-8")
    result = read_file(bad)
    assert not result.ok and result.state is SyncState.UNREADABLE


def test_reader_unknown_version_is_unreadable(tmp_path: Path) -> None:
    bad = tmp_path / "x.json"
    bad.write_text(json.dumps({"schema_version": "9.9", "kind": "status"}), encoding="utf-8")
    result = read_file(bad)
    assert not result.ok and result.state is SyncState.UNREADABLE


def test_reader_schema_violation_is_unreadable(tmp_path: Path) -> None:
    bad = tmp_path / "x.json"
    bad.write_text(json.dumps({"schema_version": "0.1", "kind": "status"}), encoding="utf-8")
    result = read_file(bad)
    assert not result.ok and result.state is SyncState.UNREADABLE and result.errors


# --- the Python enums stay in lock-step with the wire schema ---


def test_sync_states_match_schema() -> None:
    common = json.loads((SCHEMAS_DIR / "_common.schema.json").read_text(encoding="utf-8"))
    assert list(SYNC_STATES) == common["$defs"]["sync_state"]["enum"]
    assert list(TEST_STATUS_VALUES) == common["$defs"]["test_status_value"]["enum"]


# --- PAPER-04: the supervisor's full outcome vocabulary is native ---


@pytest.mark.parametrize("outcome", ["crashed", "hung", "quarantined"])
def test_supervisor_outcomes_validate_natively(outcome: str) -> None:
    record = json.loads((EXAMPLES_DIR / "test_run_record.json").read_text(encoding="utf-8"))
    record["result"] = outcome
    assert validate_payload(record) == []


def test_unknown_outcome_still_fails_closed() -> None:
    record = json.loads((EXAMPLES_DIR / "test_run_record.json").read_text(encoding="utf-8"))
    record["result"] = "exploded"
    assert validate_payload(record)
