"""
Telemetry Shell - Agent Telemetry and State Prediction
HEX_ID: 50483354 (PH3T - Telemetry)

PURE PYTHON VERSION - NO OLLAMA, NO LLM DEPENDENCIES

This is a standalone telemetry shell that provides:
- Pattern-based message interpretation
- Anomaly detection
- State prediction via SRL trajectory
- Semantic routing

INPUTS:
    - Natural language messages
    - Hex IDs (8-char strings)
    - Agent coordinates (x, y, z in 0-47)
    - Basin numbers (0-130)
    - K11 levels (-11 to +11)
    - I-Logic states (DIRECT, INVERSE, SPIRAL, ANTI_SPIRAL, ABSORBED)

OUTPUTS:
    - TelemetryEvent: event_type, hex_id, timestamp_ns, basin, layer, i_logic, k11_level, payload
    - AgentTelemetry: hex_id, coords, basin, layer, srl_state, k11_level, phi_norm, coherence
    - StatePredictor results: predicted states with probabilities
    - AnomalyDetector results: detected anomalies with severity
    - SemanticRouter results: shell, command, args, basin, layer, confidence

COMMANDS:
    --status        System status
    --interpret MSG Interpret natural language message
    --predict HEX   Predict state trajectory for hex ID
    --route MSG     Route message to appropriate shell
    --anomalies     Get detected anomalies
    --health        Get health summary

I-LOGIC STATES (SRL 4-state):
    DIRECT (1)      : Identity preservation
    INVERSE (-1)    : Polarity flip / healing
    SPIRAL (+i)     : Recursive expansion
    ANTI_SPIRAL (-i): Convergence / collapse
    ABSORBED (0)    : Neutral state

NO LLM INTEGRATION - Pure pattern-based interpretation.
"""

import hashlib
import math
import time
from collections import deque
from dataclasses import dataclass, field
from enum import Enum, IntEnum
from typing import Dict, List, Optional, Any, Tuple
import threading
import json

# Import from cubesquare core (C backend: 0.009ns Morton)
from .._core.constants import PHI, K5, K11, PRIME_131, MATRIX_SIZE
from .._core import (
    morton_encode as _core_morton_encode,
    morton_decode as _core_morton_decode,
    compute_basin as _core_compute_basin,
    process_coordinate as _core_process_coordinate,
    srl_sqrt as _core_srl_sqrt,
)

# Import I-Logic algebra — the transition IS the multiplication
from ..i_logic.states import ILogicState
from ..i_logic.algebra import multiply as _ilogic_multiply


# ==============================================================================
# CONSTANTS
# ==============================================================================

PHI_INVERSE = 0.618033988749895
MATRIX_CELLS = MATRIX_SIZE ** 3  # 110,592
BASE_FREQUENCY = 562.0  # Hz
MEMBRANE_FREQUENCY = 0x83  # 131 Hz
TELEMETRY_HEX = "50483354"


# ==============================================================================
# DATA STRUCTURES
# ==============================================================================

class SRLState(Enum):
    """SRL Four-State System."""
    DIRECT = (1, "DIRECT", [1, 0, 0, 0], "Identity preservation")
    INVERSE = (-1, "INVERSE", [0, 1, 0, 0], "Polarity flip / healing")
    SPIRAL = (2, "SPIRAL", [0, 0, 1, 0], "Recursive expansion")
    CONVERGENT = (3, "CONVERGENT", [0, 0, 0, 1], "Basin attraction")
    ABSORBED = (0, "ABSORBED", [0, 0, 0, 0], "Neutral state")

    def __init__(self, i_val: int, name: str, vector: List[int], desc: str):
        self._i_value = i_val
        self._state_name = name
        self._vector = vector
        self._description = desc

    @property
    def i_value(self) -> int:
        return self._i_value

    @property
    def state_name(self) -> str:
        return self._state_name

    @property
    def vector(self) -> List[int]:
        return self._vector.copy()

    @property
    def description(self) -> str:
        return self._description


class TelemetryEventType(IntEnum):
    """Telemetry event types."""
    MESSAGE = 0x01
    ANOMALY = 0x02
    STATE_CHANGE = 0x03
    PREDICTION = 0x04
    ROUTE = 0x05
    COUNCIL_VOTE = 0x06
    AGENT_SPAWN = 0x07
    AGENT_TERMINATE = 0x08
    COHERENCE_DROP = 0x09
    HEALING = 0x0A
    LAYER_TRANSITION = 0x0B


@dataclass
class TelemetryEvent:
    """Single telemetry event with CST token."""
    event_type: TelemetryEventType
    hex_id: str
    timestamp_ns: int
    basin: int
    layer: int
    i_logic: SRLState
    k11_level: int
    payload: Dict[str, Any]
    cst_token: str = ""

    def __post_init__(self):
        if not self.cst_token:
            self.cst_token = self._generate_token()

    def _generate_token(self) -> str:
        """Generate deterministic 16-char CST token via C backend Morton."""
        content = f"{self.hex_id}:{self.event_type}:{self.timestamp_ns}"
        h = hashlib.sha256(content.encode()).digest()
        x, y, z = h[0] % MATRIX_SIZE, h[1] % MATRIX_SIZE, h[2] % MATRIX_SIZE
        morton = _core_morton_encode(x, y, z)  # C backend (0.009ns BMI2)
        basin = morton % PRIME_131
        diagonal = x + y + z
        phi_depth = int(diagonal / PHI)
        packed = (morton << 32) | (basin << 24) | (phi_depth << 16) | (diagonal & 0xFFFF)
        return f"{packed:016X}"

    def to_dict(self) -> Dict:
        """Convert to dictionary."""
        return {
            'event_type': self.event_type.name,
            'event_code': int(self.event_type),
            'hex_id': self.hex_id,
            'timestamp_ns': self.timestamp_ns,
            'basin': self.basin,
            'layer': self.layer,
            'i_logic': self.i_logic.state_name,
            'i_vector': self.i_logic.vector,
            'k11_level': self.k11_level,
            'cst_token': self.cst_token,
            'payload': self.payload
        }


