"""
Watkins Kaleidoscope Tensor -- 3x3x2 Transfer Operator
=======================================================

Extends the 3x3 integer Kaleidoscope matrix into a two-mode tensor:

    Mode 0 [:,:,0] -- Integer spine (raw sequence values)
    Mode 1 [:,:,1] -- Asymptotic expansion layer (column-specific transforms)

Mode 1 transforms (by column / conservation channel):
    Col 0 (lambda-channel, Numerators):  Signed Lagrange-Burmann coefficients
        (-1)^(n+1) * val / (n+1)!
    Col 1 (kappa-channel, GCDs):         Resonance spike indicators
        val if val > 1 else 0
    Col 2 (eta-channel, Preservation):   phi^2-scaled moments
        val / phi^(2n)

Phase 87.x | CSID: TENSOR-001

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

import numpy as np
from math import factorial
from typing import Dict, List

from .kaleidoscope import (
    seq1_cycle_lb_numerator, seq2_mersenne_numerator, seq3_mersenne_gcd,
    seq4_lucas_numerator, seq5_lucas_gcd, seq6_mersenne_lucas_gcd,
    seq7_binary_preservation, seq8_cross_gcd, seq9_weight_moments,
)
from .constants import PHI_SQUARED

__all__ = ['KaleidoscopeTensor']

# Sequence grid: _SEQ_GRID[row][col] -> callable(n) -> int
# Rows: 0=Mersenne(sigma=0), 1=Bridge(0 cap 1), 2=Lucas(sigma=1)
# Cols: 0=Numerators(lambda), 1=GCDs(kappa), 2=Preservation(eta)
_SEQ_GRID = [
    [seq2_mersenne_numerator, seq3_mersenne_gcd,      seq1_cycle_lb_numerator],
    [seq6_mersenne_lucas_gcd, seq8_cross_gcd,         seq7_binary_preservation],
    [seq4_lucas_numerator,    seq5_lucas_gcd,          seq9_weight_moments],
]


class KaleidoscopeTensor:
    """3x3x2 transfer operator for the Watkins Spectral Kaleidoscope.

    Rows (sigma-parametrizations):
        0: Mersenne (binary, sigma=0)
        1: Bridge (interference, sigma=0 cap 1)
        2: Lucas (golden, sigma=1)

    Columns (conservation channels):
        0: Numerators (lambda-channel, coherence)
        1: GCDs (kappa-channel, curvature)
        2: Preservation (eta-channel, entropy/flow)

    Modes:
        0: Integer spine -- raw sequence values
        1: Asymptotic expansion -- signed LB / resonance / phi^2-scaled
    """

    ROW_LABELS = ['sigma=0 (Mersenne)', 'Bridge (0 cap 1)', 'sigma=1 (Lucas)']
    COL_LABELS = ['Numerators (lambda)', 'GCDs (kappa)', 'Preservation (eta)']

    def integer_matrix(self, n: int) -> np.ndarray:
        """3x3 integer matrix at index n (Mode 0 slice)."""
        return np.array([
            [_SEQ_GRID[r][c](n) for c in range(3)]
            for r in range(3)
        ], dtype=float)

    def asymptotic_matrix(self, n: int) -> np.ndarray:
        """3x3 float matrix at index n (Mode 1 slice).

        Column transforms:
            Col 0: (-1)^(n+1) * val / (n+1)!   (signed Lagrange-Burmann)
            Col 1: val if val > 1 else 0        (resonance spike)
            Col 2: val / phi^(2n)               (phi-squared-scaled)
        """
        sign = (-1) ** (n + 1)
        fact = factorial(n + 1)
        phi_2n = PHI_SQUARED ** n

        result = np.zeros((3, 3), dtype=float)
        for r in range(3):
            v0 = float(_SEQ_GRID[r][0](n))
            v1 = float(_SEQ_GRID[r][1](n))
            v2 = float(_SEQ_GRID[r][2](n))
            result[r, 0] = sign * v0 / fact
            result[r, 1] = v1 if v1 > 1 else 0.0
            result[r, 2] = v2 / phi_2n if phi_2n != 0 else 0.0
        return result

    def tensor_at(self, n: int) -> np.ndarray:
        """Evaluate the full 3x3x2 tensor at index n.

        Returns ndarray of shape (3, 3, 2) with dtype float64.
        """
        T = np.zeros((3, 3, 2), dtype=float)
        T[:, :, 0] = self.integer_matrix(n)
        T[:, :, 1] = self.asymptotic_matrix(n)
        return T

    def curvature(self, n: int) -> float:
        """trace(M0) / det(M0) -- LDPC curvature hook."""
        M = self.integer_matrix(n)
        det = np.linalg.det(M)
        if abs(det) < 1e-15:
            return float('inf')
        return float(np.trace(M) / det)

    def eigenvalues(self, n: int) -> np.ndarray:
        """Sorted real eigenvalues (descending) of the integer matrix."""
        M = self.integer_matrix(n)
        eigs = np.linalg.eigvals(M).real
        return np.sort(eigs)[::-1]

    def spectral_gap(self, n: int) -> float:
        """|lambda_1 - lambda_2| of the integer matrix eigenvalues."""
        eigs = self.eigenvalues(n)
        return float(abs(eigs[0] - eigs[1]))

    def resonance_slice(self, n: int) -> Dict:
        """Full resonance analysis at index n."""
        M0 = self.integer_matrix(n)
        M1 = self.asymptotic_matrix(n)

        kappa_raw = M0[:, 1].tolist()
        kappa_spikes = M1[:, 1].tolist()
        resonant_rows = [r for r in range(3) if kappa_spikes[r] > 0]
        cross_gcd_val = int(M0[1, 1])

        return {
            'n': n,
            'kappa_channel': kappa_raw,
            'kappa_spikes': kappa_spikes,
            'resonant_rows': resonant_rows,
            'cross_gcd': cross_gcd_val,
            'is_bridge_resonance': cross_gcd_val > 1,
            'curvature': self.curvature(n),
            'spectral_gap': self.spectral_gap(n),
            'eigenvalues': self.eigenvalues(n).tolist(),
            'lagrange_burmann_col': M1[:, 0].tolist(),
            'phi_scaled_col': M1[:, 2].tolist(),
        }

    def print_tensor(self, n: int) -> str:
        """Pretty-print with resonance highlighting. Returns the string."""
        M0 = self.integer_matrix(n)
        M1 = self.asymptotic_matrix(n)
        cross_gcd = int(M0[1, 1])

        lines: List[str] = []
        lines.append(f"KALEIDOSCOPE TENSOR T({n}) -- 3x3x2")
        lines.append("=" * 60)

        lines.append("Mode 0 (integer spine):")
        labels = ['sigma=0 (Mers)', 'Bridge       ', 'sigma=1 (Luc) ']
        for r in range(3):
            row_str = ", ".join(f"{int(M0[r, c]):>8}" for c in range(3))
            marker = "  <-- SPIKE" if cross_gcd > 1 and r == 1 else ""
            lines.append(f"  {labels[r]}: [{row_str}]{marker}")

        lines.append("")
        lines.append("Mode 1 (asymptotic expansion):")
        cols = ['LB-signed', 'Resonance', 'phi^2-scaled']
        header = "               " + "  ".join(f"{c:>12}" for c in cols)
        lines.append(header)
        for r in range(3):
            row_str = "  ".join(f"{M1[r, c]:>12.6f}" for c in range(3))
            lines.append(f"  {labels[r]}: {row_str}")

        curv = self.curvature(n)
        gap = self.spectral_gap(n)
        lines.append("")
        curv_str = f"{curv:.6f}" if curv != float('inf') else "inf (singular)"
        lines.append(f"Curvature: trace/det = {curv_str}")
        lines.append(f"Spectral gap: |l1-l2| = {gap:.6f}")

        if cross_gcd > 1:
            lines.append(f"Resonance: Bridge spike = {cross_gcd}")

        out = "\n".join(lines)
        print(out)
        return out
