import pytest
import networkx as nx
import cogiles


class TestRoundTrip:
    """parse(encode(parse(s))) should produce a graph isomorphic to parse(s)."""

    @pytest.mark.parametrize(
        "cogiles_str",
        [
            "R",
            "RGR",
            "RRRGGG",
            "R-1RRRRR-1",
            "R-1RRRR-1",
            "RR(BB)RR",
            "Y(G)(G)(G)",
            "RR.BB",
            "R-1-2GGG-2BBB-1",
            "R-1R(GY-1)BRRR(R)RB",
            "R.G.B",
            "BrGrPi",
            "Y-3(RR-3)(R)",
            "R(G(B(Y)))",
            "RRR(G)(YGG)RR",
            "OlOlBr",
            "KBlK",
            "PiPi(Ol)Gr",
        ],
    )
    def test_parse_encode_parse_consistency(self, cogiles_str):
        """Parsing, encoding, then parsing again gives equivalent graphs."""
        G1 = cogiles.parse(cogiles_str)
        encoded = cogiles.encode(G1)
        G2 = cogiles.parse(encoded)

        assert G1.number_of_nodes() == G2.number_of_nodes()
        assert G1.number_of_edges() == G2.number_of_edges()

        # Structural isomorphism with color matching
        assert nx.is_isomorphic(
            G1,
            G2,
            node_match=lambda n1, n2: n1["color"] == n2["color"],
        )

    @pytest.mark.parametrize(
        "cogiles_str",
        [
            "R",
            "RGR",
            "R-1RRRRR-1",
            "RR.BB",
            "Y(G)(G)(G)",
            "BrGrPi",
            "R.G.B",
        ],
    )
    def test_encode_is_idempotent(self, cogiles_str):
        """encode(parse(encode(parse(s)))) == encode(parse(s))."""
        G1 = cogiles.parse(cogiles_str)
        s1 = cogiles.encode(G1)
        G2 = cogiles.parse(s1)
        s2 = cogiles.encode(G2)
        assert s1 == s2

    def test_bl_alias_canonicalizes_to_k(self):
        """Bl should be parsed but encode should produce K."""
        G = cogiles.parse("BlBl")
        s = cogiles.encode(G)
        assert "Bl" not in s
        assert "K" in s

    def test_roundtrip_user_built_graph(self):
        """Graph built by hand (no symbol attr) round-trips through nearest match."""
        G = nx.Graph()
        G.add_node(0, color=(1.0, 0.0, 0.0))
        G.add_node(1, color=(0.0, 0.5, 0.0))
        G.add_node(2, color=(0.0, 0.0, 1.0))
        G.add_edge(0, 1)
        G.add_edge(1, 2)
        s1 = cogiles.encode(G)
        G2 = cogiles.parse(s1)
        s2 = cogiles.encode(G2)
        assert s1 == s2

    def test_roundtrip_disconnected_with_branches(self):
        """Complex multi-component string with branches round-trips correctly."""
        s = "RR(G)(B).Y(C)(M)"
        G1 = cogiles.parse(s)
        encoded = cogiles.encode(G1)
        G2 = cogiles.parse(encoded)
        assert G1.number_of_nodes() == G2.number_of_nodes()
        assert G1.number_of_edges() == G2.number_of_edges()
        assert nx.is_isomorphic(
            G1,
            G2,
            node_match=lambda n1, n2: n1["color"] == n2["color"],
        )

    def test_bug_fix_node_duplication(self):
        """Regression test for the node duplication bug (06.06.23)."""
        G1 = cogiles.parse("Y-3(RR-3)(R)")
        s = cogiles.encode(G1)
        G2 = cogiles.parse(s)
        assert G1.number_of_nodes() == G2.number_of_nodes()
        assert G1.number_of_edges() == G2.number_of_edges()
