"""REC-235 Phase 2 — composition-manifest model + pure round-trip.

Behavioral tests for ``recoil/api/schemas/composition.py::CompositionManifest``: the
board's ordered-pointer composition PRIMITIVE (atom-version URN members + layout + kind).
Pure data + validation + JSON round-trip via the ``_Versioned`` idiom
(``model_validate(m.model_dump())``) — NO rendering, NO generation. A reorder is a new
manifest with ``members`` permuted (same atoms, new order). Layout-slot validation is the
renderer's job (Phase 3), so the schema accepts any dict ``layout`` here.
"""
import sys
import pathlib

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent.parent.parent))
from recoil.core.paths import ensure_pipeline_importable  # noqa: E402

ensure_pipeline_importable()

import pytest  # noqa: E402
from pydantic import ValidationError  # noqa: E402

from recoil.api.schemas.composition import CompositionManifest  # noqa: E402


_URN0 = "atom:EP001/beat/EP001_SH02@t0"
_URN1 = "atom:EP001/beat/EP001_SH03@t1"


def test_round_trip_preserves_members_order():
    m = CompositionManifest(kind="CONT", members=[_URN0, _URN1], layout={"slots": 4})
    # The existing _Versioned round-trip idiom — NOT from_dict/to_dict.
    reloaded = CompositionManifest.model_validate(m.model_dump())
    assert reloaded == m
    assert reloaded.kind == "CONT"
    assert reloaded.layout == {"slots": 4}
    # Order is editorial state — it must survive the round-trip exactly.
    assert reloaded.members == [_URN0, _URN1]


def test_invalid_member_urn_raises():
    with pytest.raises(ValidationError):
        CompositionManifest(kind="CONT", members=["not-an-atom"], layout={"slots": 4})


def test_invalid_kind_raises():
    with pytest.raises(ValidationError):
        CompositionManifest(kind="FOO", members=[_URN0], layout={"slots": 4})


def test_reorder_is_same_atoms_new_order():
    m = CompositionManifest(kind="CONT", members=[_URN0, _URN1], layout={"slots": 4})
    # Reorder = a NEW manifest with members permuted (reversed here).
    reordered = CompositionManifest(
        kind=m.kind, members=list(reversed(m.members)), layout=m.layout
    )
    assert reordered.members == [_URN1, _URN0]
    # Same atoms — none added or removed; only the order changed.
    assert set(reordered.members) == set(m.members)
    assert len(reordered.members) == len(m.members)
    assert reordered.members != m.members
