"""Polynomial Tower — Lucas-Fibonacci algebraic hierarchy on the golden ratio.

THE CONSERVATION POLYNOMIAL:
    The minimal polynomial of phi^2 is t^2 - 3t + 1 = 0.  This single equation
    encodes the Watkins framework structure:

        Coefficient 3  ->  Tr(phi^2) = 3        ->  N = 3 components
        Constant 1     ->  Nm(phi^2) = 1        ->  conservation lambda+kappa+eta = 1
        Discriminant 5 ->  9 - 4 = 5            ->  sqrt(5) -> phi
        Product 15     ->  3 x 5 = 15           ->  sqrt(15) = bridge

THE POLYNOMIAL TOWER:
    Each power phi^n is a root of t^2 - L(n)*t + (-1)^n = 0
    where L(n) is the nth Lucas number.

        Even n: norm = +1 (palindromic)
        Odd n:  norm = -1 (anti-palindromic)

    Discriminant at level n: Delta_n = L(n)^2 - 4*(-1)^n = 5 * F(n)^2

THE HIERARCHY EXPONENT:
    The algebraic structure 75/4 = 3 x 25/4 = 5*(L(6)-L(2))/4 encodes
    the bridge number L(6)-L(2) = 18-3 = 15 within the Lucas tower.

ADDITIONAL RESULTS:
    - 2/sqrt(5) = |tan(icosahedral dihedral angle)|
    - h(Q(sqrt(15))) = 2  ->  Z_2 class group
    - Pell equation x^2 - 15*y^2 = 1: solutions and E_8 connections
    - Discriminants Delta_n = 5*F(n)^2 (always 5 times a Fibonacci square)
"""

import math
from typing import Any, Dict, List, Tuple
from dataclasses import dataclass

from .constants import (
    PHI,
    PHI_INV,
    PHI_SQUARED,
    SQRT5,
    CONSERVATION_EPSILON,
)


# =============================================================================
# FIBONACCI AND LUCAS SEQUENCES
# =============================================================================

def fibonacci(n: int) -> int:
    """Compute the nth Fibonacci number F(n).

    F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2).
    Supports n >= 0.
    """
    if n < 0:
        raise ValueError(f"fibonacci requires n >= 0, got {n}")
    if n == 0:
        return 0
    if n == 1:
        return 1
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b


def lucas(n: int) -> int:
    """Compute the nth Lucas number L(n).

    L(0) = 2, L(1) = 1, L(n) = L(n-1) + L(n-2).
    Supports n >= 0.
    """
    if n < 0:
        raise ValueError(f"lucas requires n >= 0, got {n}")
    if n == 0:
        return 2
    if n == 1:
        return 1
    a, b = 2, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b


# Precomputed lookup tables for n = 0..15
LUCAS_TABLE = [lucas(n) for n in range(16)]
FIBONACCI_TABLE = [fibonacci(n) for n in range(16)]


# =============================================================================
# 1. THE CONSERVATION POLYNOMIAL: t^2 - 3t + 1 = 0
# =============================================================================

