"""Tests for the soup population manager."""

import numpy as np
import pytest

from abiogenesis.soup import Soup, InteractionResult


class TestSoup:
    def test_initialization(self):
        soup = Soup(n_tapes=16, tape_len=8, seed=42)
        assert soup.tapes.shape == (16, 8)
        assert soup.tapes.dtype == np.uint8

    def test_deterministic_with_seed(self):
        s1 = Soup(n_tapes=16, tape_len=8, seed=42)
        s2 = Soup(n_tapes=16, tape_len=8, seed=42)
        np.testing.assert_array_equal(s1.tapes, s2.tapes)

    def test_interact_returns_result(self):
        soup = Soup(n_tapes=16, tape_len=8, seed=42)
        result = soup.interact()
        assert isinstance(result, InteractionResult)
        assert 0 <= result.idx_a < 16
        assert 0 <= result.idx_b < 16
        assert result.idx_a != result.idx_b
        assert result.ops_count >= 0

    def test_interact_modifies_tapes(self):
        soup = Soup(n_tapes=16, tape_len=8, seed=42)
        before = soup.get_state()
        # Run many interactions — at least some should modify tapes
        for _ in range(100):
            soup.interact()
        after = soup.get_state()
        # Not guaranteed to be different for every interaction,
        # but across 100 it's virtually certain
        assert not np.array_equal(before, after)

    def test_get_set_state(self):
        # Use larger tapes so interactions are more likely to modify state
        soup = Soup(n_tapes=32, tape_len=64, seed=42)
        state = soup.get_state()
        rng_state = soup.get_rng_state()

        # Run enough interactions that state almost certainly changes
        for _ in range(100):
            soup.interact()
        assert not np.array_equal(soup.tapes, state)

        # Restore
        soup.set_state(state, rng_state)
        np.testing.assert_array_equal(soup.tapes, state)

    def test_reproducibility_with_state_restore(self):
        soup = Soup(n_tapes=16, tape_len=8, seed=42)
        # Run 10 interactions
        for _ in range(10):
            soup.interact()

        # Save state
        state = soup.get_state()
        rng_state = soup.get_rng_state()

        # Run 10 more
        results_a = []
        for _ in range(10):
            results_a.append(soup.interact())

        # Restore and run same 10
        soup.set_state(state, rng_state)
        results_b = []
        for _ in range(10):
            results_b.append(soup.interact())

        for a, b in zip(results_a, results_b):
            assert a.ops_count == b.ops_count
            assert a.idx_a == b.idx_a
            assert a.idx_b == b.idx_b
