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

Four-mode tensor encoding the Kaleidoscope Structure Theorem:

    Mode 0 [:,:,0] -- Integer spine (WHAT the sequences are)
    Mode 1 [:,:,1] -- Asymptotic expansion (HOW they grow: e, 2, phi)
    Mode 2 [:,:,2] -- Arithmetic depth (WHERE in the prime lattice)
    Mode 3 [:,:,3] -- Cofactor interaction (HOW they interact pairwise)

Mode 1 transforms (column-specific scale):
    Col 0: (-1)^(n+1) * val / (n+1)!   (signed Lagrange-Burmann, governed by e)
    Col 1: val if val > 1 else 0        (resonance spike, governed by 2)
    Col 2: val / phi^(2n)               (golden decay, governed by phi)

Mode 2 transforms (column-specific arithmetic depth):
    Col 0: v_2(val) -- 2-adic valuation (Mersenne lattice Z[2])
    Col 1: v_5(val) -- 5-adic valuation (golden lattice Z[sqrt(5)])
    Col 2: Omega(val) -- total prime omega (arithmetic complexity)

Mode 3: Cofactor (interaction) matrix
    C[i,j] = (-1)^(i+j) * det(minor obtained by deleting row i, col j)
    Encodes pairwise interaction energies between rows and columns.
    Key entries:
        C[0,0] = L(2n) - seq7*seq5     (Bridge-Lucas minor, when seq8=1)
        C[1,1] = seq2*L(2n) - seq1*seq4 (Mersenne-Lucas minor = Delta_ML)
        C[2,2] = seq2 - seq3*seq6       (Mersenne-Bridge minor, when seq8=1)
    Satisfies: M(n) * adj(M(n)) = det(M(n)) * I

Phase 88.0 | CSID: TENSOR-003

(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


def _v_adic(n: int, p: int) -> int:
    """p-adic valuation of integer n: largest k such that p^k | n."""
    if n == 0:
        return 0
    n = abs(int(n))
    v = 0
    while n > 0 and n % p == 0:
        v += 1
        n //= p
    return v


def _big_omega(n: int) -> int:
    """Total number of prime factors with multiplicity (Omega function)."""
    n = abs(int(n))
    if n <= 1:
        return 0
    count = 0
    d = 2
    while d * d <= n:
        while n % d == 0:
            count += 1
            n //= d
        d += 1
    if n > 1:
        count += 1
    return count

__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:
    """3x3x4 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 -- WHAT (raw sequence values)
        1: Asymptotic expansion -- HOW they grow (e, 2, phi)
        2: Arithmetic depth -- WHERE in the prime lattice (v_2, v_5, Omega)
        3: Cofactor interaction -- HOW they interact pairwise (adj(M))
    """

    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 arithmetic_depth_matrix(self, n: int) -> np.ndarray:
        """3x3 integer matrix at index n (Mode 2 slice).

        Column transforms (arithmetic depth in the prime lattice):
            Col 0: v_2(val)    2-adic valuation (Mersenne/binary depth)
            Col 1: v_5(val)    5-adic valuation (golden depth, phi in Z[sqrt(5)])
            Col 2: Omega(val)  Total prime omega (arithmetic complexity)
        """
        result = np.zeros((3, 3), dtype=float)
        for r in range(3):
            v0 = int(_SEQ_GRID[r][0](n))
            v1 = int(_SEQ_GRID[r][1](n))
            v2 = int(_SEQ_GRID[r][2](n))
            result[r, 0] = float(_v_adic(v0, 2))
            result[r, 1] = float(_v_adic(v1, 5))
            result[r, 2] = float(_big_omega(v2)) if abs(v2) > 1 else 0.0
        return result

    def cofactor_matrix(self, n: int) -> np.ndarray:
        """3x3 cofactor (interaction) matrix at index n (Mode 3 slice).

        C[i,j] = (-1)^(i+j) * det(2x2 minor from deleting row i, col j).
        This is the COFACTOR matrix (transpose of adjugate).

        Key entries (when seq8=1):
            C[0,0] = L(2n) - seq7*seq5       (Bridge-Lucas interaction)
            C[1,1] = seq2*L(2n) - seq1*seq4   (Mersenne-Lucas minor, Thm I)
            C[2,2] = seq2 - seq3*seq6          (Mersenne-Bridge interaction)

        Satisfies: M * adj(M) = det(M) * I, where adj = cofactor^T.
        """
        M = self.integer_matrix(n)
        result = np.zeros((3, 3), dtype=float)
        for i in range(3):
            for j in range(3):
                rows = [r for r in range(3) if r != i]
                cols = [c for c in range(3) if c != j]
                minor = M[rows[0], cols[0]] * M[rows[1], cols[1]] - \
                        M[rows[0], cols[1]] * M[rows[1], cols[0]]
                result[i, j] = ((-1) ** (i + j)) * minor
        return result

    def tensor_at(self, n: int, n_modes: int = 4) -> np.ndarray:
        """Evaluate the tensor at index n.

        Args:
            n: Sequence index.
            n_modes: Number of modes (3 for backward compat, 4 for full).

        Returns ndarray of shape (3, 3, n_modes) with dtype float64.
        """
        T = np.zeros((3, 3, n_modes), dtype=float)
        T[:, :, 0] = self.integer_matrix(n)
        T[:, :, 1] = self.asymptotic_matrix(n)
        T[:, :, 2] = self.arithmetic_depth_matrix(n)
        if n_modes >= 4:
            T[:, :, 3] = self.cofactor_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)
        M2 = self.arithmetic_depth_matrix(n)
        M3 = self.cofactor_matrix(n)
        cross_gcd = int(M0[1, 1])

        lines: List[str] = []
        lines.append(f"KALEIDOSCOPE TENSOR T({n}) -- 3x3x4")
        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}")

        lines.append("")
        lines.append("Mode 2 (arithmetic depth):")
        cols2 = ['v_2(val)', 'v_5(val)', 'Omega(val)']
        header2 = "               " + "  ".join(f"{c:>12}" for c in cols2)
        lines.append(header2)
        for r in range(3):
            row_str = "  ".join(f"{int(M2[r, c]):>12}" for c in range(3))
            lines.append(f"  {labels[r]}: {row_str}")

        lines.append("")
        lines.append("Mode 3 (cofactor interaction):")
        cols3 = ['C[i,0]', 'C[i,1]', 'C[i,2]']
        header3 = "               " + "  ".join(f"{c:>12}" for c in cols3)
        lines.append(header3)
        for r in range(3):
            row_str = "  ".join(f"{int(M3[r, c]):>12}" 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}")
        lines.append(f"Delta_ML (C[1,1]): {int(M3[1,1])}")

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

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