"""
Tests: E₈ branching structure vs KaleidoscopeTensor 3×3×4 factorization.

RESULT SUMMARY (exploratory research):
The 3×3×4 KaleidoscopeTensor does NOT factor under E₈ branching rules.
The tensor and E₈ BCC grid live in different structural worlds:

  - E₈ BCC is bisymmetric with fixed eigenvalues 63±3√33, 0.
  - Tensor Mode 0 is NOT bisymmetric, eigenvalues are n-dependent.
  - No eigenvalue ratio matches √33.
  - No trace matches |E₇| = 126.
  - E₈ grid cannot be expressed as linear combination of tensor slices.

However, two ALGEBRAIC identities hold universally:
  1. Mode 3 eigenvalues = det(M0)/λᵢ(M0)  (cofactor spectral theorem)
  2. tr(M0 · M3ᵀ) = 3·det(M0)             (adjugate trace identity)

These are matrix algebra facts, not E₈-specific.

Phase 88.0 | CSID: E8-TENSOR-001

(C) 2024-2026 Dustin Watkins & DataSphere AI. All Rights Reserved.
"""

import math
import sys
import numpy as np
import pytest

sys.path.insert(0, "src")

from watkins_nn.tensor import KaleidoscopeTensor
from watkins_nn.cascade import (
    bcc_grid,
    bcc_eigenvalues,
    democratic_deficit,
    CASCADE_ORBITS,
    E8_BCC_EIGENVALUE_1,
    E8_BCC_EIGENVALUE_2,
    cascade_chain,
)

T = KaleidoscopeTensor()
SQRT33 = math.sqrt(33)
E8_GRID = bcc_grid("E8")
E8_EIGS = bcc_eigenvalues("E8")
E8_TRACE = 126
E8_DEFICIT = 6
N_VALUES = [5, 10, 20, 50]


# -----------------------------------------------------------------------
# Section 1: Tensor structure sanity
# -----------------------------------------------------------------------

class TestTensorStructure:
    """Verify KaleidoscopeTensor shape and mode semantics."""

    def test_tensor_shape_is_3x3x4(self):
        for n in N_VALUES:
            assert T.tensor_at(n).shape == (3, 3, 4)

    def test_mode0_is_integer_valued(self):
        for n in N_VALUES:
            M = T.tensor_at(n)[:, :, 0]
            np.testing.assert_array_equal(M, np.round(M))

    def test_mode3_cofactor_identity(self):
        """M0 @ adj(M0) = det(M0) * I, where adj = M3^T.

        At large n, det(M0) ~ 10^35, so absolute error ~ 10^20 is
        expected from double-precision arithmetic. Use relative tolerance.
        """
        for n in N_VALUES:
            M0 = T.integer_matrix(n)
            M3 = T.cofactor_matrix(n)
            product = M0 @ M3.T
            det_M0 = np.linalg.det(M0)
            expected = det_M0 * np.eye(3)
            scale = max(abs(det_M0), 1.0)
            np.testing.assert_allclose(product / scale, expected / scale,
                atol=1e-5,
                err_msg=f"Cofactor identity fails at n={n}")


# -----------------------------------------------------------------------
# Section 2: NEGATIVE — No bisymmetry in tensor slices
# -----------------------------------------------------------------------

class TestNoBisymmetry:
    """E₈ BCC grid is bisymmetric [[a,b,a],[b,c,b],[a,b,a]].
    Tensor slices are NOT. This is the fundamental obstruction."""

    def test_e8_bcc_is_bisymmetric(self):
        """Reference: E₈ BCC grid has the bisymmetric form."""
        G = E8_GRID.astype(float)
        assert G[0, 0] == G[2, 2]  # a = a
        assert G[0, 1] == G[2, 1]  # b = b
        assert G[0, 2] == G[2, 0]  # corners equal
        assert G[1, 0] == G[1, 2]  # b = b

    def test_mode0_not_bisymmetric(self):
        """Mode 0 (integer spine) is NOT bisymmetric for any tested n."""
        for n in N_VALUES:
            M = T.integer_matrix(n)
            is_bisym = (
                abs(M[0, 0] - M[2, 2]) < 1e-10
                and abs(M[0, 1] - M[2, 1]) < 1e-10
                and abs(M[1, 0] - M[1, 2]) < 1e-10
            )
            assert not is_bisym, f"Unexpected bisymmetry at n={n}"

    def test_no_mode_is_bisymmetric(self):
        """No mode (0-3) is bisymmetric at any tested n."""
        for n in N_VALUES:
            for mode in range(4):
                M = T.tensor_at(n)[:, :, mode]
                is_bisym = (
                    abs(M[0, 0] - M[2, 2]) < 1e-10
                    and abs(M[0, 1] - M[2, 1]) < 1e-10
                    and abs(M[1, 0] - M[1, 2]) < 1e-10
                )
                assert not is_bisym, \
                    f"Unexpected bisymmetry at n={n}, mode={mode}"