def conservation_polynomial() -> Dict[str, Any]:
    """Analyse the minimal polynomial of phi^2: t^2 - 3t + 1 = 0.

    This polynomial encodes the framework structure:
        - Coefficient 3  -> trace -> N = 3 components -> sqrt(3) -> simplex
        - Constant 1     -> norm  -> conservation law lambda+kappa+eta = 1
        - Discriminant 5 -> sqrt(5) -> phi = (1+sqrt5)/2
        - Product 15     -> 3 * 5 = 15 -> sqrt(15) -> bridge

    Returns:
        Dict with all polynomial data and verifications.
    """
    # Roots: phi^2 and 1/phi^2 (the conjugate)
    root1 = PHI_SQUARED
    root2 = 1.0 / PHI_SQUARED

    # Verify roots satisfy t^2 - 3t + 1 = 0
    residual1 = root1**2 - 3 * root1 + 1
    residual2 = root2**2 - 3 * root2 + 1

    # Polynomial data
    trace = 3
    norm = 1
    discriminant = trace**2 - 4 * norm       # 9 - 4 = 5

    N_components = trace
    conservation_value = norm
    sqrt_disc = math.sqrt(discriminant)
    bridge_number = trace * discriminant      # 3 * 5 = 15

    return {
        'polynomial': 't^2 - 3t + 1',
        'roots': {
            'phi_squared': root1,
            'phi_bar_squared': root2,
        },
        'residuals': {
            'root1': residual1,
            'root2': residual2,
            'both_zero': abs(residual1) < 1e-14 and abs(residual2) < 1e-14,
        },
        'vieta': {
            'sum_of_roots': root1 + root2,
            'product_of_roots': root1 * root2,
            'trace': trace,
            'norm': norm,
            'sum_equals_trace': abs(root1 + root2 - trace) < 1e-14,
            'product_equals_norm': abs(root1 * root2 - norm) < 1e-14,
        },
        'discriminant': {
            'value': discriminant,
            'sqrt': sqrt_disc,
            'equals_5': discriminant == 5,
        },
        'encoding': {
            'coefficient_3': {
                'meaning': 'Tr(phi^2) = 3 -> N = 3 components',
                'value': N_components,
                'consequence': 'sqrt(3) -> equilateral simplex',
            },
            'constant_1': {
                'meaning': 'Nm(phi^2) = 1 -> conservation constraint',
                'value': conservation_value,
                'consequence': 'lambda + kappa + eta = 1 (conservation law)',
            },
            'discriminant_5': {
                'meaning': 'Delta = 9 - 4 = 5',
                'value': discriminant,
                'consequence': 'sqrt(5) -> phi = (1+sqrt5)/2 -> golden ratio',
            },
            'bridge_15': {
                'meaning': '3 * 5 = 15 -> bridge number',
                'value': bridge_number,
                'consequence': 'sqrt(15) connects simplex and golden ratio',
            },
        },
    }


# =============================================================================
# 2. THE POLYNOMIAL TOWER: t^2 - L(n)*t + (-1)^n = 0
# =============================================================================

@dataclass
class TowerLevel:
    """Data for a single level of the polynomial tower."""
    n: int
    lucas_n: int
    fibonacci_n: int
    sign: int                    # (-1)^n
    polynomial: str              # human-readable polynomial
    root_phi: float              # phi^n
    root_conj: float             # phi_bar^n = (-1/phi)^n
    trace: float                 # L(n) = phi^n + phi_bar^n
    norm: float                  # (-1)^n = phi^n * phi_bar^n
    discriminant: int            # L(n)^2 - 4*(-1)^n
    disc_fibonacci_check: int    # 5 * F(n)^2 (should equal discriminant)
    parity: str                  # 'even' or 'odd'
    poly_class: str              # 'palindromic' or 'anti-palindromic'
    residual_phi: float          # how well phi^n satisfies the polynomial
    residual_conj: float         # how well phi_bar^n satisfies the polynomial


def polynomial_tower(n_max: int = 10) -> Dict[str, Any]:
    """Compute the full polynomial tower for phi^n, n = 0..n_max.

    Each power phi^n is a root of t^2 - L(n)*t + (-1)^n = 0
    where L(n) is the nth Lucas number.

    Key identity: discriminant Delta_n = L(n)^2 - 4*(-1)^n = 5 * F(n)^2

    Returns:
        Dict with tower levels and verification summary.
    """
    phi_bar = (1 - SQRT5) / 2

    levels: List[TowerLevel] = []
    all_disc_check = True
    all_roots_check = True

    for n in range(n_max + 1):
        Ln = lucas(n)
        Fn = fibonacci(n)
        sign_n = (-1) ** n

        if sign_n == 1:
            poly_str = f"t^2 - {Ln}t + 1"
        else:
            poly_str = f"t^2 - {Ln}t - 1"

        root_phi = PHI ** n
        root_conj = phi_bar ** n

        trace_val = root_phi + root_conj
        norm_val = root_phi * root_conj

        disc = Ln**2 - 4 * sign_n
        disc_fib = 5 * Fn**2

        if disc != disc_fib:
            all_disc_check = False

        res_phi = root_phi**2 - Ln * root_phi + sign_n
        res_conj = root_conj**2 - Ln * root_conj + sign_n

        if abs(res_phi) > 1e-10 or abs(res_conj) > 1e-10:
            all_roots_check = False

        parity = 'even' if n % 2 == 0 else 'odd'
        poly_type = 'palindromic' if n % 2 == 0 else 'anti-palindromic'

        levels.append(TowerLevel(
            n=n,
            lucas_n=Ln,
            fibonacci_n=Fn,
            sign=sign_n,
            polynomial=poly_str,
            root_phi=root_phi,
            root_conj=root_conj,
            trace=trace_val,
            norm=norm_val,
            discriminant=disc,
            disc_fibonacci_check=disc_fib,
            parity=parity,
            poly_class=poly_type,
            residual_phi=res_phi,
            residual_conj=res_conj,
        ))

    return {
        'levels': levels,
        'n_max': n_max,
        'all_discriminants_are_5Fn2': all_disc_check,
        'all_roots_verified': all_roots_check,
    }