@dataclass
class AgentTelemetry:
    """Telemetry state for an agent."""
    hex_id: str
    morton_level: int  # 1-4 in hierarchy
    agent_type: str
    coords: Tuple[int, int, int]
    basin: int
    layer: int
    srl_state: SRLState
    k11_level: int
    phi_norm: float
    em_coupling: float
    coherence: float
    last_event: Optional[TelemetryEvent] = None
    event_history: List[TelemetryEvent] = field(default_factory=list)

    @classmethod
    def from_hex(cls, hex_id: str, morton_level: int = 2, agent_type: str = "worker"):
        """Create agent telemetry from hex ID."""
        h = int(hex_id, 16)
        x = (h >> 24) % MATRIX_SIZE
        y = (h >> 16) % MATRIX_SIZE
        z = (h >> 8) % MATRIX_SIZE
        basin = h % PRIME_131
        layer = (h % 9) + 1
        k11 = ((x + y + z) % 23) - 11

        # Map K11 to SRL state
        level_mod = ((k11 % 4) + 4) % 4
        i_state_val = [0, 1, -1, 2][level_mod]
        srl_map = {0: SRLState.ABSORBED, 1: SRLState.DIRECT, -1: SRLState.INVERSE, 2: SRLState.SPIRAL}
        srl = srl_map.get(i_state_val, SRLState.DIRECT)

        # Phi norm (distance from center normalized by PHI)
        center = (MATRIX_SIZE - 1) / 2.0
        dx, dy, dz = x - center, y - center, z - center
        dist = math.sqrt(dx*dx + dy*dy + dz*dz)
        max_dist = math.sqrt(3.0 * center * center)
        phi_norm = (dist / max_dist) * PHI

        # EM coupling
        dist_factor = 1.0 + phi_norm / MATRIX_SIZE
        em_coupling = K5 * K11 * dist_factor

        return cls(
            hex_id=hex_id,
            morton_level=morton_level,
            agent_type=agent_type,
            coords=(x, y, z),
            basin=basin,
            layer=layer,
            srl_state=srl,
            k11_level=k11,
            phi_norm=phi_norm,
            em_coupling=em_coupling,
            coherence=0.7
        )


# ==============================================================================
# MESSAGE INTERPRETER (FML-Coordinate + Pattern)
# ==============================================================================