# -----------------------------------------------------------------------
# Section 3: NEGATIVE — Eigenvalues don't match E₈
# -----------------------------------------------------------------------

class TestEigenvalueMismatch:
    """Tensor eigenvalues bear no relation to E₈ BCC eigenvalues."""

    def test_e8_bcc_eigenvalues_reference(self):
        """E₈ BCC eigenvalues: 63+3√33, 63−3√33, 0."""
        assert abs(E8_EIGS[0] - (63 + 3 * SQRT33)) < 1e-10
        assert abs(E8_EIGS[1] - (63 - 3 * SQRT33)) < 1e-10
        assert abs(E8_EIGS[2]) < 1e-10

    def test_mode0_eigenvalue_ratio_not_e8(self):
        """Mode 0 eigenvalue ratios do NOT match E₈ ratio (63+3√33)/(63−3√33)."""
        e8_ratio = E8_BCC_EIGENVALUE_1 / E8_BCC_EIGENVALUE_2  # ≈ 1.753
        for n in N_VALUES:
            eigs = T.eigenvalues(n)
            nonzero = [e for e in eigs if abs(e) > 1e-6]
            if len(nonzero) >= 2:
                ratio = abs(nonzero[0] / nonzero[1])
                assert abs(ratio - e8_ratio) > 0.1, \
                    f"Unexpected E₈ ratio match at n={n}"

    def test_mode0_no_universal_zero_eigenvalue(self):
        """Mode 0 does NOT always have eigenvalue 0 (unlike E₈ BCC).

        E₈ BCC has null vector (1,0,−1) with eigenvalue 0.
        Mode 0 does not share this symmetry.
        """
        zero_count = sum(
            1 for n in N_VALUES
            if any(abs(e) < 1e-6 for e in T.eigenvalues(n))
        )
        assert zero_count < len(N_VALUES), \
            "Unexpectedly all n values have zero eigenvalue"

    def test_mode0_eigenvalues_n_dependent(self):
        """Mode 0 eigenvalues grow with n — NOT fixed like E₈ BCC."""
        gaps = [T.spectral_gap(n) for n in [10, 20]]
        assert gaps[1] > 100 * gaps[0], \
            "Spectral gap should grow rapidly with n"


# -----------------------------------------------------------------------
# Section 4: NEGATIVE — Trace does not match |E₇| = 126
# -----------------------------------------------------------------------

class TestTraceMismatch:
    """tr(E₈ BCC) = 126 = |E₇|. Tensor traces are n-dependent."""

    def test_e8_bcc_trace_is_126(self):
        assert np.trace(E8_GRID) == 126

    def test_mode0_trace_grows_with_n(self):
        """Mode 0 trace is n-dependent, far from constant."""
        traces = [np.trace(T.integer_matrix(n)) for n in [10, 20]]
        assert traces[1] > 100 * traces[0]

    def test_no_single_mode_trace_is_126(self):
        """No single mode at any tested n has trace = 126."""
        for n in N_VALUES:
            for mode in range(4):
                M = T.tensor_at(n)[:, :, mode]
                tr = np.trace(M)
                assert abs(tr - 126) > 0.5 or n > 30, \
                    f"Unexpected trace=126 at n={n}, mode={mode}"

    def test_total_trace_not_126(self):
        """Sum of traces across all 4 modes is NOT 126."""
        for n in N_VALUES:
            total = sum(np.trace(T.tensor_at(n)[:, :, m]) for m in range(4))
            assert abs(total - 126) > 1.0


# -----------------------------------------------------------------------
# Section 5: NEGATIVE — det(M0) never matches cascade layer sizes
# -----------------------------------------------------------------------

class TestDetMismatch:
    """E₈ cascade layers have sizes 4, 6, 12, 16, 32, 54, 114.
    det(Mode 0) never matches any of these."""

    def test_cascade_layer_sizes(self):
        """Reference: cascade layer sizes sum to 238 (E₈ minus A₁)."""
        chain = cascade_chain()
        sizes = [step["layer_size"] for step in chain]
        assert sorted(sizes) == [4, 6, 12, 16, 32, 54, 114]
        assert sum(sizes) == 238

    def test_det_mode0_never_matches_layer_size(self):
        """det(Mode 0) for n=1..30 never equals a cascade layer size."""
        layer_sizes = {4, 6, 12, 16, 32, 54, 114}
        for n in range(1, 31):
            det = abs(round(np.linalg.det(T.integer_matrix(n))))
            assert det not in layer_sizes, \
                f"Unexpected: det(M0) at n={n} = {det} matches a layer size"


# -----------------------------------------------------------------------
# Section 6: NEGATIVE — E₈ grid not expressible via tensor slices
# -----------------------------------------------------------------------