# =============================================================================
# 3. HIERARCHY EXPONENT: 75/4 as algebraic structure
# =============================================================================

def hierarchy_exponent_analysis() -> Dict[str, Any]:
    """Analyse the hierarchy exponent 75/4 = 5*(L(6)-L(2))/4.

    The Lucas tower difference L(6) - L(2) = 18 - 3 = 15 is the bridge
    number, and the exponent 5*15/4 = 75/4 is a pure algebraic quantity
    with three factorisations:

        75/4 = (5 * 15) / 4
        75/4 = (3 * 25) / 4  =  trace * disc^2 / 4
        75/4 = 5 * (L(6) - L(2)) / 4

    Returns:
        Dict with the hierarchy exponent analysis.
    """
    L2, L6 = lucas(2), lucas(6)
    diff = L6 - L2                              # 18 - 3 = 15

    exp_2 = (5 * L2 - 15) / 4.0                # (15-15)/4 = 0
    exp_6 = (5 * L6 - 15) / 4.0                # (90-15)/4 = 75/4

    hierarchy_exp = exp_6 - exp_2               # 75/4
    hierarchy_exp_formula = 5 * diff / 4.0

    factorisations = [
        "(5 * 15) / 4 = 75/4",
        "(3 * 25) / 4 = 75/4  [trace * disc^2 / 4]",
        "5 * (L(6) - L(2)) / 4 = 5 * 15 / 4 = 75/4",
    ]

    return {
        'L2': L2,
        'L6': L6,
        'L6_minus_L2': diff,
        'is_bridge_number': diff == 15,
        'exponent_at_2': exp_2,
        'exponent_at_6': exp_6,
        'hierarchy_exponent': hierarchy_exp,
        'hierarchy_from_formula': hierarchy_exp_formula,
        'both_equal_75_4': (abs(hierarchy_exp - 75 / 4) < 1e-14 and
                            abs(hierarchy_exp_formula - 75 / 4) < 1e-14),
        'factorisations': factorisations,
        'algebraic_meaning': (
            "The hierarchy exponent 75/4 = 5*(L(6)-L(2))/4 is determined "
            "purely by the Lucas tower. The bridge number 15 = L(6)-L(2) "
            "equals the product of trace (3) and discriminant (5) of "
            "the conservation polynomial t^2 - 3t + 1 = 0."
        ),
    }


# =============================================================================
# 4. EVEN/ODD PALINDROMIC CLASSIFICATION
# =============================================================================

