"""
I-Logic 4-State Machine - 3D Unit Sphere Model
Standalone version for cubesquare package.

Implements I-Logic 4-state system with O(1) operations.
"""

from enum import Enum
from dataclasses import dataclass
from typing import Tuple
import math

# Constants
PHI = 1.618033988749895
MATRIX_SIZE = 48
MATRIX_CENTER = 23.5
K11_MODULUS = 23
LIGHT_UNIT = 131


class ILogicState(Enum):
    """
    I-Logic 4-state system on complex plane unit circle.

    States:
    - DIRECT: (+1, 0) - Identity transform
    - INVERSE: (-1, 0) - Negation
    - SPIRAL: (0, +1) - +90 degree rotation
    - ANTI_SPIRAL: (0, -1) - -90 degree rotation
    - ABSORBED: (0, 0) - Origin/C=0 Fernandez sphere (energy transforms, not destroyed)
    """
    DIRECT = (1, 0)
    INVERSE = (-1, 0)
    SPIRAL = (0, 1)
    ANTI_SPIRAL = (0, -1)
    ABSORBED = (0, 0)

    def to_complex(self) -> complex:
        """Convert to Python complex number."""
        return complex(self.value[0], self.value[1])

    def magnitude(self) -> float:
        """Compute |z| (magnitude on unit circle)."""
        re, im = self.value
        return (re * re + im * im) ** 0.5

    def is_stable(self) -> bool:
        """Check if state is stable (DIRECT or origin-collapsed ABSORBED)."""
        return self in (ILogicState.DIRECT, ILogicState.ABSORBED)


def get_k11_level(x: int, y: int, z: int) -> int:
    """
    Compute K11 level from 3D coordinates.
    Formula: K11 = (x + y + z) % 23 - 11
    Range: [-11, +11]
    """
    coord_sum = x + y + z
    return (coord_sum % K11_MODULUS) - 11


def get_intrinsic_state_from_k11(k11_level: int, z_polarity: int = 0) -> ILogicState:
    """
    Derive I-Logic state from K11 level and z-polarity.

    K11 mod 4 mapping:
    - 0 -> ABSORBED (origin-collapse at C=0, energy conserved)
    - 1 -> DIRECT
    - 2 -> INVERSE
    - 3 -> SPIRAL (+i) if z >= center, ANTI_SPIRAL (-i) if z < center
    """
    level_mod = ((k11_level % 4) + 4) % 4

    if level_mod == 0:
        return ILogicState.ABSORBED
    elif level_mod == 1:
        return ILogicState.DIRECT
    elif level_mod == 2:
        return ILogicState.INVERSE
    else:
        if z_polarity >= MATRIX_CENTER:
            return ILogicState.SPIRAL
        else:
            return ILogicState.ANTI_SPIRAL


def get_phi_norm_distance(x: int, y: int, z: int) -> float:
    """Compute phi-normalized distance from center on 3D unit sphere."""
    dx = x - MATRIX_CENTER
    dy = y - MATRIX_CENTER
    dz = z - MATRIX_CENTER

    distance = math.sqrt(dx*dx + dy*dy + dz*dz)
    max_distance = math.sqrt(3.0 * MATRIX_CENTER * MATRIX_CENTER)

    return (distance / max_distance) * PHI if max_distance > 0 else 0.0


def get_sphere_coords(x: int, y: int, z: int) -> Tuple[float, float, float]:
    """Get normalized (a, b, c) position on unit sphere."""
    a = (x - MATRIX_CENTER) / MATRIX_CENTER
    b = (y - MATRIX_CENTER) / MATRIX_CENTER
    c = (z - MATRIX_CENTER) / MATRIX_CENTER

    r_sq = a*a + b*b + c*c
    if r_sq > 1.0:
        scale = 1.0 / math.sqrt(r_sq)
        a *= scale
        b *= scale
        c *= scale

    return (a, b, c)


def get_wave_particle_strength(x: int, y: int, z: int) -> Tuple[float, float]:
    """Compute wave and particle strength at coordinate."""
    a, b, c = get_sphere_coords(x, y, z)
    r_sq = a*a + b*b + c*c

    if r_sq < 0.001:
        return (1000.0, 0.001)

    wave = 1.0 / r_sq
    particle = r_sq

    return (wave, particle)


def hex_to_coords(hex_id: str) -> Tuple[int, int, int]:
    """
    Extract (x, y, z) coordinates from hex ID.
    Hex ID format: AACCLLII (Area, Category, Layer, Index)
    """
    area = int(hex_id[0:2], 16) if len(hex_id) >= 2 else 0
    category = int(hex_id[2:4], 16) if len(hex_id) >= 4 else 0
    layer = int(hex_id[4:6], 16) if len(hex_id) >= 6 else 0

    x = area % MATRIX_SIZE
    y = category % MATRIX_SIZE
    z = layer % MATRIX_SIZE

    return (x, y, z)