class TestNoLinearDecomposition:
    """E₈ BCC grid cannot be expressed as a linear combination of
    tensor mode slices."""

    def test_e8_not_in_mode0_span(self):
        """E₈ BCC grid NOT in span of Mode 0 at n=1..5 (25% relative error)."""
        target = E8_GRID.astype(float).flatten()
        basis = [T.integer_matrix(n).flatten() for n in range(1, 6)]
        A = np.array(basis).T
        coeffs, _, _, _ = np.linalg.lstsq(A, target, rcond=None)
        recon = A @ coeffs
        rel_error = np.linalg.norm(target - recon) / np.linalg.norm(target)
        assert rel_error > 0.1, \
            f"Unexpected: E₈ grid nearly in Mode 0 span (error={rel_error:.4f})"

    def test_e8_not_in_multimode_span(self):
        """E₈ grid NOT in span of all 4 modes at any single n."""
        target = E8_GRID.astype(float).flatten()
        for n in [5, 10, 15, 20]:
            modes = [T.tensor_at(n)[:, :, m].flatten() for m in range(4)]
            A = np.array(modes).T
            coeffs, _, _, _ = np.linalg.lstsq(A, target, rcond=None)
            recon = A @ coeffs
            rel_error = np.linalg.norm(target - recon) / np.linalg.norm(target)
            assert rel_error > 0.1, \
                f"Unexpected: E₈ in multimode span at n={n} (error={rel_error:.4f})"


# -----------------------------------------------------------------------
# Section 7: POSITIVE — Cofactor spectral theorem (algebraic, not E₈)
# -----------------------------------------------------------------------

class TestCofactorSpectralTheorem:
    """Mode 3 eigenvalues = det(M0)/λᵢ(M0). This is universal matrix
    algebra, not E₈-specific, but confirms tensor internal consistency."""

    def test_cofactor_eigenvalues_are_det_over_lambda(self):
        """eigenvalues(M3) = det(M0)/eigenvalues(M0), sorted."""
        for n in [5, 7, 9, 11, 15, 20]:
            M0 = T.integer_matrix(n)
            M3 = T.cofactor_matrix(n)
            eigs0 = np.linalg.eigvals(M0).real
            eigs3 = np.linalg.eigvals(M3).real
            det0 = np.linalg.det(M0)

            # Skip if any eigenvalue is near zero (division ill-conditioned)
            if any(abs(e) < 1e-6 for e in eigs0):
                continue

            predicted = sorted([det0 / e for e in eigs0], reverse=True)
            actual = sorted(eigs3, reverse=True)
            np.testing.assert_allclose(actual, predicted, rtol=1e-4,
                err_msg=f"Cofactor spectral theorem fails at n={n}")

    def test_adjugate_trace_identity(self):
        """tr(M0 · M3ᵀ) = 3 · det(M0)."""
        for n in N_VALUES:
            M0 = T.integer_matrix(n)
            M3 = T.cofactor_matrix(n)
            lhs = np.trace(M0 @ M3.T)
            rhs = 3 * np.linalg.det(M0)
            np.testing.assert_allclose(lhs, rhs, rtol=1e-4,
                err_msg=f"Adjugate trace identity fails at n={n}")


# -----------------------------------------------------------------------
# Section 8: Mode 2 structure (arithmetic depth)
# -----------------------------------------------------------------------

class TestMode2Structure:
    """Mode 2 (arithmetic depth) has a distinctive upper-triangular
    structure: all diagonal mass concentrates at position (2,2).
    This is because v_2 and v_5 of the diagonal sequences are often 0."""

    def test_mode2_upper_triangular(self):
        """Mode 2 is upper triangular (lower-left entries are zero)."""
        for n in N_VALUES:
            M2 = T.arithmetic_depth_matrix(n)
            assert M2[1, 0] == 0.0, f"M2[1,0] != 0 at n={n}"
            assert M2[2, 0] == 0.0, f"M2[2,0] != 0 at n={n}"
            assert M2[2, 1] == 0.0, f"M2[2,1] != 0 at n={n}"

    def test_mode2_diagonal_at_22(self):
        """Mode 2 diagonal is (0, 0, Omega(seq9)) — mass at (2,2)."""
        for n in N_VALUES:
            M2 = T.arithmetic_depth_matrix(n)
            assert M2[0, 0] == 0.0, f"M2[0,0] != 0 at n={n}"
            assert M2[1, 1] == 0.0, f"M2[1,1] != 0 at n={n}"
            assert M2[2, 2] >= 0.0, f"M2[2,2] < 0 at n={n}"

    def test_mode2_has_zero_determinant(self):
        """Mode 2 is always singular (det=0) due to zero diagonal."""
        for n in N_VALUES:
            M2 = T.arithmetic_depth_matrix(n)
            assert abs(np.linalg.det(M2)) < 1e-10