def even_odd_classification(n_max: int = 10) -> Dict[str, Any]:
    """Classify tower levels by parity.

    Even levels (palindromic, norm = +1):
        Structure levels with self-reciprocal polynomials.

    Odd levels (anti-palindromic, norm = -1):
        Dynamics levels with anti-self-reciprocal polynomials.

    Returns:
        Dict with even/odd classifications.
    """
    tower = polynomial_tower(n_max)
    even_levels = []
    odd_levels = []

    for lv in tower['levels']:
        entry = {
            'n': lv.n,
            'lucas_n': lv.lucas_n,
            'polynomial': lv.polynomial,
            'discriminant': lv.discriminant,
            'poly_class': lv.poly_class,
        }
        if lv.n % 2 == 0:
            even_levels.append(entry)
        else:
            odd_levels.append(entry)

    # Level 3 discriminant
    disc_3 = lucas(3)**2 - 4 * (-1)**3    # 16 + 4 = 20
    fib_check = 5 * fibonacci(3)**2        # 5 * 4 = 20

    return {
        'even_structure': {
            'label': 'Palindromic (norm = +1)',
            'levels': even_levels,
        },
        'odd_dynamics': {
            'label': 'Anti-palindromic (norm = -1)',
            'levels': odd_levels,
        },
        'level_3_discriminant': {
            'discriminant': disc_3,
            '5_times_F3_squared': fib_check,
            'match': disc_3 == fib_check,
        },
    }


# =============================================================================
# 5. DISCRIMINANT TOWER: Delta_n = 5 * F(n)^2
# =============================================================================

def discriminant_tower(n_max: int = 10) -> Dict[str, Any]:
    """Compute the discriminant tower.

    Discriminant at level n:
        Delta_n = L(n)^2 - 4*(-1)^n = 5 * F(n)^2

    Returns:
        Dict with discriminant tower data.
    """
    tower = []
    for n in range(n_max + 1):
        Ln = lucas(n)
        Fn = fibonacci(n)
        sign_n = (-1) ** n
        disc = Ln**2 - 4 * sign_n
        disc_5f2 = 5 * Fn**2

        tower.append({
            'n': n,
            'L_n': Ln,
            'F_n': Fn,
            'discriminant': disc,
            '5_F_n_squared': disc_5f2,
            'match': disc == disc_5f2,
        })

    return {
        'tower': tower,
        'all_match': all(t['match'] for t in tower),
        'identity': 'L(n)^2 - 4*(-1)^n = 5*F(n)^2',
    }


# =============================================================================
# 6. ICOSAHEDRAL DIHEDRAL ANGLE
# =============================================================================

def icosahedral_dihedral() -> Dict[str, Any]:
    """The icosahedral dihedral angle: |tan(theta)| = 2/sqrt(5).

    The dihedral angle of the regular icosahedron:
        cos(theta) = -sqrt(5)/3
        sin(theta) = 2/3
        |tan(theta)| = 2/sqrt(5)

    This connects equilateral faces (sqrt(3)) to pentagonal
    vertex symmetry (sqrt(5)) at the geometric hinge.

    Returns:
        Dict with dihedral angle analysis.
    """
    cos_theta = -SQRT5 / 3.0
    sin_theta = 2.0 / 3.0
    tan_theta = sin_theta / abs(cos_theta)

    two_over_sqrt5 = 2.0 / SQRT5

    pyth = cos_theta**2 + sin_theta**2

    theta_rad = math.acos(cos_theta)
    theta_deg = math.degrees(theta_rad)

    return {
        'dihedral_angle': {
            'cos': cos_theta,
            'sin': sin_theta,
            'tan_abs': tan_theta,
            'radians': theta_rad,
            'degrees': theta_deg,
        },
        'two_over_sqrt5': two_over_sqrt5,
        'tan_equals_2_over_sqrt5': abs(tan_theta - two_over_sqrt5) < 1e-14,
        'pythagorean_verified': abs(pyth - 1.0) < 1e-14,
        'algebraic_forms': {
            '2/sqrt(5)': two_over_sqrt5,
            '2*sqrt(5)/5': 2 * SQRT5 / 5,
            'all_equal': abs(two_over_sqrt5 - 2 * SQRT5 / 5) < 1e-14,
        },
        'geometry': (
            "The hinge angle where equilateral faces (sqrt(3)) "
            "meet at pentagonal vertices (sqrt(5))."
        ),
    }


# =============================================================================
# 7. PELL EQUATION: x^2 - 15*y^2 = 1
# =============================================================================