# Multiplication table (O(1) lookup)
_STATE_MULTIPLY_TABLE = {
    (ILogicState.DIRECT, ILogicState.DIRECT): ILogicState.DIRECT,
    (ILogicState.DIRECT, ILogicState.INVERSE): ILogicState.INVERSE,
    (ILogicState.DIRECT, ILogicState.SPIRAL): ILogicState.SPIRAL,
    (ILogicState.DIRECT, ILogicState.ANTI_SPIRAL): ILogicState.ANTI_SPIRAL,
    (ILogicState.DIRECT, ILogicState.ABSORBED): ILogicState.ABSORBED,

    (ILogicState.INVERSE, ILogicState.DIRECT): ILogicState.INVERSE,
    (ILogicState.INVERSE, ILogicState.INVERSE): ILogicState.DIRECT,
    (ILogicState.INVERSE, ILogicState.SPIRAL): ILogicState.ANTI_SPIRAL,
    (ILogicState.INVERSE, ILogicState.ANTI_SPIRAL): ILogicState.SPIRAL,
    (ILogicState.INVERSE, ILogicState.ABSORBED): ILogicState.ABSORBED,

    (ILogicState.SPIRAL, ILogicState.DIRECT): ILogicState.SPIRAL,
    (ILogicState.SPIRAL, ILogicState.INVERSE): ILogicState.ANTI_SPIRAL,
    (ILogicState.SPIRAL, ILogicState.SPIRAL): ILogicState.INVERSE,
    (ILogicState.SPIRAL, ILogicState.ANTI_SPIRAL): ILogicState.DIRECT,
    (ILogicState.SPIRAL, ILogicState.ABSORBED): ILogicState.ABSORBED,

    (ILogicState.ANTI_SPIRAL, ILogicState.DIRECT): ILogicState.ANTI_SPIRAL,
    (ILogicState.ANTI_SPIRAL, ILogicState.INVERSE): ILogicState.SPIRAL,
    (ILogicState.ANTI_SPIRAL, ILogicState.SPIRAL): ILogicState.DIRECT,
    (ILogicState.ANTI_SPIRAL, ILogicState.ANTI_SPIRAL): ILogicState.INVERSE,
    (ILogicState.ANTI_SPIRAL, ILogicState.ABSORBED): ILogicState.ABSORBED,

    (ILogicState.ABSORBED, ILogicState.DIRECT): ILogicState.ABSORBED,
    (ILogicState.ABSORBED, ILogicState.INVERSE): ILogicState.ABSORBED,
    (ILogicState.ABSORBED, ILogicState.SPIRAL): ILogicState.ABSORBED,
    (ILogicState.ABSORBED, ILogicState.ANTI_SPIRAL): ILogicState.ABSORBED,
    (ILogicState.ABSORBED, ILogicState.ABSORBED): ILogicState.ABSORBED,
}


@dataclass
class StateTransition:
    """Result of state transition."""
    from_state: ILogicState
    to_state: ILogicState
    is_stable: bool
    magnitude_change: float
    converges_to_absorbed: bool


class ILogicStateMachine:
    """I-Logic 4-state machine with O(1) operations."""

    def __init__(self):
        """Initialize state machine."""
        pass

    def transition(self, current: ILogicState, input_state: ILogicState) -> StateTransition:
        """Perform state transition via multiplication."""
        new_state = _STATE_MULTIPLY_TABLE[(current, input_state)]
        mag_old = current.magnitude()
        mag_new = new_state.magnitude()

        return StateTransition(
            from_state=current,
            to_state=new_state,
            is_stable=new_state.is_stable(),
            magnitude_change=mag_new - mag_old,
            converges_to_absorbed=(mag_new < mag_old),
        )

    def multiply(self, state1: ILogicState, state2: ILogicState) -> ILogicState:
        """Multiply two states."""
        return _STATE_MULTIPLY_TABLE[(state1, state2)]

    def is_stable(self, state: ILogicState) -> bool:
        """Check if state is stable."""
        return state.is_stable()


def get_state_from_hex_area(hex_id: str) -> ILogicState:
    """Determine I-Logic state from hex ID coordinates."""
    x, y, z = hex_to_coords(hex_id)
    k11 = get_k11_level(x, y, z)
    return get_intrinsic_state_from_k11(k11, z)


def get_intrinsic_stability(hex_id: str) -> dict:
    """Get complete intrinsic state of a hex ID."""
    x, y, z = hex_to_coords(hex_id)
    a, b, c = get_sphere_coords(x, y, z)
    k11 = get_k11_level(x, y, z)
    wave, particle = get_wave_particle_strength(x, y, z)
    state = get_intrinsic_state_from_k11(k11, z)
    phi_norm = get_phi_norm_distance(x, y, z)
    polarity = 1 if c >= 0 else -1

    return {
        'hex_id': hex_id,
        'coords': (x, y, z),
        'sphere_position': (a, b, c),
        'k11_level': k11,
        'state': state,
        'state_name': state.name,
        'wave_strength': wave,
        'particle_strength': particle,
        'polarity': polarity,
        'phi_norm': phi_norm,
        'is_stable': state.is_stable(),
    }


def is_intrinsically_stable(hex_id: str) -> bool:
    """Check if hex ID is intrinsically stable."""
    state = get_state_from_hex_area(hex_id)
    return state.is_stable()


def get_stability_from_loop(loop_hex_ids: list) -> ILogicState:
    """Determine overall stability of hex ID loop."""
    from collections import Counter
    states = [get_state_from_hex_area(hid) for hid in loop_hex_ids]
    state_counts = Counter(states)
    dominant_state = state_counts.most_common(1)[0][0]
    return dominant_state
