"""Cross-spec DAG structural compatibility.

The refimpls keep separate stores (ACTStore for ACT records, ECTStore
for ECT payloads). These tests verify that the DAG topology expressed
via `pred` arrays has the same interpretation in both — a 3-node
graph looks the same whether it is made of ACT records or ECT payloads.
"""

from __future__ import annotations

import uuid

import pytest

from ect.dag import ECTStore


class TestPredArraySemantics:
    def test_identical_pred_values_in_both_refimpls(
        self, act_record_builder, ect_payload_builder
    ):
        """Same pred array produces same on-wire interpretation."""
        parent1 = str(uuid.uuid4())
        parent2 = str(uuid.uuid4())

        _, act_rec = act_record_builder(pred=[parent1, parent2])
        _, ect_pl = ect_payload_builder(pred=[parent1, parent2])

        assert act_rec.pred == ect_pl.pred == [parent1, parent2]

    def test_empty_pred_is_root_node_on_both(
        self, act_record_builder, ect_payload_builder
    ):
        _, act_rec = act_record_builder(pred=[])
        _, ect_pl = ect_payload_builder(pred=[])
        assert act_rec.pred == [] == ect_pl.pred


class TestThreeNodeDAG:
    """A diamond: root -> {child1, child2} -> join.

    Built twice: once in ACT's ledger and once in ECT's store. The
    shape must be recognised identically by each refimpl's DAG
    machinery.
    """

    def test_diamond_topology_consistent(self, act_record_builder, ect_payload_builder):
        # Shared jti identifiers so the topologies are isomorphic.
        root_jti = str(uuid.uuid4())
        c1_jti = str(uuid.uuid4())
        c2_jti = str(uuid.uuid4())
        join_jti = str(uuid.uuid4())

        # --- Build ACT records ---
        _, root_act = act_record_builder(jti=root_jti, pred=[])
        _, c1_act = act_record_builder(jti=c1_jti, pred=[root_jti])
        _, c2_act = act_record_builder(jti=c2_jti, pred=[root_jti])
        _, join_act = act_record_builder(jti=join_jti, pred=[c1_jti, c2_jti])

        # --- Build ECT payloads ---
        _, root_ect = ect_payload_builder(jti=root_jti, pred=[])
        _, c1_ect = ect_payload_builder(jti=c1_jti, pred=[root_jti])
        _, c2_ect = ect_payload_builder(jti=c2_jti, pred=[root_jti])
        _, join_ect = ect_payload_builder(jti=join_jti, pred=[c1_jti, c2_jti])

        # Topological equivalence check: same predecessor sets by jti.
        act_pred_map = {
            r.jti: set(r.pred) for r in (root_act, c1_act, c2_act, join_act)
        }
        ect_pred_map = {
            p.jti: set(p.pred) for p in (root_ect, c1_ect, c2_ect, join_ect)
        }
        assert act_pred_map == ect_pred_map


class TestStoresAreSeparate:
    """Document that refimpls do not cross-resolve predecessor jtis."""

    def test_ect_store_is_abstract_and_payload_typed(self):
        """ECTStore is an ABC whose API only accepts ECT Payload objects.

        No method on ECTStore accepts ACTRecord — cross-type
        predecessor resolution would have to be implemented outside
        both refimpls. Documented here so no one copy-pastes a
        bridging store into either codebase.
        """
        # ECTStore is abstract, so we can only inspect its interface.
        from ect.dag import ECTStore as _ECTStore

        abstract_methods = getattr(_ECTStore, "__abstractmethods__", frozenset())
        assert "get_by_tid" in abstract_methods
        assert "contains" in abstract_methods
        # No cross-type API surface.
        assert not hasattr(_ECTStore, "add_act_record")