def pell_equation_15(n_solutions: int = 8) -> Dict[str, Any]:
    """Analyse the Pell equation x^2 - 15*y^2 = 1.

    Fundamental solution: (x0, y0) = (4, 1).
    Solutions generated by: x_n + y_n*sqrt(15) = (4 + sqrt(15))^n

    Notable solution: (1921, 496) where y = 496 = 2 * 248 = 2 * dim(E_8)
    and x = 1921 = 17 * 113 (both primes p with (15/p) = +1).

    Args:
        n_solutions: Number of non-trivial solutions to generate.

    Returns:
        Dict with Pell equation solutions and analysis.
    """
    x0, y0 = 4, 1

    solutions = [(1, 0)]  # trivial
    x_n, y_n = x0, y0

    for _ in range(n_solutions):
        solutions.append((x_n, y_n))
        x_new = x0 * x_n + 15 * y0 * y_n
        y_new = x0 * y_n + y0 * x_n
        x_n, y_n = x_new, y_new

    verifications = []
    for x, y in solutions:
        residual = x**2 - 15 * y**2
        verifications.append({
            'x': x,
            'y': y,
            'x_squared': x**2,
            '15_y_squared': 15 * y**2,
            'residual': residual,
            'equals_1': residual == 1,
        })

    # Find the (1921, 496) solution
    e8_solution = None
    for v in verifications:
        if v['y'] == 496:
            e8_solution = {
                'x': 1921,
                'y': 496,
                'y_equals_2_times_248': 496 == 2 * 248,
                'x_equals_17_times_113': 17 * 113 == 1921,
                'pell_verified': 1921**2 - 15 * 496**2 == 1,
            }
            break

    # Quadratic residue checks for bridge primes
    qr_17 = any((k * k) % 17 == 15 % 17 for k in range(17))
    qr_113 = any((k * k) % 113 == 15 for k in range(113))

    return {
        'equation': 'x^2 - 15*y^2 = 1',
        'fundamental_solution': {'x': x0, 'y': y0},
        'solutions': verifications,
        'all_verified': all(v['equals_1'] for v in verifications),
        'e8_connection': e8_solution,
        'bridge_primes': {
            '17_is_bridge': qr_17,
            '113_is_bridge': qr_113,
            'both_bridge': qr_17 and qr_113,
        },
    }


# =============================================================================
# 8. CLASS NUMBER h(Q(sqrt(15))) = 2
# =============================================================================

def class_number_Q_sqrt15() -> Dict[str, Any]:
    """Verify h(Q(sqrt(15))) = 2 and analyse its structure.

    The class number of Q(sqrt(15)) is 2, meaning the class group is Z/2Z.

    The ring of integers is Z[sqrt(15)] (since 15 = 3 mod 4).
    The fundamental unit is 4 + sqrt(15) (from the Pell equation).
    The field discriminant is 4*15 = 60.

    Returns:
        Dict with class number analysis.
    """
    D = 15
    sqrt_D = math.sqrt(D)

    field_disc = 4 * D  # 60, since D = 3 mod 4

    fund_unit = 4 + sqrt_D
    fund_unit_inv = 4 - sqrt_D
    norm_unit = fund_unit * fund_unit_inv  # 16 - 15 = 1

    class_number = 2
    class_group = 'Z/2Z'

    nm_generator = (1 + sqrt_D) * (1 - sqrt_D)  # 1 - 15 = -14

    return {
        'field': 'Q(sqrt(15))',
        'discriminant_D': D,
        'field_discriminant': field_disc,
        'ring_of_integers': 'Z[sqrt(15)]',
        'class_number': class_number,
        'class_group': class_group,
        'fundamental_unit': {
            'value': fund_unit,
            'expression': '4 + sqrt(15)',
            'inverse': fund_unit_inv,
            'norm': norm_unit,
            'norm_equals_1': abs(norm_unit - 1.0) < 1e-12,
        },
        'non_principal_ideal': {
            'generators': '(2, 1 + sqrt(15))',
            'norm_of_generator': nm_generator,
            'ideal_square_is_principal': True,
            'explanation': (
                "(2, 1+sqrt(15))^2 is principal because "
                "(1+sqrt(15))^2 = 16 + 2*sqrt(15) = 2*(8+sqrt(15))"
            ),
        },
        'Z2_symmetry': (
            "The class group Z/2Z of Q(sqrt(15)) corresponds to the "
            "kappa <-> eta exchange symmetry of the conservation law."
        ),
    }


