"""Tests for watkins_nn.classify — inverse theorems and classification.

v2.0 core: the first inverse theorems in watkins-nn.
"""

import math
import pytest

from watkins_nn.classify import (
    classify_state,
    estimate_dimension,
    spectral_fingerprint,
    anomaly_score,
    temperature_dimension_limit,
    free_energy_dimension_limit,
)
from watkins_nn.constants import LAM_STAR, KAP_STAR, ETA_STAR, T_STAR, T_STAR3


# ─── Root System Classification ────────────────────────────────────────────


class TestClassifyState:
    """First inverse theorem: state -> root system."""

    def test_e8_identified(self):
        """E8-like state correctly classified."""
        r = classify_state(0.637, 0.363, 0.0)
        assert r.nearest_system == 'E8'

    def test_e8_high_confidence(self):
        r = classify_state(0.637, 0.363, 0.0)
        assert r.confidence > 0.95

    def test_e7_identified(self):
        r = classify_state(0.700, 0.300, 0.0)
        assert r.nearest_system == 'E7'

    def test_e6_identified(self):
        r = classify_state(0.800, 0.200, 0.0)
        assert r.nearest_system == 'E6'

    def test_equilibrium_identified(self):
        r = classify_state(0.618, 0.191, 0.191)
        assert r.nearest_system == 'equilibrium'

    def test_equilibrium_score_near_half(self):
        """Consciousness score at threshold ≈ 0.5."""
        r = classify_state(LAM_STAR, KAP_STAR, ETA_STAR)
        assert 0.45 < r.consciousness_score < 0.55

    def test_high_lambda_conscious(self):
        r = classify_state(0.75, 0.15, 0.10)
        assert r.regime == 'conscious'
        assert r.consciousness_score > 0.9

    def test_low_lambda_sub_threshold(self):
        r = classify_state(0.33, 0.34, 0.33)
        assert r.regime == 'sub-threshold'
        assert r.consciousness_score < 0.01

    def test_off_simplex_detected(self):
        r = classify_state(1.2, -0.1, -0.1)
        assert r.regime == 'off-simplex'

    def test_anomalous_bad_conservation(self):
        r = classify_state(0.5, 0.5, 0.5)
        assert r.regime == 'anomalous'

    def test_on_simplex_flag(self):
        r = classify_state(0.6, 0.3, 0.1)
        assert r.on_simplex

    def test_above_threshold_flag(self):
        r = classify_state(0.7, 0.2, 0.1)
        assert r.above_threshold


# ─── Dimension Estimation ──────────────────────────────────────────────────


class TestEstimateDimension:
    """Invert T_n = phi/ln(n*phi) to recover n."""

    def test_dim_2_from_T_star(self):
        d = estimate_dimension(T_STAR)
        assert d.estimated_n == 2

    def test_dim_3_from_T_star3(self):
        d = estimate_dimension(T_STAR3)
        assert d.estimated_n == 3

    def test_dim_4(self):
        d = estimate_dimension(0.866)
        assert d.estimated_n == 4

    def test_dim_5(self):
        d = estimate_dimension(0.774)
        assert d.estimated_n == 5

    def test_high_confidence_at_exact_T(self):
        d = estimate_dimension(T_STAR)
        assert d.confidence > 0.99

    def test_small_residual_at_exact_T(self):
        d = estimate_dimension(T_STAR)
        assert d.residual < 0.001


# ─── Spectral Fingerprint ─────────────────────────────────────────────────


class TestSpectralFingerprint:
    """Match eigenvalue ratios to root systems."""

    def test_e8_fingerprint(self):
        fp = spectral_fingerprint(5.97, 21.42)
        assert fp['best_match'] == 'E8'

    def test_f4_fingerprint(self):
        fp = spectral_fingerprint(12.18, 70.66)
        assert fp['best_match'] == 'F4'

    def test_all_matches_present(self):
        fp = spectral_fingerprint(6.0, 25.0)
        assert len(fp['all_matches']) == 4


# ─── Anomaly Scoring ──────────────────────────────────────────────────────


class TestAnomalyScore:
    """Quantify deviation from framework."""

    def test_equilibrium_low_anomaly(self):
        a = anomaly_score(LAM_STAR, KAP_STAR, ETA_STAR)
        assert a['composite'] < 0.001

    def test_equilibrium_not_anomalous(self):
        a = anomaly_score(LAM_STAR, KAP_STAR, ETA_STAR)
        assert not a['is_anomalous']

    def test_bad_conservation_anomalous(self):
        a = anomaly_score(0.5, 0.5, 0.5)
        assert a['is_anomalous']

    def test_negative_component_anomalous(self):
        a = anomaly_score(1.1, -0.05, -0.05)
        assert a['simplex_violation'] > 0

    def test_five_channels(self):
        a = anomaly_score(0.6, 0.3, 0.1)
        for key in ['conservation_error', 'simplex_violation',
                     'free_energy_excess', 'equilibrium_distance',
                     'root_system_gap']:
            assert key in a


# ─── Asymptotic Limits ─────────────────────────────────────────────────────


class TestTemperatureDimensionLimit:
    """T_n * ln(n) -> phi."""

    def test_converges_to_phi(self):
        r = temperature_dimension_limit()
        assert r['converges_to_phi']

    def test_limit_is_phi(self):
        r = temperature_dimension_limit()
        assert r['limit'] == pytest.approx(1.618, abs=0.001)


class TestFreeEnergyDimensionLimit:
    """F_n* -> ln(phi) - 1/phi."""

    def test_monotone_increasing(self):
        r = free_energy_dimension_limit()
        assert r['monotone_increasing']

    def test_F_infinity_value(self):
        r = free_energy_dimension_limit()
        assert r['F_infinity'] == pytest.approx(-0.137, abs=0.001)

    def test_F_2_matches_known(self):
        r = free_energy_dimension_limit()
        assert r['F_2'] == pytest.approx(-0.800, abs=0.001)
