"""R6 Phase 10 — audit assertion #27 tests.

Targets `assert_sidecar_housekeeping_fields_present` and its registration
in the VIDEO_ASSERTIONS tuple. Per Opus M3 fix: workspace frozen-contract
statuses only ({candidate, pinned, canonical, archived}) — "approved" and
"rejected" must FAIL.
"""

import pytest
from recoil.pipeline.tools.audit_assertions import (
    assert_sidecar_housekeeping_fields_present,
)


def _valid_sc():
    return {
        "schema_version": "1.0",
        "source": "pipeline",
        "status": "candidate",
        "created_at": "2026-05-22T01:00:00Z",
        "updated_at": "2026-05-22T01:00:00Z",
        "lineage": {},
        "notes": "",
        "tags": [],
    }


def test_assertion_27_accepts_valid_sidecar():
    payload = {"_audit_sidecar_dict": _valid_sc()}
    assert_sidecar_housekeeping_fields_present(payload, "video_i2v", "seeddance-2.0", None)


def test_assertion_27_accepts_integer_schema_version():
    sc = _valid_sc()
    sc["schema_version"] = 1
    payload = {"_audit_sidecar_dict": sc}
    assert_sidecar_housekeeping_fields_present(payload, "video_i2v", "seeddance-2.0", None)


def test_assertion_27_soft_noop_without_sidecar():
    # No _audit_sidecar_dict — soft no-op (not raised).
    assert_sidecar_housekeeping_fields_present({}, "video_i2v", "seeddance-2.0", None)


def test_assertion_27_rejects_unknown_source():
    sc = _valid_sc()
    sc["source"] = "bogus"
    with pytest.raises(AssertionError, match="source"):
        assert_sidecar_housekeeping_fields_present(
            {"_audit_sidecar_dict": sc}, "video_i2v", "seeddance-2.0", None
        )


def test_assertion_27_rejects_unknown_status():
    sc = _valid_sc()
    sc["status"] = "weird"
    with pytest.raises(AssertionError, match="status"):
        assert_sidecar_housekeeping_fields_present(
            {"_audit_sidecar_dict": sc}, "video_i2v", "seeddance-2.0", None
        )


def test_assertion_27_rejects_approved_status_per_opus_m3():
    """Opus M3 fix: workspace SIDECAR_VALID_STATUSES frozen contract does NOT
    include 'approved'. The audit must reject it to stay in sync."""
    sc = _valid_sc()
    sc["status"] = "approved"
    with pytest.raises(AssertionError, match="status"):
        assert_sidecar_housekeeping_fields_present(
            {"_audit_sidecar_dict": sc}, "video_i2v", "seeddance-2.0", None
        )


def test_assertion_27_rejects_rejected_status_per_opus_m3():
    """Opus M3 fix: 'rejected' is NOT in the workspace frozen contract."""
    sc = _valid_sc()
    sc["status"] = "rejected"
    with pytest.raises(AssertionError, match="status"):
        assert_sidecar_housekeeping_fields_present(
            {"_audit_sidecar_dict": sc}, "video_i2v", "seeddance-2.0", None
        )


def test_assertion_27_rejects_wrong_lineage_type():
    sc = _valid_sc()
    sc["lineage"] = "not a dict"
    with pytest.raises(AssertionError, match="lineage"):
        assert_sidecar_housekeeping_fields_present(
            {"_audit_sidecar_dict": sc}, "video_i2v", "seeddance-2.0", None
        )


def test_assertion_27_rejects_bad_schema_version():
    sc = _valid_sc()
    sc["schema_version"] = "2.0"
    with pytest.raises(AssertionError, match="schema_version"):
        assert_sidecar_housekeeping_fields_present(
            {"_audit_sidecar_dict": sc}, "video_i2v", "seeddance-2.0", None
        )


def test_assertion_27_rejects_non_str_created_at():
    sc = _valid_sc()
    sc["created_at"] = 1234567890
    with pytest.raises(AssertionError, match="created_at"):
        assert_sidecar_housekeeping_fields_present(
            {"_audit_sidecar_dict": sc}, "video_i2v", "seeddance-2.0", None
        )


def test_assertion_27_rejects_non_list_tags():
    sc = _valid_sc()
    sc["tags"] = "not a list"
    with pytest.raises(AssertionError, match="tags"):
        assert_sidecar_housekeeping_fields_present(
            {"_audit_sidecar_dict": sc}, "video_i2v", "seeddance-2.0", None
        )


def test_assertion_27_rejects_non_str_notes():
    sc = _valid_sc()
    sc["notes"] = ["not", "a", "string"]
    with pytest.raises(AssertionError, match="notes"):
        assert_sidecar_housekeeping_fields_present(
            {"_audit_sidecar_dict": sc}, "video_i2v", "seeddance-2.0", None
        )


def test_assertion_27_in_video_assertions_tuple():
    from recoil.pipeline.tools.audit_assertions import VIDEO_ASSERTIONS
    names = [a.__name__ for a in VIDEO_ASSERTIONS]
    assert "assert_sidecar_housekeeping_fields_present" in names
