"""Tests for the axiom_derivation module.

Tests cover:
- Module-level constants (PHI_BAR, N_FROM_TRACE)
- Route 1: Trace of Frobenius (Tr(phi^2) = 3)
- Route 2: Adams operations / Lucas numbers (psi^2 = 3)
- Route 3: Information theory / Fibonacci lattice
- Route 4: Quantum SU(2) at golden root (CS level k=3)
- Route 5: Three-Distance Theorem
- Route 6: Self-consistency bootstrap (N=3 unique fixed point)
- Master derivation: all 6 routes
- N=3 uniqueness proof
- DerivationResult type
"""

import math
import pytest

from watkins_nn.constants import (
    PHI, PHI_INV, PHI_SQUARED, SQRT5,
    T_STAR, LAM_STAR, KAP_STAR, ETA_STAR,
    CONSERVATION_EPSILON,
)
from watkins_nn.axiom_derivation import (
    PHI_BAR,
    PHI_BAR_SQUARED,
    N_FROM_TRACE,
    DerivationResult,
    derive_from_trace_of_frobenius,
    derive_from_adams_operations,
    derive_from_information_theory,
    derive_from_quantum_groups,
    derive_from_quasicrystal,
    derive_from_bootstrap,
    derive_conservation_law,
    n3_uniqueness_proof,
)


EPS = CONSERVATION_EPSILON


# ── Module Constants ─────────────────────────────────────────────────────────

class TestModuleConstants:

    def test_phi_bar(self):
        """phi_bar = (1 - sqrt(5)) / 2 = -1/phi."""
        assert abs(PHI_BAR - (1 - SQRT5) / 2) < EPS
        assert abs(PHI_BAR + 1 / PHI) < EPS

    def test_phi_bar_squared(self):
        assert abs(PHI_BAR_SQUARED - PHI_BAR ** 2) < EPS

    def test_n_from_trace_is_3(self):
        """Tr(phi^2) = phi^2 + phi_bar^2 = 3."""
        assert abs(N_FROM_TRACE - 3.0) < EPS

    def test_vieta_sum(self):
        """phi + phi_bar = 1."""
        assert abs(PHI + PHI_BAR - 1.0) < EPS

    def test_vieta_product(self):
        """phi * phi_bar = -1."""
        assert abs(PHI * PHI_BAR - (-1.0)) < EPS

    def test_newtons_identity(self):
        """Tr(phi)^2 - 2*N(phi) = 1 - 2*(-1) = 3."""
        result = (PHI + PHI_BAR) ** 2 - 2 * (PHI * PHI_BAR)
        assert abs(result - 3.0) < EPS


# ── DerivationResult ─────────────────────────────────────────────────────────

class TestDerivationResult:

    def test_is_valid_when_n3(self):
        r = DerivationResult(
            route_name="test", route_number=0, n_derived=3,
            is_exact=True, numerical_value=3.0, deviation=0.0,
            key_identity="test", interpretation="test"
        )
        assert r.is_valid

    def test_not_valid_when_not_n3(self):
        r = DerivationResult(
            route_name="test", route_number=0, n_derived=4,
            is_exact=True, numerical_value=4.0, deviation=0.0,
            key_identity="test", interpretation="test"
        )
        assert not r.is_valid


# ── Route 1: Trace of Frobenius ──────────────────────────────────────────────

class TestRoute1:

    def test_gives_n3(self):
        r = derive_from_trace_of_frobenius()
        assert r.n_derived == 3

    def test_is_exact(self):
        r = derive_from_trace_of_frobenius()
        assert r.is_exact

    def test_is_valid(self):
        r = derive_from_trace_of_frobenius()
        assert r.is_valid

    def test_three_sub_routes_agree(self):
        r = derive_from_trace_of_frobenius()
        d = r.details
        assert abs(d['trace_direct'] - 3.0) < EPS
        assert abs(d['trace_via_norm'] - 3.0) < EPS
        assert abs(d['trace_via_expansion'] - 3.0) < EPS
        assert d['all_routes_agree']

    def test_sub_identities(self):
        r = derive_from_trace_of_frobenius()
        d = r.details
        assert abs(d['Tr_phi'] - 1.0) < EPS
        assert abs(d['N_phi'] - (-1.0)) < EPS


# ── Route 2: Adams Operations ────────────────────────────────────────────────

class TestRoute2:

    def test_gives_n3(self):
        r = derive_from_adams_operations()
        assert r.n_derived == 3

    def test_is_exact(self):
        r = derive_from_adams_operations()
        assert r.is_exact

    def test_L2_equals_3(self):
        r = derive_from_adams_operations()
        assert abs(r.details['L_2'] - 3.0) < EPS

    def test_lucas_recurrence(self):
        r = derive_from_adams_operations()
        assert r.details['recurrence_holds']

    def test_newtons_identity(self):
        r = derive_from_adams_operations()
        assert r.details['newtons_identity'] == 3