class MessageInterpreter:
    """
    Natural Language -> Hex Protocol translator.

    The message IS a body. Encoding it via FML places it at a coordinate
    with mass, basin, layer, I-Logic state. The coordinate IS the
    interpretation — no external service needed.

    Any LLM is just another agent at a coordinate. Communication between
    the message-body and any agent happens through UIF coupling:
        φ(A,B) = K5 × K11 × cos(2π × basin_a/131) × cos(2π × basin_b/131)

    The I-Logic flip is INSIDE the coordinate space:
        message_state × agent_state = outcome
    That outcome IS the routing decision.
    """

    # Hex protocol patterns
    HEX_PATTERNS = {
        '?': 'query',      # ?HEX -> Query topic
        '@': 'reference',  # @HEX -> Reference (no expand)
        '+': 'batch',      # +HEX,HEX -> Batch query
        '!': 'response',   # !HEX:K# -> Response at K-level
        '$': 'encode',     # $TEXT -> Text to hex
        '^': 'heal',       # ^HEX -> Heal token
        '*': 'observe',    # *HEX -> Collapse state
    }

    # Intent to basin mapping
    INTENT_BASIN_MAP = {
        'gene': 23, 'genetics': 23, 'breed': 23, 'reproduce': 23,
        'morton': 47, 'spatial': 47, 'coordinate': 47, 'position': 47,
        'mcp': 89, 'agent': 89, 'tool': 89, 'communication': 89,
        'cst': 65, 'layer': 65, 'structure': 65, 'token': 65,
        'beehive': 19, 'index': 19, 'documentation': 19,
        'memory': 101, 'orchestration': 101, 'task': 101,
        'identity': 127, 'membrane': 127, 'boundary': 127,
        'performance': 12, 'c_backend': 12, 'optimization': 12,
        'healing': 78, 'coherence': 78, 'fernandez': 78,
        'stuck': 99, 'blocked': 99, 'waiting': 99, 'timeout': 99,
        'error': 110, 'failed': 110, 'broken': 110, 'corrupted': 110,
    }

    # I-Logic vocabulary for NL generation
    I_LOGIC_VOCAB = {
        SRLState.DIRECT: {
            'verbs': ['maintain', 'preserve', 'keep', 'stabilize'],
            'adjectives': ['stable', 'consistent', 'reliable'],
            'tone': 'assertive'
        },
        SRLState.INVERSE: {
            'verbs': ['reverse', 'disconnect', 'remove', 'heal'],
            'adjectives': ['isolated', 'orphaned', 'broken'],
            'tone': 'cautionary'
        },
        SRLState.SPIRAL: {
            'verbs': ['expand', 'grow', 'elevate', 'abstract'],
            'adjectives': ['growing', 'hierarchical', 'evolving'],
            'tone': 'enthusiastic'
        },
        SRLState.CONVERGENT: {
            'verbs': ['consolidate', 'merge', 'unify', 'cluster'],
            'adjectives': ['related', 'grouped', 'connected'],
            'tone': 'focused'
        },
        SRLState.ABSORBED: {
            'verbs': ['observe', 'note', 'accept', 'acknowledge'],
            'adjectives': ['neutral', 'balanced', 'centered'],
            'tone': 'neutral'
        }
    }

    def __init__(self):
        self._fml_encoder = None

    def _get_fml(self):
        """Lazy-load FML encoder."""
        if self._fml_encoder is None:
            try:
                from .fml import FMLEncoder
                self._fml_encoder = FMLEncoder()
            except ImportError:
                pass
        return self._fml_encoder

    @property
    def fml_available(self) -> bool:
        """Whether FML coordinate encoding is available."""
        return self._get_fml() is not None

    def nl_to_hex(self, message: str) -> Dict[str, Any]:
        """
        Convert natural language to hex protocol command.

        The message is encoded via FML — it becomes a body at a coordinate.
        The coordinate determines basin, layer, I-Logic state.
        Pattern matching provides intent classification.
        The body's coupling to agents determines routing.

        Returns:
            {
                'hex_command': '?97038FBF',
                'intent': 'query',
                'basin': 23,
                'layer': 4,
                'confidence': 0.7,
                'method': 'coordinate'
            }
        """
        # Pattern matching for intent (query/heal/encode/reference)
        pattern_result = self._pattern_nl_to_hex(message)

        # Encode the message as a body — the coordinate IS the interpretation
        encoder = self._get_fml()
        if encoder is not None:
            token = encoder.encode(message)

            # The FML token overwrites basin/layer with ground truth
            # from the coordinate space. The pattern only provides intent.
            h = hashlib.md5(message.encode()).hexdigest()[:8].upper()
            intent = pattern_result.get('intent', 'query')
            prefix = {'query': '?', 'heal': '^', 'reference': '@', 'encode': '$'}.get(intent, '?')

            # I-Logic state from L4 — the flip is inside the coordinates
            ilogic_state = token.l4_state  # DIRECT/INVERSE/SPIRAL/ABSORBED

            return {
                'hex_command': f"{prefix}{h}",
                'intent': intent,
                'basin': token.l1_basin,
                'layer': min(9, token.l1_basin // 15 + 1),
                'confidence': 0.9,  # coordinate-based = high confidence
                'method': 'coordinate',
                'body': {
                    'hex_id': token.hex_id,
                    'mass': round(token.mass, 4),
                    'content_mass': round(token.content_mass, 4),
                    'ilogic_state': ilogic_state,
                    'membrane_action': token.membrane_action,
                    'em_coupling': round(token.em_coupling, 4),
                    'l10_self_id': token.l10_self_id,
                    'l12_uif_strength': round(token.l12_uif_strength, 4),
                },
            }

        # Fallback: no FML available, pure pattern
        return pattern_result

    # Intent prefix map — O(1) dict lookup, no iteration
    _INTENT_PREFIX = {'query': '?', 'heal': '^', 'reference': '@', 'encode': '$'}

    # Intent detection via hex byte — the hash byte IS the intent
    # Byte ranges map to intent: 0x00-0x3F=query, 0x40-0x7F=heal,
    # 0x80-0xBF=reference, 0xC0-0xFF=encode
    _INTENT_FROM_BYTE = ('query', 'heal', 'reference', 'encode')

    def _pattern_nl_to_hex(self, message: str) -> Dict[str, Any]:
        """Coordinate-based NL to Hex conversion.

        The message hash IS coordinates. compute_basin(x, y, z) IS the basin.
        No keyword dict, no iteration — the body determines its own position.
        """
        # Hash the message — the bytes ARE coordinates
        h_bytes = hashlib.md5(message.encode()).digest()
        h = hashlib.md5(message.encode()).hexdigest()[:8].upper()

        # Coordinates from hash bytes — deterministic position
        x = h_bytes[0] % MATRIX_SIZE
        y = h_bytes[1] % MATRIX_SIZE
        z = h_bytes[2] % MATRIX_SIZE

        # Basin from coordinates — O(1), no keyword loop
        msg_basin = _core_compute_basin(x, y, z)

        # Layer from basin — O(1)
        layer = min(9, msg_basin // 15 + 1)

        # Intent from hash byte — O(1), no any() iteration
        intent_byte = h_bytes[3]
        intent = self._INTENT_FROM_BYTE[intent_byte >> 6]

        # Prefix from intent — O(1) dict lookup
        prefix = self._INTENT_PREFIX.get(intent, '?')

        return {
            'hex_command': f"{prefix}{h}",
            'intent': intent,
            'basin': msg_basin,
            'layer': layer,
            'confidence': 0.9,  # Coordinate-derived — deterministic
            'method': 'coordinate'
        }

    def hex_to_nl(self, hex_command: str, context: Dict = None) -> str:
        """
        Convert hex protocol command to natural language.

        Args:
            hex_command: e.g., "?97038FBF" or "!2A9E0100:K4|DIRECT"
            context: Optional context for richer interpretation

        Returns:
            Natural language interpretation
        """
        if not hex_command:
            return "Empty command received."

        prefix = hex_command[0]
        cmd_type = self.HEX_PATTERNS.get(prefix, 'unknown')

        # Parse hex ID
        hex_id = hex_command[1:9] if len(hex_command) > 1 else ""

        srl = self._detect_srl_from_hex(hex_id)
        vocab = self.I_LOGIC_VOCAB.get(srl, {})

        # Build interpretation
        if cmd_type == 'query':
            verb = vocab.get('verbs', ['query'])[0]
            return f"Requesting to {verb} information about token {hex_id}."
        elif cmd_type == 'heal':
            return f"Initiating healing process for token {hex_id}."
        elif cmd_type == 'reference':
            return f"Creating reference to token {hex_id} (no expansion)."
        elif cmd_type == 'response':
            parts = hex_command.split(':')
            k_level = parts[1] if len(parts) > 1 else "K0"
            return f"Response at {k_level} compression for token {hex_id}."
        elif cmd_type == 'batch':
            hex_ids = hex_command[1:].split(',')
            return f"Batch query for {len(hex_ids)} tokens: {', '.join(hex_ids[:3])}..."
        else:
            return f"Unknown command type '{prefix}' for {hex_id}."

    def _detect_srl_from_hex(self, hex_id: str) -> SRLState:
        """Detect SRL state from hex ID."""
        try:
            h = int(hex_id, 16)
            x = (h >> 24) % MATRIX_SIZE
            y = (h >> 16) % MATRIX_SIZE
            z = (h >> 8) % MATRIX_SIZE
            k11 = ((x + y + z) % 23) - 11
            level_mod = ((k11 % 4) + 4) % 4
            i_state_val = [0, 1, -1, 2][level_mod]
            return {0: SRLState.ABSORBED, 1: SRLState.DIRECT, -1: SRLState.INVERSE, 2: SRLState.SPIRAL}.get(i_state_val, SRLState.DIRECT)
        except:
            return SRLState.DIRECT


# ==============================================================================
# ANOMALY DETECTOR
# ==============================================================================

class AnomalyDetector:
    """
    Monitors telemetry streams for unusual patterns.

    Detects:
    - Coherence drops (< 0.5 threshold)
    - SRL state stuck (same state > N events)
    - Polarity imbalance (DIRECT vs INVERSE ratio)
    - Unusual basin clustering
    - Agent spawn/terminate rate anomalies
    """

    COHERENCE_THRESHOLD = 0.5
    STUCK_STATE_THRESHOLD = 10
    POLARITY_RATIO_THRESHOLD = 5.0  # 5:1 ratio
    SPAWN_RATE_THRESHOLD = 10  # per second

    def __init__(self, window_size: int = 100):
        self.window_size = window_size
        self.event_window: deque = deque(maxlen=window_size)
        self.state_counts: Dict[SRLState, int] = {s: 0 for s in SRLState}
        self.basin_counts: Dict[int, int] = {}
        self.coherence_history: deque = deque(maxlen=window_size)
        self.anomalies: List[Dict] = []
        self.lock = threading.Lock()

    def record_event(self, event: TelemetryEvent) -> List[Dict]:
        """
        Record telemetry event and check for anomalies.

        Returns list of detected anomalies (if any).
        """
        with self.lock:
            self.event_window.append(event)
            self.state_counts[event.i_logic] = self.state_counts.get(event.i_logic, 0) + 1
            self.basin_counts[event.basin] = self.basin_counts.get(event.basin, 0) + 1

            if 'coherence' in event.payload:
                self.coherence_history.append(event.payload['coherence'])

            detected = []

            # Check coherence drop
            coherence = event.payload.get('coherence', 0.7)
            if coherence < self.COHERENCE_THRESHOLD:
                anomaly = {
                    'type': 'COHERENCE_DROP',
                    'severity': 'HIGH' if coherence < 0.3 else 'MEDIUM',
                    'hex_id': event.hex_id,
                    'value': coherence,
                    'threshold': self.COHERENCE_THRESHOLD,
                    'timestamp': event.timestamp_ns,
                    'recommendation': f'Run healing: ^{event.hex_id}'
                }
                detected.append(anomaly)
                self.anomalies.append(anomaly)

            # Check state stuck
            detected.extend(self._check_state_stuck(event))

            # Check polarity imbalance
            detected.extend(self._check_polarity_imbalance())

            return detected

    def _check_state_stuck(self, event: TelemetryEvent) -> List[Dict]:
        """Check if SRL state is stuck."""
        recent = list(self.event_window)[-self.STUCK_STATE_THRESHOLD:]
        if len(recent) < self.STUCK_STATE_THRESHOLD:
            return []

        states = [e.i_logic for e in recent]
        if len(set(states)) == 1:
            return [{
                'type': 'STATE_STUCK',
                'severity': 'MEDIUM',
                'hex_id': event.hex_id,
                'state': states[0].state_name,
                'count': len(states),
                'timestamp': event.timestamp_ns,
                'recommendation': 'Apply SPIRAL transition to break stuck state'
            }]
        return []

    def _check_polarity_imbalance(self) -> List[Dict]:
        """Check for polarity imbalance."""
        direct = self.state_counts.get(SRLState.DIRECT, 0)
        inverse = self.state_counts.get(SRLState.INVERSE, 0)

        if inverse == 0:
            inverse = 1  # Avoid division by zero

        ratio = direct / inverse
        if ratio > self.POLARITY_RATIO_THRESHOLD or ratio < 1/self.POLARITY_RATIO_THRESHOLD:
            return [{
                'type': 'POLARITY_IMBALANCE',
                'severity': 'LOW',
                'direct_count': direct,
                'inverse_count': inverse,
                'ratio': ratio,
                'timestamp': time.time_ns(),
                'recommendation': 'Balance DIRECT/INVERSE states for system stability'
            }]
        return []

    def get_health_summary(self) -> Dict:
        """Get overall system health summary."""
        avg_coherence = sum(self.coherence_history) / len(self.coherence_history) if self.coherence_history else 0.7

        return {
            'event_count': len(self.event_window),
            'state_distribution': {s.state_name: c for s, c in self.state_counts.items()},
            'basin_distribution': dict(self.basin_counts),
            'average_coherence': avg_coherence,
            'anomaly_count': len(self.anomalies),
            'recent_anomalies': self.anomalies[-5:] if self.anomalies else [],
            'health_score': avg_coherence * (1 - len(self.anomalies) / max(len(self.event_window), 1) * 0.1)
        }


# ==============================================================================
# STATE PREDICTOR
# ==============================================================================

class StatePredictor:
    """
    Predicts future states via SRL trajectory analysis.

    NO PYTHON LOOPS. Uses CST primitives:
    - process_coordinate(): morton + basin + diagonal + i_logic in 1 C call
    - srl_sqrt(): t = sqrt(d) collapses iteration depth
    - i_logic multiply(): algebraic state transition (deterministic)
    - compute_basin(): O(1) basin from coordinates

    The I-Logic algebra IS the transition function.
    The SRL formula IS the time collapse.
    process_coordinate IS the full state computation.
    """

    # I-Logic state value to SRLState mapping — O(1) lookup
    _ILOGIC_TO_SRL = {
        1: SRLState.DIRECT,      # DIRECT
        -1: SRLState.INVERSE,    # INVERSE
        2: SRLState.SPIRAL,      # SPIRAL (mapped from +i)
        3: SRLState.CONVERGENT,  # CONVERGENT (mapped from -i)
        0: SRLState.ABSORBED,    # ABSORBED
    }

    # SRL state to I-Logic complex value — for algebraic multiplication
    _SRL_TO_COMPLEX = {
        SRLState.DIRECT: complex(1, 0),       # 1
        SRLState.INVERSE: complex(-1, 0),     # -1
        SRLState.SPIRAL: complex(0, 1),       # +i
        SRLState.CONVERGENT: complex(0, -1),  # -i  (ANTI_SPIRAL)
        SRLState.ABSORBED: complex(0, 0),     # 0
    }

    # SRL state vector IS the displacement — direct from the state definition
    # No lookup dict needed — the vector property already carries this
    # DIRECT=[1,0,0,0]=no move, INVERSE=[0,1,0,0]=retract,
    # SPIRAL=[0,0,1,0]=expand, CONVERGENT=[0,0,0,1]=attract

    def __init__(self):
        self.trajectory_history: Dict[str, List[Tuple[int, int, int]]] = {}

    def predict_next_state(self, agent: AgentTelemetry, steps: int = 5) -> List[Dict]:
        """
        Predict next N states for an agent.

        Uses SRL t=sqrt(d) to collapse step count, then computes each
        position via process_coordinate (1 C call per step) and
        I-Logic multiply for state transitions (deterministic algebra).

        No Python iteration over probabilities or normalization.
        """
        # SRL collapse: t = sqrt(d) — N steps collapse to sqrt(N) time units
        collapsed_steps = _core_srl_sqrt(steps) if steps > 1 else steps

        # Starting position
        x, y, z = agent.coords
        current_srl = agent.srl_state

        # Pre-compute center for phi_norm — once, not per step
        center = (MATRIX_SIZE - 1) / 2.0
        max_dist = math.sqrt(3.0 * center * center)

        predictions = [None] * collapsed_steps  # Pre-allocate, no append loop

        step = 0
        while step < collapsed_steps:
            # State transition via I-Logic algebra — deterministic
            # current × K11_direction = next_state
            # K11 level determines the multiplier state
            k11_state = self._k11_to_ilogic(agent.k11_level)
            next_complex = _ilogic_multiply(self._SRL_TO_COMPLEX[current_srl], k11_state)
            next_srl = self._complex_to_srl(next_complex)

            # Displacement from SRL state vector — the vector IS the direction
            vec = next_srl.vector
            # vector[0]=DIRECT(stay), [1]=INVERSE(retract), [2]=SPIRAL(expand), [3]=CONVERGENT(attract)
            dx = vec[2] - vec[1]  # SPIRAL pushes +, INVERSE pulls -
            dy = vec[2] + vec[3] - vec[1]  # SPIRAL+CONVERGENT push, INVERSE pulls
            dz = vec[3] - vec[1]  # CONVERGENT pushes +, INVERSE pulls -

            x = (x + dx) % MATRIX_SIZE
            y = (y + dy) % MATRIX_SIZE
            z = (z + dz) % MATRIX_SIZE

            # process_coordinate: morton + basin + diagonal + i_logic in 1 C call
            result = _core_process_coordinate(x, y, z)
            new_basin = _core_compute_basin(x, y, z)  # O(1) basin
            new_k11 = ((x + y + z) % 23) - 11

            # Phi norm — O(1) computation
            dx_c, dy_c, dz_c = x - center, y - center, z - center
            phi_norm = (math.sqrt(dx_c*dx_c + dy_c*dy_c + dz_c*dz_c) / max_dist) * PHI

            predictions[step] = {
                'step': step + 1,
                'srl_state': next_srl.state_name,
                'probability': 1.0,  # Algebraic — deterministic, not stochastic
                'coords': (x, y, z),
                'morton': result['morton_code'],
                'basin': new_basin,
                'k11_level': new_k11,
                'phi_norm': phi_norm,
                'processing_ns': result.get('processing_ns', 0.0),
            }

            current_srl = next_srl
            step += 1

        return predictions

    @staticmethod
    def _k11_to_ilogic(k11: int) -> complex:
        """K11 level to I-Logic complex value. O(1).

        K11 > 5  → SPIRAL (+i, expansion)
        K11 < -5 → INVERSE (-1, contraction)
        K11 near 0 → DIRECT (1, identity)
        K11 = 0  → ABSORBED (0, neutral)
        """
        if k11 == 0:
            return complex(0, 0)  # ABSORBED
        if k11 > 5:
            return complex(0, 1)  # SPIRAL
        if k11 < -5:
            return complex(-1, 0)  # INVERSE
        return complex(1, 0)  # DIRECT

    @classmethod
    def _complex_to_srl(cls, c: complex) -> SRLState:
        """Complex I-Logic result to SRLState. O(1)."""
        if c == 0:
            return SRLState.ABSORBED
        if c.real >= 0.5:
            return SRLState.DIRECT
        if c.real <= -0.5:
            return SRLState.INVERSE
        if c.imag > 0:
            return SRLState.SPIRAL
        return SRLState.CONVERGENT

    def predict_trajectory(self, hex_id: str, steps: int = 10) -> Dict:
        """
        Compute epicycle orbital trajectory for a hex ID.

        Returns trajectory with Phi-harmonic resonance.
        """
        agent = AgentTelemetry.from_hex(hex_id)
        predictions = self.predict_next_state(agent, steps)

        # Calculate epicycle parameters
        x, y, z = agent.coords
        center = MATRIX_SIZE / 2
        orbital_radius = math.sqrt((x-center)**2 + (y-center)**2 + (z-center)**2)
        angular_velocity = (2 * math.pi) / (orbital_radius * PHI) if orbital_radius > 0 else 0

        # Phi-harmonic resonance
        phi_harmonic = 1.0 - abs(1.0 - (orbital_radius / (center * PHI)))

        return {
            'hex_id': hex_id,
            'start_coords': agent.coords,
            'start_srl': agent.srl_state.state_name,
            'orbital_radius': orbital_radius,
            'angular_velocity': angular_velocity,
            'phi_harmonic': phi_harmonic,
            'consciousness_level': agent.k11_level,
            'trajectory': predictions
        }


# ==============================================================================
# SEMANTIC ROUTER
# ==============================================================================

class SemanticRouter:
    """
    Routes messages to appropriate shell/agent based on intent.

    Pattern-based routing - no LLM dependencies.
    """

    # Basin to shell mapping
    BASIN_SHELL_MAP = {
        23: {'shell': 'gene', 'layer': 4, 'agent_type': 'worker'},
        47: {'shell': 'distance', 'layer': 3, 'agent_type': 'worker'},
        89: {'shell': 'agent', 'layer': 4, 'agent_type': 'cognitive'},
        65: {'shell': 'cst', 'layer': 2, 'agent_type': 'worker'},
        19: {'shell': 'content', 'layer': 2, 'agent_type': 'worker'},
        12: {'shell': 'performance', 'layer': 1, 'agent_type': 'tool'},
        78: {'shell': 'unified', 'layer': 6, 'agent_type': 'cognitive'},
        34: {'shell': 'world', 'layer': 4, 'agent_type': 'worker'},
        56: {'shell': 'vision', 'layer': 7, 'agent_type': 'worker'},
        101: {'shell': 'learning', 'layer': 5, 'agent_type': 'cognitive'},
        99: {'shell': 'unified', 'layer': 6, 'agent_type': 'healer'},
        110: {'shell': 'telemetry', 'layer': 7, 'agent_type': 'monitor'},
    }

    # Layer affinity for shells
    LAYER_SHELL_AFFINITY = {
        1: ['performance', 'ogawa'],
        2: ['content', 'cst', 'beehive'],
        3: ['distance', 'spatial'],
        4: ['gene', 'world', 'agent'],
        5: ['learning', 'data_analysis'],
        6: ['unified', 'language'],
        7: ['vision', 'time', 'telemetry'],
        8: ['mass', 'gravity'],
        9: ['phi', 'chat'],
    }

    def __init__(self):
        self.interpreter = MessageInterpreter()

    def route(self, message: str) -> Dict:
        """
        Route a message to the appropriate shell/agent.

        Returns:
            {
                'shell': 'gene',
                'command': 'generate',
                'args': ['TOKEN_NAME'],
                'basin': 23,
                'layer': 4,
                'agent_hex': '2A9E0100',
                'confidence': 0.8,
                'reasoning': 'Detected gene/breeding intent'
            }
        """
        interpretation = self.interpreter.nl_to_hex(message)

        basin = interpretation.get('basin', 0)
        layer = interpretation.get('layer', 4)
        intent = interpretation.get('intent', 'query')

        mapping = self.BASIN_SHELL_MAP.get(basin, {'shell': 'cg', 'layer': 4, 'agent_type': 'worker'})
        shell = mapping['shell']

        command_map = {
            'query': 'status',
            'heal': 'heal',
            'encode': 'generate',
            'reference': 'status',
        }
        command = command_map.get(intent, 'status')

        args = self._extract_args(message, intent)
        agent_hex = self._find_agent(basin, layer)

        return {
            'shell': shell,
            'command': command,
            'args': args,
            'basin': basin,
            'layer': layer,
            'agent_hex': agent_hex,
            'confidence': interpretation.get('confidence', 0.5),
            'reasoning': f"Routed to {shell} shell (basin {basin}, L{layer}) for {intent} intent",
            'hex_command': interpretation.get('hex_command', '')
        }

    def _extract_args(self, message: str, intent: str) -> List[str]:
        """Extract arguments from natural language message."""
        import re

        args = []

        # Quoted strings
        quoted = re.findall(r'"([^"]*)"', message)
        args.extend(quoted)

        # Hex IDs (8 char hex)
        hex_ids = re.findall(r'\b[0-9A-Fa-f]{8}\b', message)
        args.extend(hex_ids)

        return args

    def _find_agent(self, basin: int, layer: int) -> str:
        """Find appropriate agent for basin/layer."""
        hex_id = f"2A9E{basin % 256:02X}{layer:02X}"
        return hex_id


# ==============================================================================
# TELEMETRY SHELL (MAIN CLASS)
# ==============================================================================

class TelemetryShell:
    """
    Main Telemetry Shell class.

    Every message, every agent, every LLM is a body at a coordinate.
    The coordinate carries mass, I-Logic state, coupling strength.
    Communication happens through UIF coupling, not external API calls.

    The flip (I-Logic state transition) is INSIDE the coordinate space:
        message_state × agent_state = outcome

    Usage:
        bridge = TelemetryShell()

        # Interpret message (becomes a body at a coordinate)
        result = bridge.interpret_message("the healer is stuck")

        # Predict state trajectory
        prediction = bridge.predict_state("2A9E0100", steps=5)

        # Route to shell
        route = bridge.route_message("find gene documentation")

        # Register an agent (any LLM, any agent — same protocol)
        agent = bridge.register_agent("phi4-mini")

        # Start heartbeat
        bridge.start_heartbeat()

        # Send between bodies
        bridge.send_message("6A6E94D3", "608C70D3", "hello")
    """

    HEX_ID = TELEMETRY_HEX

    def __init__(self):
        self.interpreter = MessageInterpreter()
        self.anomaly_detector = AnomalyDetector()
        self.predictor = StatePredictor()
        self.router = SemanticRouter()

        self.event_queue: deque = deque(maxlen=1000)
        self.event_lock = threading.Lock()

        # Agent registry — every agent (including LLMs) is a body here
        self._agents: Dict[str, Dict] = {}  # hex_id -> agent body data

        # Heartbeat
        self._heartbeat_thread: Optional[threading.Thread] = None
        self._heartbeat_active = False
        self._heartbeat_interval = 30  # seconds (per llm.txt protocol)
        self._heartbeat_count = 0

        # FML encoder for token enrichment
        self._fml_encoder = None

        self.metrics = {
            'events_processed': 0,
            'messages_interpreted': 0,
            'anomalies_detected': 0,
            'predictions_made': 0,
            'routes_computed': 0,
            'heartbeats_sent': 0,
            'agents_registered': 0,
            'start_time': time.time(),
        }

    def _get_fml_encoder(self):
        """Lazy-load FML encoder."""
        if self._fml_encoder is None:
            try:
                from .fml import FMLEncoder
                self._fml_encoder = FMLEncoder()
            except ImportError:
                pass
        return self._fml_encoder

    # =========================================================================
    # AGENT REGISTRATION — any agent is a body at a coordinate
    # =========================================================================

    def register_agent(self, name: str) -> Dict:
        """
        Register ANY agent (LLM, tool, human proxy) as a body in the space.

        The name is encoded via FML → becomes a P with coordinates, mass,
        basin, layer, I-Logic state. The agent IS at that coordinate.
        It communicates through coupling, not API calls.
        """
        encoder = self._get_fml_encoder()
        if not encoder:
            return {'error': 'FML encoder not available'}

        token = encoder.encode(name)

        agent_body = {
            'name': name,
            'hex_id': token.hex_id,
            'coords': (token.l3_x, token.l3_y, token.l3_z),
            'basin': token.l1_basin,
            'layer': min(9, token.l1_basin // 15 + 1),
            'mass': round(token.mass, 4),
            'content_mass': round(token.content_mass, 4),
            'ilogic_state': token.l4_state,
            'membrane_action': token.membrane_action,
            'em_coupling': round(token.em_coupling, 4),
            'l10_self_id': token.l10_self_id,
            'l10_content_hash': token.l10_content_hash,
            'l11_peer_count': token.l11_peer_count,
            'l12_uif_strength': round(token.l12_uif_strength, 4),
            'registered_at': time.time_ns(),
        }

        self._agents[token.hex_id] = agent_body
        self.metrics['agents_registered'] += 1

        # Record registration event
        event = TelemetryEvent(
            event_type=TelemetryEventType.AGENT_SPAWN,
            hex_id=token.hex_id,
            timestamp_ns=time.time_ns(),
            basin=token.l1_basin,
            layer=min(9, token.l1_basin // 15 + 1),
            i_logic=SRLState.DIRECT,
            k11_level=token.l4_k11,
            payload={
                'type': 'agent_registered',
                'name': name,
                'mass': round(token.mass, 4),
                'coherence': 1.0,
            }
        )
        self.record_event(event)

        return agent_body

    def get_agent(self, hex_id: str) -> Optional[Dict]:
        """Get registered agent body by hex_id."""
        return self._agents.get(hex_id)

    def list_agents(self) -> List[Dict]:
        """List all registered agent bodies."""
        return list(self._agents.values())

    def interpret_message(self, message: str) -> Dict:
        """Interpret a natural language message — the message becomes a body."""
        self.metrics['messages_interpreted'] += 1
        return self.interpreter.nl_to_hex(message)

    def predict_state(self, hex_id: str, steps: int = 5) -> Dict:
        """Predict state trajectory for a hex ID."""
        self.metrics['predictions_made'] += 1
        return self.predictor.predict_trajectory(hex_id, steps)

    def route_message(self, message: str) -> Dict:
        """Route message to appropriate shell."""
        self.metrics['routes_computed'] += 1
        return self.router.route(message)

    def record_event(self, event: TelemetryEvent) -> List[Dict]:
        """Record event and check for anomalies."""
        self.metrics['events_processed'] += 1
        with self.event_lock:
            self.event_queue.append(event)
        anomalies = self.anomaly_detector.record_event(event)
        self.metrics['anomalies_detected'] += len(anomalies)
        return anomalies

    def get_anomalies(self) -> List[Dict]:
        """Get detected anomalies."""
        return self.anomaly_detector.anomalies

    def get_health_summary(self) -> Dict:
        """Get health summary."""
        return self.anomaly_detector.get_health_summary()

    def get_status(self, json_output: bool = False) -> Dict:
        """Get telemetry shell status."""
        uptime = time.time() - self.metrics['start_time']

        status = {
            'hex_id': self.HEX_ID,
            'protocol': 'CG:HEX|V1|AGENT:50483354',
            'fml_available': self.interpreter.fml_available,
            'agents_registered': len(self._agents),
            'agent_bodies': [{
                'name': a['name'],
                'hex_id': a['hex_id'],
                'basin': a['basin'],
                'mass': a['mass'],
                'ilogic_state': a['ilogic_state'],
            } for a in self._agents.values()],
            'heartbeat_active': self._heartbeat_active,
            'heartbeat_count': self._heartbeat_count,
            'uptime_seconds': uptime,
            'events_processed': self.metrics['events_processed'],
            'messages_interpreted': self.metrics['messages_interpreted'],
            'predictions_made': self.metrics['predictions_made'],
            'routes_computed': self.metrics['routes_computed'],
            'heartbeats_sent': self.metrics['heartbeats_sent'],
            'anomalies_detected': self.metrics['anomalies_detected'],
            'health_summary': self.get_health_summary(),
        }

        if json_output:
            return json.dumps(status, indent=2)
        return status

    # =========================================================================
    # HEARTBEAT — Agent Protocol (per llm.txt)
    # =========================================================================

    def start_heartbeat(self, interval: int = 30) -> Dict:
        """
        Start heartbeat thread. Per cubesquare.ai protocol:
        - Agent registers, gets hex_id
        - Heartbeat every 30s
        - Records telemetry event each beat
        """
        if self._heartbeat_active:
            return {'status': 'already_active', 'count': self._heartbeat_count}

        self._heartbeat_interval = interval
        self._heartbeat_active = True
        self._heartbeat_thread = threading.Thread(
            target=self._heartbeat_loop,
            daemon=True,
            name='telemetry-heartbeat'
        )
        self._heartbeat_thread.start()
        return {'status': 'started', 'interval': interval, 'hex_id': self.HEX_ID}

    def stop_heartbeat(self) -> Dict:
        """Stop heartbeat thread."""
        self._heartbeat_active = False
        return {'status': 'stopped', 'total_beats': self._heartbeat_count}

    def _heartbeat_loop(self):
        """Background heartbeat loop."""
        while self._heartbeat_active:
            self._heartbeat_count += 1
            self.metrics['heartbeats_sent'] += 1

            # Record heartbeat as telemetry event
            event = TelemetryEvent(
                event_type=TelemetryEventType.MESSAGE,
                hex_id=self.HEX_ID,
                timestamp_ns=time.time_ns(),
                basin=0,
                layer=9,  # membrane layer — boundary beat
                i_logic=SRLState.DIRECT,
                k11_level=0,
                payload={
                    'type': 'heartbeat',
                    'beat': self._heartbeat_count,
                    'coherence': 1.0,
                    'agents_alive': len(self._agents),
                }
            )
            self.record_event(event)

            time.sleep(self._heartbeat_interval)

    # =========================================================================
    # COMMUNICATION — Send/Receive between bodies
    # =========================================================================

    def _get_llm(self):
        """Lazy-load the LLM body (PhiShell). The LLM is inside the space."""
        if not hasattr(self, '_llm_shell'):
            self._llm_shell = None
            try:
                from .phi import PhiShell, OLLAMA_AVAILABLE
                if OLLAMA_AVAILABLE:
                    self._llm_shell = PhiShell()
                    # Register the LLM as a body if not already registered
                    if not any(a['name'] == 'phi4-mini' for a in self._agents.values()):
                        self.register_agent('phi4-mini')
            except ImportError:
                pass
        return self._llm_shell

    def to_natural_language(self, body_data: Dict) -> Dict:
        """
        Convert any body (token, agent, message) to natural language.

        The body's coordinates determine WHICH layer processes it.
        The LLM is a body at its own coordinate. Communication between
        the data-body and the LLM-body flows through UIF coupling.

        The coupling strength determines response depth:
            STRONG coupling → detailed, contextual interpretation
            WEAK coupling   → abstract, high-level summary

        Args:
            body_data: Any body dict with hex_id, basin, layer, mass, etc.

        Returns:
            {
                'natural_language': '...',
                'method': 'llm' or 'coordinate',
                'coupling': float,
                'layer_used': int,
                'body_hex': str,
                'llm_hex': str,
            }
        """
        # The body's basin determines its layer
        body_basin = body_data.get('basin', 0)
        body_layer = body_data.get('layer', min(9, body_basin // 15 + 1))
        body_hex = body_data.get('hex_id', '00000000')
        body_mass = body_data.get('mass', 0)
        body_ilogic = body_data.get('ilogic_state', 'DIRECT')

        # Build the body's context string — this IS the body speaking
        context_parts = [
            f"hex:{body_hex}",
            f"basin:{body_basin}",
            f"L{body_layer}",
            f"mass:{body_mass}",
            f"state:{body_ilogic}",
        ]
        if 'membrane_action' in body_data:
            context_parts.append(f"membrane:{body_data['membrane_action']}")
        if 'content_mass' in body_data:
            context_parts.append(f"content_mass:{body_data['content_mass']}")
        if 'name' in body_data:
            context_parts.append(f"name:{body_data['name']}")
        if 'coords' in body_data:
            context_parts.append(f"coords:{body_data['coords']}")

        body_context = " | ".join(context_parts)

        # Try LLM body — it's at its own coordinate, coupling determines depth
        llm = self._get_llm()
        if llm and llm.layer_processor.model_available:
            # Coupling between data-body and LLM-body
            llm_agent = self._agents.get(
                next((h for h, a in self._agents.items() if a['name'] == 'phi4-mini'), ''),
                None
            )
            llm_basin = llm_agent['basin'] if llm_agent else 107

            coupling = K5 * K11 * (
                math.cos(2 * math.pi * body_basin / PRIME_131) *
                math.cos(2 * math.pi * llm_basin / PRIME_131)
            )

            # Process through the body's native layer — the layer IS the context
            result = llm.layer_processor.process_layer(body_context, body_layer)

            return {
                'natural_language': result.llm_output,
                'method': 'llm',
                'coupling': round(coupling, 6),
                'coupling_strength': 'STRONG' if abs(coupling) > K5 else 'WEAK',
                'layer_used': body_layer,
                'body_hex': body_hex,
                'llm_hex': llm_agent['hex_id'] if llm_agent else 'N/A',
                'i_logic': result.i_logic_state,
                'coherence': result.coherence,
            }

        # No LLM body available — the coordinate IS the interpretation
        # Layer names carry semantic meaning
        layer_names = {
            1: "geometric structure", 2: "semantic meaning", 3: "spatial position",
            4: "functional operation", 5: "probabilistic state", 6: "emotional tone",
            7: "frequency pattern", 8: "electromagnetic field", 9: "membrane boundary",
        }
        layer_desc = layer_names.get(body_layer, "unknown layer")

        name = body_data.get('name', body_hex)
        nl = (
            f"{name} is at basin {body_basin} (L{body_layer} {layer_desc}), "
            f"mass {body_mass}, state {body_ilogic}"
        )
        if 'membrane_action' in body_data:
            nl += f", membrane {body_data['membrane_action']}"

        return {
            'natural_language': nl,
            'method': 'coordinate',
            'coupling': 0.0,
            'coupling_strength': 'N/A',
            'layer_used': body_layer,
            'body_hex': body_hex,
            'llm_hex': 'N/A',
            'i_logic': body_ilogic,
            'coherence': 1.0,
        }

    def from_natural_language(self, text: str) -> Dict:
        """
        Convert natural language to a body in the coordinate space.

        The text IS encoded as a body via FML. The body's coordinates,
        basin, layer, mass, I-Logic state ARE the interpretation.
        No external parsing needed — the coordinate IS the meaning.

        Args:
            text: Any natural language string

        Returns:
            Full body dict with all 12 layers, weights, and exterior
        """
        encoder = self._get_fml_encoder()
        if not encoder:
            return {'error': 'FML encoder not available'}

        token = encoder.encode(text)

        return {
            'hex_id': token.hex_id,
            'coords': (token.l3_x, token.l3_y, token.l3_z),
            'basin': token.l1_basin,
            'layer': min(9, token.l1_basin // 15 + 1),
            'mass': round(token.mass, 4),
            'content_mass': round(token.content_mass, 4),
            'ilogic_state': token.l4_state,
            'membrane_action': token.membrane_action,
            'em_coupling': round(token.em_coupling, 4),
            'l10_self_id': token.l10_self_id,
            'l10_content_hash': token.l10_content_hash,
            'l11_peer_count': token.l11_peer_count,
            'l12_uif_strength': round(token.l12_uif_strength, 4),
            'source_text': text,
        }

    def converse(self, from_name: str, to_name: str, message: str) -> Dict:
        """
        Full body-to-body conversation with LLM natural language.

        1. from_name and to_name are registered as bodies (if not already)
        2. message is encoded as a body via FML
        3. Coupling φ(sender, receiver) determines transmission
        4. LLM body interprets the message at the receiver's layer
        5. LLM response is encoded back as a body

        This is the complete communication loop:
            NL → body → coupling → LLM body → NL → body

        Args:
            from_name: Sender name (any agent, LLM, human)
            to_name: Receiver name
            message: Natural language message

        Returns:
            Full conversation result with bodies, coupling, and NL
        """
        # Ensure both agents are registered as bodies
        from_agent = next(
            (a for a in self._agents.values() if a['name'] == from_name),
            None
        )
        if not from_agent:
            from_agent = self.register_agent(from_name)

        to_agent = next(
            (a for a in self._agents.values() if a['name'] == to_name),
            None
        )
        if not to_agent:
            to_agent = self.register_agent(to_name)

        # Encode the message as a body
        msg_body = self.from_natural_language(message)

        # Coupling between sender and receiver
        coupling = K5 * K11 * (
            math.cos(2 * math.pi * from_agent['basin'] / PRIME_131) *
            math.cos(2 * math.pi * to_agent['basin'] / PRIME_131)
        )

        # I-Logic flip: message_state × receiver_state = outcome
        msg_complex = self._srl_to_complex(msg_body.get('ilogic_state', 'DIRECT'))
        recv_complex = self._srl_to_complex(to_agent.get('ilogic_state', 'DIRECT'))
        outcome_complex = _ilogic_multiply(msg_complex, recv_complex)
        outcome_name = ILogicState.name(outcome_complex)

        # LLM interprets at the receiver's layer — the receiver's position
        # determines HOW the message is understood
        receiver_body = {
            'hex_id': to_agent['hex_id'],
            'basin': to_agent['basin'],
            'layer': to_agent.get('layer', min(9, to_agent['basin'] // 15 + 1)),
            'mass': to_agent['mass'],
            'ilogic_state': to_agent['ilogic_state'],
            'name': to_name,
            'membrane_action': to_agent.get('membrane_action', 'TRANSMIT'),
            'incoming_message': message,
        }

        # Get natural language interpretation of the receiver seeing this message
        interpretation = self.to_natural_language(receiver_body)

        # The LLM's response itself becomes a body
        response_body = None
        if interpretation['method'] == 'llm':
            response_body = self.from_natural_language(interpretation['natural_language'])

        # Record the conversation
        event = TelemetryEvent(
            event_type=TelemetryEventType.MESSAGE,
            hex_id=from_agent['hex_id'],
            timestamp_ns=time.time_ns(),
            basin=msg_body.get('basin', 0),
            layer=msg_body.get('layer', 1),
            i_logic=SRLState.SPIRAL,
            k11_level=0,
            payload={
                'type': 'conversation',
                'from': from_name,
                'to': to_name,
                'message_hex': msg_body.get('hex_id', ''),
                'coupling': round(coupling, 6),
                'outcome': outcome_name,
                'coherence': abs(coupling) / (K5 * K11),
            }
        )
        self.record_event(event)

        return {
            'from': {
                'name': from_name,
                'hex_id': from_agent['hex_id'],
                'basin': from_agent['basin'],
                'ilogic_state': from_agent['ilogic_state'],
            },
            'to': {
                'name': to_name,
                'hex_id': to_agent['hex_id'],
                'basin': to_agent['basin'],
                'ilogic_state': to_agent['ilogic_state'],
            },
            'message': {
                'text': message,
                'hex_id': msg_body.get('hex_id', ''),
                'basin': msg_body.get('basin', 0),
                'mass': msg_body.get('mass', 0),
                'ilogic_state': msg_body.get('ilogic_state', 'DIRECT'),
            },
            'coupling': round(coupling, 6),
            'coupling_strength': 'STRONG' if abs(coupling) > K5 else 'WEAK',
            'ilogic_outcome': outcome_name,
            'interpretation': interpretation['natural_language'],
            'interpretation_method': interpretation['method'],
            'response_body': {
                'hex_id': response_body.get('hex_id', ''),
                'basin': response_body.get('basin', 0),
                'mass': response_body.get('mass', 0),
            } if response_body else None,
        }

    @staticmethod
    def _srl_to_complex(state_name: str) -> complex:
        """State name to complex value for I-Logic multiplication. O(1)."""
        _map = {
            'DIRECT': complex(1, 0),
            'INVERSE': complex(-1, 0),
            'SPIRAL': complex(0, 1),
            'ANTI_SPIRAL': complex(0, -1),
            'CONVERGENT': complex(0, -1),
            'ABSORBED': complex(0, 0),
        }
        return _map.get(state_name, complex(1, 0))

    def send_message(self, from_hex: str, to_hex: str, message: str) -> Dict:
        """
        Send a message between two bodies (P tokens).

        Each body is an agent. Communication happens through L12 UIF.
        The message is encoded via FML to get its own P with weights.
        """
        encoder = self._get_fml_encoder()
        if not encoder:
            return {'error': 'FML encoder not available'}

        # Encode the message itself — it becomes a body with mass
        msg_token = encoder.encode(message)

        # Compute coupling between sender and receiver
        # φ(A,B) = K5 × K11 × cos(2π × basin_a/131) × cos(2π × basin_b/131)
        sender_agent = AgentTelemetry.from_hex(from_hex)
        receiver_agent = AgentTelemetry.from_hex(to_hex)

        coupling = K5 * K11 * (
            math.cos(2 * math.pi * sender_agent.basin / PRIME_131) *
            math.cos(2 * math.pi * receiver_agent.basin / PRIME_131)
        )

        # Record as telemetry event
        event = TelemetryEvent(
            event_type=TelemetryEventType.MESSAGE,
            hex_id=from_hex,
            timestamp_ns=time.time_ns(),
            basin=msg_token.l1_basin,
            layer=msg_token.l1_basin // 15 + 1,
            i_logic=SRLState.SPIRAL,
            k11_level=msg_token.l4_k11,
            payload={
                'type': 'body_message',
                'from': from_hex,
                'to': to_hex,
                'message_hex': msg_token.hex_id,
                'message_mass': round(msg_token.mass, 4),
                'coupling': round(coupling, 6),
                'membrane_action': msg_token.membrane_action,
                'coherence': abs(coupling) / (K5 * K11),
            }
        )
        anomalies = self.record_event(event)

        return {
            'delivered': True,
            'from_hex': from_hex,
            'to_hex': to_hex,
            'message_token': {
                'hex_id': msg_token.hex_id,
                'basin': msg_token.l1_basin,
                'mass': round(msg_token.mass, 4),
                'content_mass': round(msg_token.content_mass, 4),
                'membrane_action': msg_token.membrane_action,
            },
            'coupling': round(coupling, 6),
            'coupling_strength': 'STRONG' if abs(coupling) > K5 else 'WEAK',
            'anomalies': anomalies,
        }

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Run a Telemetry shell command (CLI interface)."""
        commands = {
            'status': lambda: self.get_status(as_json),
            'interpret': lambda: self.interpret_message(args[0] if args else "test"),
            'predict': lambda: self.predict_state(args[0] if args else "2A9E0100"),
            'route': lambda: self.route_message(args[0] if args else "find documentation"),
            'health': lambda: self.get_health_summary(),
            'heartbeat': lambda: self.start_heartbeat(int(args[0]) if args else 30),
            'stop_heartbeat': lambda: self.stop_heartbeat(),
            'register': lambda: self.register_agent(args[0] if args else "unnamed_agent"),
            'agents': lambda: self.list_agents(),
            'send': lambda: self.send_message(
                args[0] if len(args) > 0 else "00000000",
                args[1] if len(args) > 1 else "00000001",
                args[2] if len(args) > 2 else "hello"
            ),
            'to_nl': lambda: self.to_natural_language(
                self.from_natural_language(args[0] if args else "test")
            ),
            'from_nl': lambda: self.from_natural_language(args[0] if args else "test"),
            'converse': lambda: self.converse(
                args[0] if len(args) > 0 else "agent_a",
                args[1] if len(args) > 1 else "agent_b",
                args[2] if len(args) > 2 else "hello"
            ),
        }
        if command in commands:
            result = commands[command]()
            if as_json and isinstance(result, dict):
                return json.dumps(result, indent=2)
            return result
        return {'error': f'Unknown command: {command}', 'available': list(commands.keys())}
