"""Tests for CfCMeshCoupler — mirrors Swift test suite."""

import pytest
from mesh_cognition import MeshNode, CfCMeshCoupler, CouplingConfig, CouplingDecision
from mesh_cognition.coupler import CouplingDecisionType

DIM = 64


def zeros(dim=DIM):
    return [0.0] * dim


def uniform(val, dim=DIM):
    return [val] * dim


class TestSoloMode:
    def test_returns_local_unchanged(self):
        coupler = CfCMeshCoupler()
        h1, h2 = coupler.couple(zeros(), zeros())
        assert h1 == zeros()
        assert h2 == zeros()

    def test_metrics_are_none(self):
        coupler = CfCMeshCoupler()
        assert coupler.l2_distance(zeros(), zeros()) is None
        assert coupler.kuramoto_order_parameter(zeros(), zeros()) is None
        assert not coupler.has_active_peers


class TestAlignedCoupling:
    def test_similar_states_couple(self):
        coupler = CfCMeshCoupler()
        local = uniform(0.5)
        peer = list(local)
        peer[0] = 0.52  # tiny difference

        coupler.update_peer("peer-1", peer, peer, confidence=0.9)
        h1, _ = coupler.couple(local, local)

        assert h1 != local  # should be influenced
        assert coupler.last_decisions["peer-1"].decision == CouplingDecisionType.ALIGNED


class TestMisalignedRejection:
    def test_opposite_states_rejected(self):
        coupler = CfCMeshCoupler()
        local = [i / DIM for i in range(DIM)]
        peer = [-i / DIM for i in range(DIM)]

        coupler.update_peer("peer-bad", peer, peer, confidence=0.9)
        h1, h2 = coupler.couple(local, local)

        assert h1 == local  # unchanged — peer rejected
        assert coupler.last_decisions["peer-bad"].decision == CouplingDecisionType.REJECTED


class TestPeerManagement:
    def test_remove_peer(self):
        coupler = CfCMeshCoupler()
        coupler.update_peer("p1", zeros(), zeros(), 0.5)
        assert coupler.has_active_peers
        coupler.remove_peer("p1")
        assert not coupler.has_active_peers

    def test_remove_all(self):
        coupler = CfCMeshCoupler()
        coupler.update_peer("p1", zeros(), zeros(), 0.5)
        coupler.update_peer("p2", zeros(), zeros(), 0.5)
        assert coupler.active_peer_count == 2
        coupler.remove_all_peers()
        assert coupler.active_peer_count == 0


class TestKuramotoMetrics:
    def test_identical_states_high_r(self):
        coupler = CfCMeshCoupler()
        h = uniform(0.5)
        coupler.update_peer("p1", h, h, confidence=0.9)
        r = coupler.kuramoto_order_parameter(h, h)
        assert r is not None
        assert r > 0.99


class TestCustomConfig:
    def test_config_applied(self):
        config = CouplingConfig(
            drift_threshold_aligned=0.1,
            alpha_aligned=0.5,
        )
        coupler = CfCMeshCoupler(config=config)
        assert coupler.config.drift_threshold_aligned == 0.1
        assert coupler.config.alpha_aligned == 0.5


class TestDimensionMismatch:
    def test_wrong_dim_skipped(self):
        coupler = CfCMeshCoupler()
        coupler.update_peer("p1", [0.0] * 32, [0.0] * 32, 0.9)  # wrong dim
        h1, h2 = coupler.couple(zeros(), zeros())
        assert h1 == zeros()  # local unchanged


class TestMeshNode:
    def test_full_workflow(self):
        node = MeshNode(hidden_dim=DIM)
        h = uniform(0.5)
        node.update_local_state(h, h, confidence=0.8)

        # No peers — returns local
        c1, c2 = node.coupled_state()
        assert c1 == h

        # Add aligned peer
        peer = list(h)
        peer[0] = 0.52
        node.add_peer("peer-1", peer, peer, confidence=0.9)

        c1, c2 = node.coupled_state()
        assert c1 != h  # influenced by peer
        assert node.active_peer_count == 1
        assert node.kuramoto_order_parameter is not None

    def test_metrics_export(self, tmp_path):
        node = MeshNode(hidden_dim=DIM)
        h = uniform(0.5)
        node.update_local_state(h, h)
        node.add_peer("p1", h, h, confidence=0.9)
        node.coupled_state()

        path = str(tmp_path / "metrics.jsonl")
        result = node.export_metrics(path)
        assert "metrics.jsonl" in result