# ── Route 3: Information Theory ──────────────────────────────────────────────

class TestRoute3:

    def test_gives_n3(self):
        r = derive_from_information_theory()
        assert r.n_derived == 3

    def test_char_eq_residual_near_zero(self):
        """phi^4 - 3*phi^2 + 1 = 0."""
        r = derive_from_information_theory()
        assert abs(r.details['char_eq_residual']) < EPS

    def test_fibonacci_rate_positive(self):
        r = derive_from_information_theory()
        assert r.details['fibonacci_rate_bits'] > 0


# ── Route 4: Quantum Groups ─────────────────────────────────────────────────

class TestRoute4:

    def test_gives_n3(self):
        r = derive_from_quantum_groups()
        assert r.n_derived == 3

    def test_is_exact(self):
        r = derive_from_quantum_groups()
        assert r.is_exact

    def test_q_plus_qinv_equals_phi(self):
        r = derive_from_quantum_groups()
        assert r.details['q_plus_qinv_equals_phi']

    def test_quantum_integer_2_is_phi(self):
        r = derive_from_quantum_groups()
        qi = r.details['quantum_integers']
        assert abs(qi['[2]_q'] - PHI) < 1e-10

    def test_truncation_at_5(self):
        r = derive_from_quantum_groups()
        qi = r.details['quantum_integers']
        assert qi['[5]_q'] < 1e-10

    def test_cs_level_is_3(self):
        r = derive_from_quantum_groups()
        assert r.details['k_level'] == 3
        assert r.details['p_truncation'] == 5


# ── Route 5: Three-Distance Theorem ─────────────────────────────────────────

class TestRoute5:

    def test_gives_n3(self):
        r = derive_from_quasicrystal()
        assert r.n_derived == 3

    def test_at_most_3_gaps(self):
        r = derive_from_quasicrystal()
        for n, data in r.details['results_by_n'].items():
            assert data['n_distinct_gaps'] <= 3

    def test_exactly_3_at_n10(self):
        r = derive_from_quasicrystal()
        assert r.details['n_distinct_at_10'] == 3

    def test_gap_sum_is_1(self):
        r = derive_from_quasicrystal()
        assert r.details['gap_sum_is_1']


# ── Route 6: Bootstrap ──────────────────────────────────────────────────────

class TestRoute6:

    def test_gives_n3(self):
        r = derive_from_bootstrap()
        assert r.n_derived == 3

    def test_is_unique(self):
        r = derive_from_bootstrap()
        assert r.details['is_unique']
        assert r.details['fixed_points'] == [3]

    def test_n2_fails(self):
        r = derive_from_bootstrap()
        assert not r.details['first_10'][2]['is_fixed_point']

    def test_n4_fails(self):
        r = derive_from_bootstrap()
        assert not r.details['first_10'][4]['is_fixed_point']

    def test_kap_star_consistency(self):
        r = derive_from_bootstrap()
        assert r.details['kap_star_check']


# ── Master Derivation ───────────────────────────────────────────────────────

class TestDeriveConservationLaw:

    def test_all_routes_give_n3(self):
        r = derive_conservation_law()
        assert r['all_routes_give_N3']

    def test_six_routes(self):
        r = derive_conservation_law()
        assert r['n_total_routes'] == 6

    def test_five_exact_routes(self):
        """Routes 1-5 are exact, route 6 uses round()."""
        r = derive_conservation_law()
        assert r['n_exact_routes'] == 5

    def test_conservation_holds(self):
        r = derive_conservation_law()
        assert r['conservation_holds']

    def test_step_verification_all_pass(self):
        r = derive_conservation_law()
        for key, val in r['step_verification'].items():
            assert val, f"Step verification failed: {key}"

    def test_master_chain_length(self):
        r = derive_conservation_law()
        assert len(r['master_chain']) == 7  # Axiom + 6 steps

    def test_theorem_not_empty(self):
        r = derive_conservation_law()
        assert len(r['theorem']) > 100


# ── N=3 Uniqueness ──────────────────────────────────────────────────────────

class TestN3Uniqueness:

    def test_n_is_3(self):
        r = n3_uniqueness_proof()
        assert r['N'] == 3

    def test_is_integer(self):
        r = n3_uniqueness_proof()
        assert r['is_integer']

    def test_discriminant_is_5(self):
        r = n3_uniqueness_proof()
        assert r['discriminant'] == 5

    def test_lucas_numbers_are_integers(self):
        r = n3_uniqueness_proof()
        for k, data in r['lucas_numbers'].items():
            assert data['is_integer'], f"L_{k} is not integer"
