"""Tests for metrics measurement engine."""

import numpy as np
import pytest

from abiogenesis.metrics import (
    compression_ratio,
    population_histogram,
    top_sequences,
    find_repeated_subsequences,
    find_replicators,
    classify_replicator,
    ReplicatorType,
)


class TestCompressionRatio:
    def test_random_data_near_one(self):
        rng = np.random.default_rng(42)
        soup = rng.integers(0, 256, size=(64, 64), dtype=np.uint8)
        cr = compression_ratio(soup)
        # Random data doesn't compress well — ratio should be ~1.0
        assert 0.9 < cr < 1.1

    def test_uniform_data_compresses(self):
        soup = np.zeros((64, 64), dtype=np.uint8)
        cr = compression_ratio(soup)
        # All zeros compresses extremely well
        assert cr < 0.1

    def test_structured_data_between(self):
        # Repeating pattern — should compress better than random
        pattern = np.tile(np.arange(8, dtype=np.uint8), 8)
        soup = np.tile(pattern, (64, 1))
        cr = compression_ratio(soup)
        assert cr < 0.5


class TestPopulationHistogram:
    def test_all_unique(self):
        rng = np.random.default_rng(42)
        soup = rng.integers(0, 256, size=(8, 32), dtype=np.uint8)
        hist = population_histogram(soup)
        # Very unlikely any two random 32-byte tapes are identical
        assert all(v == 1 for v in hist.values())

    def test_all_identical(self):
        tape = np.array([42] * 8, dtype=np.uint8)
        soup = np.tile(tape, (16, 1))
        hist = population_histogram(soup)
        assert len(hist) == 1
        assert list(hist.values())[0] == 16

    def test_top_sequences(self):
        tape_a = np.array([1] * 8, dtype=np.uint8)
        tape_b = np.array([2] * 8, dtype=np.uint8)
        soup = np.vstack([np.tile(tape_a, (10, 1)), np.tile(tape_b, (5, 1))])
        top = top_sequences(soup, n=2)
        assert len(top) == 2
        assert top[0][1] == 10  # most common
        assert top[1][1] == 5


class TestReplicatorDetection:
    def test_finds_repeated_subsequence(self):
        # Create a soup where a specific pattern appears on multiple tapes
        soup = np.zeros((8, 16), dtype=np.uint8)
        pattern = bytes([0xDE, 0xAD, 0xBE, 0xEF])
        for i in range(4):
            soup[i, 2:6] = list(pattern)

        found = find_repeated_subsequences(soup, min_length=4, min_count=3)
        assert len(found) > 0
        # The pattern should be among the found subsequences
        assert any(pattern in seq for seq in found)

    def test_no_replicators_in_noise(self):
        rng = np.random.default_rng(42)
        soup = rng.integers(0, 256, size=(16, 32), dtype=np.uint8)
        found = find_repeated_subsequences(soup, min_length=4, min_count=3)
        # Random 32-byte tapes with only 16 of them — very unlikely to have repeats
        # (Though it's possible for very short sequences)
        # Just check it returns a dict
        assert isinstance(found, dict)


class TestReplicatorClassification:
    def test_inanimate(self):
        # A sequence with no copy instructions → inanimate
        seq = bytes([43, 43, 45])  # + + -
        result = classify_replicator(seq)
        assert result == ReplicatorType.INANIMATE

    def test_classify_empty(self):
        result = classify_replicator(b'\x00\x00\x00\x00')
        assert result == ReplicatorType.INANIMATE
