"""R2 — provenance manifest acceptance checks.

Acceptance predicate (roadmap R2): a manifest is present + schema-valid after a
run, carries a non-empty seed, and >=1 persona fingerprint per participating
node. The fingerprint parity constant below is locked identically in
hh/src/persona.rs::tests::fingerprint_of_known_vector.
"""

import json

import pytest

from cmd_chat.sor.provenance import (
    Node,
    RunManifest,
    node_fingerprint,
    validate_manifest,
    write_manifest,
)

# Known 32-byte pubkey (bytes 0..32) -> base64 -> fingerprint. Same vector as
# the Rust persona test.
KNOWN_PUB_B64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="
KNOWN_FINGERPRINT = "630dcd29"


def _fixture_manifest(root):
    return RunManifest(
        run_id="20260719T000000Z",
        sor_seed=123456789,
        topology="1-house",
        selector="static",
        churn_schedule_id="fixture-churn-0",
        nodes=[
            Node(role="host", persona_pub_b64=KNOWN_PUB_B64, engine="docker"),
            Node(role="hop", persona_pub_b64="QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNk", engine="docker"),
        ],
        engine_kind="docker",
        engine_image_digest="sha256:deadbeef",
        worktree_root=root,
    )


def test_fingerprint_parity_with_rust():
    assert node_fingerprint(KNOWN_PUB_B64) == KNOWN_FINGERPRINT


def test_manifest_written_and_schema_valid(tmp_path):
    run_dir = tmp_path / "sor-runs" / "20260719T000000Z"
    doc = write_manifest(run_dir, _fixture_manifest(tmp_path))

    # present on disk
    path = run_dir / "manifest.json"
    assert path.is_file()
    on_disk = json.loads(path.read_text())
    assert on_disk == doc

    # schema-valid, non-empty seed, >=1 fingerprint per node (the R2 predicate)
    validate_manifest(on_disk)
    assert on_disk["sor_seed"] == 123456789
    assert len(on_disk["nodes"]) >= 1
    for n in on_disk["nodes"]:
        assert n["fingerprint"]
    assert on_disk["nodes"][0]["fingerprint"] == KNOWN_FINGERPRINT


def test_manifest_is_immutable(tmp_path):
    run_dir = tmp_path / "run"
    write_manifest(run_dir, _fixture_manifest(tmp_path))
    with pytest.raises(FileExistsError):
        write_manifest(run_dir, _fixture_manifest(tmp_path))


def test_containment_rejects_local_engine(tmp_path):
    m = _fixture_manifest(tmp_path)
    m.engine_kind = "local"
    with pytest.raises(ValueError):
        write_manifest(tmp_path / "run2", m)


def test_containment_rejects_local_node_engine(tmp_path):
    m = _fixture_manifest(tmp_path)
    m.nodes[1].engine = "local"
    with pytest.raises(ValueError):
        write_manifest(tmp_path / "run3", m)


def test_invalid_pubkey_rejected():
    with pytest.raises(ValueError):
        node_fingerprint("!!! not base64 !!!")


def test_validator_catches_empty_nodes():
    with pytest.raises(ValueError):
        validate_manifest(
            {
                "schema_version": "sor-manifest/1",
                "run_id": "x",
                "created_utc": "t",
                "sor_seed": 1,
                "topology": "1-house",
                "selector": "static",
                "churn_schedule_id": "c",
                "nodes": [],
                "engine": {"kind": "docker", "image_digest": None},
                "git": {},
                "deps": {},
                "timestamps": {"start_utc": "t", "stop_utc": None},
                "events": {"path": "events.jsonl", "sha256": None},
            }
        )