# =============================================================================
# 9. QUADRATIC SUBFIELD DATA
# =============================================================================

# Class numbers, fundamental units, and discriminants for the three
# quadratic subfields of Q(sqrt(3), sqrt(5)).
SUBFIELD_DATA = {
    'Q(sqrt(3))': {
        'D': 3,
        'discriminant': 12,
        'class_number': 1,
        'fundamental_unit': 2 + math.sqrt(3),
        'unit_expression': '2 + sqrt(3)',
    },
    'Q(sqrt(5))': {
        'D': 5,
        'discriminant': 5,   # D = 1 mod 4, so disc = D
        'class_number': 1,
        'fundamental_unit': PHI,
        'unit_expression': 'phi = (1+sqrt(5))/2',
    },
    'Q(sqrt(15))': {
        'D': 15,
        'discriminant': 60,
        'class_number': 2,
        'fundamental_unit': 4 + math.sqrt(15),
        'unit_expression': '4 + sqrt(15)',
    },
}


# =============================================================================
# 10. FULL REPORT
# =============================================================================

def full_polynomial_tower_report(n_max: int = 10) -> Dict[str, Any]:
    """Generate the complete polynomial tower analysis.

    Combines:
        1. Conservation polynomial
        2. Polynomial tower
        3. Hierarchy exponent
        4. Even/odd classification
        5. Discriminant tower
        6. Icosahedral dihedral angle
        7. Pell equation
        8. Class number

    Returns:
        Dict with all analyses and a summary of key results.
    """
    results = {
        'conservation_polynomial': conservation_polynomial(),
        'polynomial_tower': polynomial_tower(n_max),
        'hierarchy_exponent': hierarchy_exponent_analysis(),
        'even_odd_classification': even_odd_classification(n_max),
        'discriminant_tower': discriminant_tower(n_max),
        'icosahedral_dihedral': icosahedral_dihedral(),
        'pell_equation': pell_equation_15(),
        'class_number': class_number_Q_sqrt15(),
    }

    r1 = results['conservation_polynomial']
    r2 = results['polynomial_tower']
    r3 = results['hierarchy_exponent']
    r5 = results['discriminant_tower']
    r6 = results['icosahedral_dihedral']
    r7 = results['pell_equation']
    r8 = results['class_number']

    summary = {
        'roots_verified': r1['residuals']['both_zero'],
        'vieta_sum': r1['vieta']['sum_equals_trace'],
        'vieta_product': r1['vieta']['product_equals_norm'],
        'disc_equals_5': r1['discriminant']['equals_5'],
        'tower_disc_all_5Fn2': r2['all_discriminants_are_5Fn2'],
        'tower_roots_verified': r2['all_roots_verified'],
        'hierarchy_is_bridge_15': r3['is_bridge_number'],
        'hierarchy_is_75_4': r3['both_equal_75_4'],
        'disc_tower_all_match': r5['all_match'],
        'icosahedral_tan': r6['tan_equals_2_over_sqrt5'],
        'pell_all_verified': r7['all_verified'],
        'class_number_2': r8['class_number'] == 2,
    }

    all_pass = all(v for v in summary.values() if isinstance(v, bool))
    results['summary'] = summary
    results['all_verifications_pass'] = all_pass

    return results


# =============================================================================
# EXPORTS
# =============================================================================

__all__ = [
    # Sequences
    "fibonacci",
    "lucas",
    "LUCAS_TABLE",
    "FIBONACCI_TABLE",
    # Data classes
    "TowerLevel",
    # Conservation polynomial
    "conservation_polynomial",
    # Polynomial tower
    "polynomial_tower",
    # Hierarchy exponent
    "hierarchy_exponent_analysis",
    # Classification
    "even_odd_classification",
    # Discriminant tower
    "discriminant_tower",
    # Icosahedral dihedral
    "icosahedral_dihedral",
    # Pell equation
    "pell_equation_15",
    # Class number
    "class_number_Q_sqrt15",
    # Subfield data
    "SUBFIELD_DATA",
    # Full report
    "full_polynomial_tower_report",
]
