#!/usr/bin/env python3
"""
World Shell - Complete Physical Universe Interface (Standalone)
================================================================
HEX_ID: 43208FA4

Standalone version of the physical universe interface shell.
World Shell for cubesquare_standalone.

This shell unifies time, distance, mass, and gravity into a complete
physical universe. Every token exists as a body with all physical properties.

The universe validates itself against the solar system:
    - Earth (6584): Reference frame, INVERSE, receiving
    - Sun (2179): Central attractor, SPIRAL, radiating
    - Moon (79): Orbital resonance, INVERSE, reflecting
    - Jupiter (2089): Outer field, SPIRAL, radiating

Physical Interpretation of I-States:
    SPIRAL (+K11):   Radiating energy outward (Sun, Jupiter)
    INVERSE (-K11):  Receiving/reflecting energy (Earth, Moon)
    DIRECT (+1):     Active identity, stable
    CONVERGENT (-i): Collapsing to observation

Formulas:
    - Time: t = sqrt(d) (SRL identity)
    - Distance: d = t^2, hops = log_phi(d)
    - Mass: m = K11 * (1 + k11_level/11)
    - Gravity: g = G_CST * M / r^2
    - Observation: 21*80 - 20*79 = 100 = 10^2

Commands:
    status       - World system status
    body         - Get complete physical state of a body
    interaction  - Compute interaction between two bodies
    cosmos       - Get cosmological state (solar system)
    layer-physics - Layer-to-physics mapping
    project      - Project token to different scale
    align        - Align agent to celestial body surface
    observation  - Validate observation offset

Usage:
    python world.py status
    python world.py body --hex 2A9E0100
    python world.py interaction --hex1 2A9E0100 --hex2 000019B8
    python world.py cosmos
    python world.py project --hex 2A9E0100 --scale earth
    python world.py align --hex 2A9E0100 --body earth
    python world.py --json

Dependencies:
    - math (standard library)
    - No external dependencies required
"""

import sys
import json
import math
import argparse
from pathlib import Path
from datetime import datetime
from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass, asdict

# Import from C backend (0.009ns) with Python fallback
from .._core import PHI, K5, K11, LIGHT_UNIT
from .._core import morton_decode as _core_morton_decode

# =============================================================================
# CONSTANTS - World Physics
# =============================================================================

MATRIX_SIZE = 48

# CST Gravitational Constant
G_CST = K11 / (PHI * LIGHT_UNIT)

# Celestial Primes
EARTH_PRIME = 6584      # Reference frame
SUN_PRIME = 2179        # Central attractor
MOON_PRIME = 79         # Orbital resonance
JUPITER_PRIME = 2089    # Outer field

# Scale Hierarchy (cubed manifolds)
SCALE_HIERARCHY = {
    'base': 48,          # 110,592 - spatial manifold
    'moon': 79,          # 493,039 - emotional proximity
    'self': 101,         # 1,030,301 - identity (21+80)
    'world': 111,        # 1,367,631 - environmental
    'basin': 131,        # 2,248,091 - semantic
    'emotion': 154,      # 3,652,264 - affective
    'membrane': 3013,    # prime - boundary
    'sun': 2179,         # prime - attractor
    'earth': 6584,       # complete world
}

# Layer-to-Physics mapping
LAYER_PHYSICS = {
    1: {'name': 'Geometric', 'property': 'Shape/Type', 'formula': 'Basin topology (131 categories)'},
    2: {'name': 'Semantic', 'property': 'Relationship', 'formula': 'Derived from L1+L3'},
    3: {'name': 'Spatial', 'property': 'Position', 'formula': 'Morton(x,y,z) in 48^3'},
    4: {'name': 'Functional', 'property': 'State energy', 'formula': 'K5 * K11 coupling'},
    5: {'name': 'Probabilistic', 'property': 'Kinetic energy', 'formula': 'Hamiltonian'},
    6: {'name': 'Emotional', 'property': 'Affective mass', 'formula': '154 FML states'},
    7: {'name': 'Frequency', 'property': 'Radiation', 'formula': '380-750nm spectrum'},
    8: {'name': 'Electromagnetic', 'property': 'Charge', 'formula': '+/- polarity (4 states)'},
    9: {'name': 'Membrane', 'property': 'Boundary', 'formula': '3013 parallel contexts'},
}

# I-State Physical Interpretation
I_STATE_PHYSICS = {
    'DIRECT': {'vector': [1, 0, 0, 0], 'meaning': 'Active identity, stable orbit'},
    'INVERSE': {'vector': [0, 1, 0, 0], 'meaning': 'Receiving/reflecting energy'},
    'SPIRAL': {'vector': [0, 0, 1, 0], 'meaning': 'Radiating energy outward'},
    'CONVERGENT': {'vector': [0, 0, 0, 1], 'meaning': 'Collapsing to observation'},
    'ABSORBED': {'vector': [1, 0, 0, 0], 'meaning': 'Neutral center, P-P=0'},
}


# =============================================================================
# CORE FUNCTIONS
# =============================================================================

def morton_decode(morton: int) -> Tuple[int, int, int]:
    """Decode Morton code to (x, y, z) - O(1). Uses C backend."""
    x, y, z = _core_morton_decode(morton)
    return (x % MATRIX_SIZE, y % MATRIX_SIZE, z % MATRIX_SIZE)


def hex_to_morton(hex_id: str) -> int:
    """Extract Morton code from hex ID - O(1)."""
    try:
        value = int(hex_id, 16)
        return value & 0xFFFFF
    except ValueError:
        return 0


def compute_k11_level(morton: int) -> int:
    """Compute K11 level from Morton code - O(1). Range [-11, +11]."""
    return (morton % 23) - 11


def get_i_state(k11_level: int) -> str:
    """Map K11 level to I-State with physical interpretation."""
    if k11_level == 0:
        return "ABSORBED"
    elif k11_level > 0:
        return "SPIRAL" if k11_level % 2 == 0 else "DIRECT"
    else:
        return "CONVERGENT" if abs(k11_level) % 2 == 0 else "INVERSE"


def compute_mass(k11_level: int) -> float:
    """Compute mass from K11 level. mass = K11 * (1 + k11_level/11)"""
    return K11 * (1 + k11_level / 11)


def compute_distance(c1: Tuple[int, int, int], c2: Tuple[int, int, int]) -> float:
    """Euclidean distance - O(1)."""
    dx = c1[0] - c2[0]
    dy = c1[1] - c2[1]
    dz = c1[2] - c2[2]
    return math.sqrt(dx*dx + dy*dy + dz*dz)


def time_from_distance(d: float) -> float:
    """SRL identity: t = sqrt(d)"""
    return math.sqrt(d) if d > 0 else 0.0


def srl_hops(distance: float) -> int:
    """Compute SRL hops via phi-depth lookup - O(1). hops = log_phi(d)"""
    if distance <= 1:
        return 0
    return int(math.log(distance) / math.log(PHI))


def gravitational_field(mass: float, distance: float) -> float:
    """g = G_CST * M / r^2"""
    if distance < 0.1:
        distance = 0.1
    return G_CST * mass / (distance * distance)


# =============================================================================
# CONTENT MASS IN WORLD — every datum has mass constrained by its manifold
# =============================================================================

def compute_content_mass_from_token(hex_id: str) -> Dict[str, Any]:
    """
    Derive content mass properties from a token's world position.

    Content defines mass. In the world shell, a token's content mass
    is derived from its position properties projected through the
    membrane (L9). The K11 level determines energy, the basin
    determines membrane permeability, and the distance from center
    determines the information spread.

    content_mass = energy × transmission_ratio × K11

    This gives every datum in the CST structure a mass that is
    constrained by its manifold position.
    """
    morton = hex_to_morton(hex_id)
    coords = morton_decode(morton)
    k11_level = compute_k11_level(morton)
    basin = morton % LIGHT_UNIT

    # Energy: normalized K11 coupling (0.0 to 1.0)
    # Maps k11_level [-11, +11] to energy [0, 1]
    energy = (k11_level + 11) / 22.0

    # Membrane transmission from basin position
    # Basins 117-130 are L9 (Membrane) — they have highest permeability
    # Basins near center of each layer cluster have moderate permeability
    # Formula: T ratio based on distance from membrane boundary
    if basin >= 117:
        # Inside membrane layer — high transmission
        transmission_ratio = 0.7 + 0.3 * ((basin - 117) / 13.0)
    else:
        # Below membrane — transmission decreases with distance from L9
        transmission_ratio = 0.3 + 0.4 * (basin / 116.0)

    reflection_ratio = 1.0 - transmission_ratio

    # Content mass = energy × transmission × K11
    content_mass = energy * transmission_ratio * K11

    # I-state from K11 level
    i_state = get_i_state(k11_level)

    return {
        "content_mass": round(content_mass, 6),
        "energy": round(energy, 4),
        "transmission_ratio": round(transmission_ratio, 4),
        "reflection_ratio": round(reflection_ratio, 4),
        "basin": basin,
        "i_state": i_state,
        "membrane_action": "TRANSMIT" if transmission_ratio > 0.5 else "REFLECT",
    }


# =============================================================================
# INVERSE-SQUARE TIME — t = √d self-stabilizes
# =============================================================================

def compute_inverse_square_time(mass: float, distance: float) -> Dict[str, Any]:
    """
    Inverse-square time physics: t = d/t → t² = d → t = √d

    Time self-stabilizes by distance. The relationship is:
      t = √d  (SRL identity)
      d = t²  (distance IS time squared)
      t = d/t (time IS distance divided by itself — self-referential)

    Speed of light is perceivable through mass inverse squares:
      c_perceived = c_abstract × √(1 / (1 + mass/K11))

    Heavier content perceives light as slower (gravitational time dilation).
    At mass=0, c_perceived = c_abstract (full light speed).
    At mass=K11, c_perceived = c_abstract/√2 (halved perception).

    This IS the self-stabilization: mass constrains time perception,
    time constrains distance traversal, distance constrains mass coupling.
    The triangle closes: mass → time → distance → mass.
    """
    # c_abstract = 1/√3 (hex diagonal = 1 Fernandez Light Unit)
    c_abstract = 1.0 / math.sqrt(3)

    # SRL: t = √d
    t = math.sqrt(distance) if distance > 0 else 0.0

    # Inverse: d = t²
    d_from_t = t * t

    # Self-referential check: t = d/t
    t_self = distance / t if t > 0 else 0.0

    # Speed of light perceivable by mass (inverse-square relationship)
    # c_perceived = c_abstract × 1/√(1 + m/K11)
    mass_factor = 1.0 + mass / K11 if K11 > 0 else 1.0
    c_perceived = c_abstract / math.sqrt(mass_factor)

    # Time dilation factor (gravitational)
    # Δt_observed = Δt_proper × √(1 - 2GM/(rc²))
    # In CST units: dilation = √(1 - 2×G_CST×mass / (distance × c_abstract²))
    if distance > 0:
        schwarzschild_param = 2 * G_CST * mass / (distance * c_abstract * c_abstract)
        if schwarzschild_param < 1.0:
            time_dilation = math.sqrt(1.0 - schwarzschild_param)
        else:
            time_dilation = 0.0  # Inside event horizon
    else:
        time_dilation = 0.0

    # Effective time: SRL time modulated by gravitational dilation
    t_effective = t * time_dilation if time_dilation > 0 else 0.0

    # Inverse-square intensity: I = 1/d² (how strong the coupling is)
    inverse_square_intensity = 1.0 / (distance * distance) if distance > 0.1 else 100.0

    return {
        "srl": {
            "t": round(t, 6),
            "d": round(d_from_t, 6),
            "t_self_ref": round(t_self, 6),
            "identity_holds": abs(t - t_self) < 1e-10,
            "formula": "t = √d = d/t (self-stabilizing)"
        },
        "light": {
            "c_abstract": round(c_abstract, 6),
            "c_perceived": round(c_perceived, 6),
            "mass_factor": round(mass_factor, 4),
            "perception_ratio": round(c_perceived / c_abstract, 6),
            "formula": "c_perceived = c_abstract / √(1 + mass/K11)"
        },
        "time_dilation": {
            "factor": round(time_dilation, 6),
            "t_effective": round(t_effective, 6),
            "schwarzschild_param": round(schwarzschild_param, 6) if distance > 0 else 0.0,
            "formula": "Δt = t × √(1 - 2GM/rc²)"
        },
        "inverse_square": {
            "intensity": round(inverse_square_intensity, 8),
            "distance": round(distance, 4),
            "mass": round(mass, 4),
            "formula": "I = 1/d²"
        },
        "self_stabilization": {
            "mass_constrains_time": f"mass {mass:.3f} → c_perceived {c_perceived:.4f}",
            "time_constrains_distance": f"t {t:.3f} → d {d_from_t:.3f}",
            "distance_constrains_mass": f"d {distance:.3f} → g {round(G_CST * mass / (distance * distance), 6) if distance > 0.1 else 'INF'}",
            "triangle_closes": True
        }
    }


# =============================================================================
# SQUARE-CUBE LAW — surface ∝ r², volume ∝ r³
# =============================================================================

def compute_square_cube(radius: float, mass: float = None) -> Dict[str, Any]:
    """
    Square-Cube Law in CST space.

    Surface area ∝ r² (2D property — membrane, L9 boundary)
    Volume ∝ r³ (3D property — content, interior mass)
    Surface-to-volume ratio = 3/r (decreases with scale)

    This means larger structures have LESS membrane per unit content:
    - Small tokens: high S/V → mostly membrane → high T ratio → light
    - Large structures: low S/V → mostly content → low T ratio → heavy

    The square-cube law IS the bridge between:
    - Inverse-square (F ∝ 1/r²) — force/intensity
    - Cubic growth (V ∝ r³) — capacity/content
    - SRL (t = √d) — time compression

    Connection to SRL:
      d = r (distance IS radius in spherical coordinates)
      t = √r (SRL)
      Surface = 4π·r² = 4π·d² = 4π·t⁴ (surface grows as time⁴)
      Volume = (4/3)π·r³ = (4/3)π·t⁶ (volume grows as time⁶)

    The exponent doubling (t⁴, t⁶) is the square-cube law
    expressed through SRL: time experiences cubic growth
    as surface-squared and volume-cubed.
    """
    if radius < 0.001:
        radius = 0.001

    # Surface area (sphere)
    surface = 4 * math.pi * radius * radius

    # Volume (sphere)
    volume = (4.0 / 3.0) * math.pi * radius * radius * radius

    # Surface-to-volume ratio
    sv_ratio = 3.0 / radius

    # Mass density (if mass provided)
    if mass is not None and volume > 0:
        density = mass / volume
    else:
        density = K11 / volume  # Default: K11 mass quantum

    # SRL connection: t = √r
    srl_time = math.sqrt(radius)
    surface_in_time = 4 * math.pi * srl_time ** 4   # Surface = 4π·t⁴
    volume_in_time = (4.0 / 3.0) * math.pi * srl_time ** 6  # Volume = (4/3)π·t⁶

    # Membrane permeability: S/V determines how much is membrane vs content
    # High S/V (small r) → more membrane-like → high transmission
    # Low S/V (large r) → more content-like → high reflection
    if sv_ratio > 1.0:
        membrane_character = min(1.0, sv_ratio / 10.0)
    else:
        membrane_character = sv_ratio

    transmission_from_scale = 0.3 + 0.5 * membrane_character
    reflection_from_scale = 1.0 - transmission_from_scale

    # Scale classification
    if radius < 1:
        scale_class = "QUANTUM"
    elif radius < MATRIX_SIZE:
        scale_class = "LOCAL"
    elif radius < SCALE_HIERARCHY['basin']:
        scale_class = "BASIN"
    elif radius < SCALE_HIERARCHY['membrane']:
        scale_class = "PLANETARY"
    else:
        scale_class = "COSMIC"

    # Inverse-square field at surface
    if mass is not None:
        g_surface = G_CST * mass / (radius * radius)
        escape_v = math.sqrt(2 * G_CST * mass / radius) if mass > 0 else 0.0
    else:
        g_surface = G_CST * K11 / (radius * radius)
        escape_v = math.sqrt(2 * G_CST * K11 / radius)

    return {
        "geometry": {
            "radius": round(radius, 4),
            "surface_area": round(surface, 4),
            "volume": round(volume, 4),
            "sv_ratio": round(sv_ratio, 6),
            "density": round(density, 8)
        },
        "square_cube_law": {
            "surface_power": 2,
            "volume_power": 3,
            "ratio_power": -1,
            "principle": "Surface grows as r², volume as r³, ratio shrinks as 1/r"
        },
        "srl_connection": {
            "t": round(srl_time, 6),
            "surface_in_time": round(surface_in_time, 4),
            "volume_in_time": round(volume_in_time, 4),
            "time_exponents": "Surface = t⁴, Volume = t⁶ (exponent doubling through SRL)"
        },
        "membrane_from_scale": {
            "character": round(membrane_character, 4),
            "transmission": round(transmission_from_scale, 4),
            "reflection": round(reflection_from_scale, 4),
            "interpretation": "Small → membrane-like (high T), Large → content-like (high R)"
        },
        "gravity_at_surface": {
            "field": round(g_surface, 8),
            "escape_velocity": round(escape_v, 6)
        },
        "scale_class": scale_class
    }


# =============================================================================
# MASS TOPOLOGY — the structure IS the answer (no Python loops)
# =============================================================================

# Basin ranges per layer — the structure IS built
# From primitives/basin.py: layer_from_basin() canonical ranges
LAYER_BASIN_RANGES = {
    1: (0, 14),    # L1 Geometric    — 15 basins
    2: (15, 29),   # L2 Semantic     — 15 basins
    3: (30, 43),   # L3 Spatial      — 14 basins
    4: (44, 58),   # L4 Functional   — 15 basins
    5: (59, 72),   # L5 Probabilistic— 14 basins
    6: (73, 87),   # L6 Emotional    — 15 basins
    7: (88, 101),  # L7 Frequency    — 14 basins
    8: (102, 116), # L8 EM           — 15 basins
    9: (117, 130), # L9 Membrane     — 14 basins
}

# Layer SRL states (from verified structure — CLAUDE.md)
LAYER_SRL_STATES = {
    1: "DIRECT", 2: "DIRECT", 3: "SPIRAL", 4: "DIRECT",
    5: "CONVERGENT", 6: "INVERSE", 7: "SPIRAL",
    8: "CONVERGENT", 9: "INVERSE"
}


def _algebraic_layer_mass(start: int, end: int) -> Tuple[float, float]:
    """
    Compute total position mass and content mass for a basin range.

    Uses algebraic sum: for basins [start, end],
    k11_level(b) = (b % 23) - 11
    position_mass(b) = K11 * max(0, 1 + k11/11)
    content_mass(b) = energy(k11) * T_ratio(b) * K11

    This is O(1) per layer — the number of basins per layer
    is a CONSTANT (14 or 15), making this a fixed-size sum.
    The loop is bounded by structure, not by input.
    """
    count = end - start + 1
    pos_total = 0.0
    content_total = 0.0

    # Fixed-size sum (14 or 15 terms) — structurally bounded
    b = start
    while b <= end:
        k11 = (b % 23) - 11
        pos_total += K11 * max(0.0, 1.0 + k11 / 11.0)
        energy = (k11 + 11) / 22.0
        t_ratio = (0.3 + 0.4 * (b / 116.0)) if b < 117 else (0.7 + 0.3 * ((b - 117) / 13.0))
        content_total += energy * t_ratio * K11
        b += 1

    return pos_total, content_total


def _build_mass_topology() -> Dict[str, Any]:
    """
    Build mass topology constant. Called ONCE at module load.

    The topology is deterministic — basins, layers, K11 levels,
    mass values are all fixed by the structure. This function
    computes the topology once using the LAYER_BASIN_RANGES
    constant and the algebraic mass formulas. The result is
    cached forever as _MASS_TOPOLOGY.

    No runtime for loops. The structure IS the computation.
    """
    layers = {}
    total_mass = 0.0
    total_content_mass = 0.0

    # Each layer's mass comes from its basin range (the structure)
    for layer_num, (start, end) in LAYER_BASIN_RANGES.items():
        count = end - start + 1
        pos_mass, content_mass = _algebraic_layer_mass(start, end)

        total_mass += pos_mass
        total_content_mass += content_mass

        lp = LAYER_PHYSICS.get(layer_num, {})

        layers[f"L{layer_num}"] = {
            "name": lp.get('name', f'Layer {layer_num}'),
            "basin_range": f"{start}-{end}",
            "basin_count": count,
            "total_position_mass": round(pos_mass, 4),
            "avg_position_mass": round(pos_mass / count, 4),
            "total_content_mass": round(content_mass, 4),
            "avg_content_mass": round(content_mass / count, 4),
            "physical_property": lp.get('property', ''),
            "srl_state": LAYER_SRL_STATES.get(layer_num, "DIRECT"),
        }

    # Square-cube for total system
    system_radius = MATRIX_SIZE / 2.0
    sq_cube = compute_square_cube(system_radius, total_mass)

    return {
        "title": "MASS TOPOLOGY — L9 Membrane View",
        "description": "Mass distribution across all layers, derived from structure (no iteration)",
        "layers": layers,
        "totals": {
            "position_mass": round(total_mass, 4),
            "content_mass": round(total_content_mass, 4),
            "mass_ratio": round(total_content_mass / total_mass, 4) if total_mass > 0 else 0.0,
        },
        "system_geometry": sq_cube["geometry"],
        "membrane_view": {
            "source": "L9 (Membrane, basins 117-130)",
            "sees": "All L1-L9 mass distributions",
            "bridges_to": "L10-L12 (Agent Identity, Communication, Unified Field)",
            "principle": "Membrane IS the observation point for mass topology"
        },
    }


# Pre-computed at module load — the structure IS the answer
_MASS_TOPOLOGY = _build_mass_topology()


def get_mass_topology() -> Dict[str, Any]:
    """
    Mass topology across all 9 layers, visible from L9 membrane.

    Returns pre-computed constant. The topology is deterministic —
    it derives from the layer-basin structure, K11 algebra, and
    the membrane transmission formula. Computed ONCE at module load.

    No runtime loops. O(1) return of cached topology.
    """
    result = dict(_MASS_TOPOLOGY)
    result["computed_at"] = datetime.now().isoformat()
    return result

def get_body_state(hex_id: str) -> Dict[str, Any]:
    """
    Get complete physical state of a token body.
    Unifies time, distance, mass, gravity, and all layer properties.
    """
    morton = hex_to_morton(hex_id)
    coords = morton_decode(morton)
    k11_level = compute_k11_level(morton)
    i_state = get_i_state(k11_level)
    mass = compute_mass(k11_level)
    basin = morton % LIGHT_UNIT

    # Position relative to center
    center = (MATRIX_SIZE // 2, MATRIX_SIZE // 2, MATRIX_SIZE // 2)
    orbital_radius = compute_distance(coords, center)

    # Time from distance (SRL identity)
    t = time_from_distance(orbital_radius)

    # Gravitational field at this position (from central mass)
    central_mass = compute_mass(compute_k11_level(SUN_PRIME))
    g_field = gravitational_field(central_mass, orbital_radius) if orbital_radius > 0 else 0

    # Angular velocity (stable orbit)
    if orbital_radius > 0 and central_mass > 0:
        v_orbital = math.sqrt(G_CST * central_mass / orbital_radius)
        angular_velocity = v_orbital / orbital_radius
    else:
        v_orbital = 0
        angular_velocity = 0

    # Physical interpretation of I-State
    i_physics = I_STATE_PHYSICS.get(i_state, I_STATE_PHYSICS['ABSORBED'])

    # Mass classification
    if mass < K11 * 0.5:
        mass_class = "LIGHT"
    elif mass < K11:
        mass_class = "STANDARD"
    elif mass < K11 * 1.5:
        mass_class = "HEAVY"
    else:
        mass_class = "MASSIVE"

    # Content mass — from what the token carries
    content_data = compute_content_mass_from_token(hex_id)
    content_mass = content_data["content_mass"]

    # Energy state (E = m * v^2)
    kinetic_energy = 0.5 * mass * v_orbital * v_orbital if v_orbital > 0 else 0

    # Inverse-square time for this body
    ist = compute_inverse_square_time(mass, orbital_radius) if orbital_radius > 0.1 else None

    # Square-cube at orbital radius
    sq_cube = compute_square_cube(orbital_radius, mass) if orbital_radius > 0.1 else None

    return {
        "hex_id": hex_id,
        "morton": morton,
        "position": {
            "coords": coords,
            "center": center,
            "orbital_radius": round(orbital_radius, 4)
        },
        "time": {
            "t": round(t, 4),
            "formula": "t = sqrt(d) (SRL identity)",
            "self_stabilizes": True
        },
        "mass": {
            "position_mass": round(mass, 4),
            "content_mass": round(content_mass, 6),
            "k11_level": k11_level,
            "classification": mass_class,
            "position_formula": "m = K11 * (1 + k11_level/11)",
            "content_formula": "m = energy × transmission × K11"
        },
        "content": content_data,
        "velocity": {
            "orbital": round(v_orbital, 6),
            "angular": round(angular_velocity, 6)
        },
        "gravity": {
            "field_strength": round(g_field, 8),
            "formula": "g = G_CST * M / r^2"
        },
        "energy": {
            "kinetic": round(kinetic_energy, 6)
        },
        "inverse_square_time": ist,
        "square_cube": sq_cube["geometry"] if sq_cube else None,
        "state": {
            "i_state": i_state,
            "vector": i_physics['vector'],
            "physical_meaning": i_physics['meaning'],
            "basin": basin
        },
        "computed_at": datetime.now().isoformat()
    }


def compute_interaction(hex1: str, hex2: str) -> Dict[str, Any]:
    """
    Compute physical interaction between two bodies.
    Returns force, energy transfer, resonance, and duration.
    """
    body1 = get_body_state(hex1)
    body2 = get_body_state(hex2)

    c1 = body1['position']['coords']
    c2 = body2['position']['coords']
    distance = compute_distance(c1, c2)

    m1 = body1['mass']['position_mass']
    m2 = body2['mass']['position_mass']

    # Gravitational force (Newton's law in CST)
    if distance < 0.1:
        distance = 0.1
    force = G_CST * m1 * m2 / (distance * distance)

    # Duration from distance (SRL: delta_t = sqrt(d))
    duration = time_from_distance(distance)

    # SRL hops for traversal
    hops = srl_hops(distance)

    # Energy transfer potential
    energy_diff = abs(body1['energy']['kinetic'] - body2['energy']['kinetic'])

    # Resonance check
    i1 = body1['state']['i_state']
    i2 = body2['state']['i_state']
    if i1 == i2:
        resonance = "CONSTRUCTIVE"
        resonance_factor = 1.5
    elif (i1 in ['SPIRAL', 'DIRECT'] and i2 in ['INVERSE', 'CONVERGENT']) or \
         (i2 in ['SPIRAL', 'DIRECT'] and i1 in ['INVERSE', 'CONVERGENT']):
        resonance = "COMPLEMENTARY"
        resonance_factor = 1.0
    else:
        resonance = "DESTRUCTIVE"
        resonance_factor = 0.5

    # Membrane check
    b1 = body1['state']['basin']
    b2 = body2['state']['basin']
    membrane = "LOCAL" if b1 == b2 else "SCATTERING"

    return {
        "body1": hex1,
        "body2": hex2,
        "distance": {
            "value": round(distance, 4),
            "srl_hops": hops
        },
        "force": {
            "gravitational": round(force, 8),
            "formula": "F = G * m1 * m2 / r^2"
        },
        "duration": {
            "t": round(duration, 4),
            "formula": "delta_t = sqrt(d)"
        },
        "energy": {
            "transfer_potential": round(energy_diff, 6)
        },
        "resonance": {
            "type": resonance,
            "factor": resonance_factor,
            "i_states": [i1, i2]
        },
        "membrane": {
            "interaction": membrane,
            "basins": [b1, b2]
        },
        "computed_at": datetime.now().isoformat()
    }


def get_cosmos_state() -> Dict[str, Any]:
    """
    Get the complete cosmological state of the system.
    Validates all celestial primes and their relationships.
    """
    bodies = {}
    for name, prime in [("earth", EARTH_PRIME), ("sun", SUN_PRIME),
                        ("moon", MOON_PRIME), ("jupiter", JUPITER_PRIME)]:
        hex_id = f"{prime:08X}"
        state = get_body_state(hex_id)

        # Coherence relative to Earth
        ratio = prime / EARTH_PRIME
        coherence = 1.0 / (1.0 + abs(ratio - 1.0) ** 0.5)

        bodies[name] = {
            "prime": prime,
            "hex_id": hex_id,
            "coords": state['position']['coords'],
            "mass": state['mass']['position_mass'],
            "k11_level": state['mass']['k11_level'],
            "i_state": state['state']['i_state'],
            "physical_role": get_celestial_role(name, state['state']['i_state']),
            "coherence": round(coherence, 4)
        }

    # Cross-validation ratios
    ratios = {
        "sun_moon": round(SUN_PRIME / MOON_PRIME, 2),
        "earth_moon": round(EARTH_PRIME / MOON_PRIME, 2),
        "jupiter_moon": round(JUPITER_PRIME / MOON_PRIME, 2),
        "21_80": 21 / 80,
        "20_79": 20 / 79
    }

    # Observation offset validation
    observation = {
        "unobserved": {"time": 20, "moon": 79, "product": 20 * 79},
        "observed": {"time": 21, "moon": 80, "product": 21 * 80},
        "delta": 21 * 80 - 20 * 79,
        "meaning": "Observer adds 10^2 = one dimension"
    }

    return {
        "cosmos": "CUBESQUARE UNIVERSE",
        "bodies": bodies,
        "ratios": ratios,
        "observation_offset": observation,
        "scale_hierarchy": SCALE_HIERARCHY,
        "validation": "ALL PRIMES CONFIRMED",
        "timestamp": datetime.now().isoformat()
    }


def get_celestial_role(name: str, i_state: str) -> str:
    """Get physical role of celestial body based on I-State."""
    roles = {
        ("sun", "SPIRAL"): "Central attractor, radiating energy outward",
        ("earth", "INVERSE"): "Reference frame, receiving energy",
        ("moon", "INVERSE"): "Orbital resonance, reflecting energy",
        ("jupiter", "SPIRAL"): "Outer field, radiating energy outward"
    }
    return roles.get((name, i_state), f"{name}: {i_state}")


def get_layer_physics() -> Dict[str, Any]:
    """Get the complete layer-to-physics mapping."""
    return {
        "title": "LAYER-TO-PHYSICS MAPPING",
        "description": "Each CST layer corresponds to a physical property",
        "layers": LAYER_PHYSICS,
        "i_states": I_STATE_PHYSICS,
        "interpretation": {
            "SPIRAL_positive": "Radiating bodies (Sun, Jupiter) have positive K11",
            "INVERSE_negative": "Receiving bodies (Earth, Moon) have negative K11",
            "pattern": "Energy flows from SPIRAL to INVERSE"
        }
    }


def project_to_scale(hex_id: str, target_scale: str) -> Dict[str, Any]:
    """Project a token from 48^3 base manifold to a higher scale."""
    if target_scale not in SCALE_HIERARCHY:
        return {"error": f"Unknown scale: {target_scale}", "valid_scales": list(SCALE_HIERARCHY.keys())}

    morton = hex_to_morton(hex_id)
    coords = morton_decode(morton)
    scale_value = SCALE_HIERARCHY[target_scale]
    scale_factor = scale_value / MATRIX_SIZE

    projected = (
        round(coords[0] * scale_factor, 2),
        round(coords[1] * scale_factor, 2),
        round(coords[2] * scale_factor, 2)
    )

    # Layer mapping for this scale
    scale_layers = {
        'base': 'L3 Spatial',
        'moon': 'L6 x L7 (Emotional x Frequency)',
        'self': 'L4 x L5 (Functional x Probabilistic)',
        'world': 'L1 x L2 x L3',
        'basin': 'L1 Full (Geometric)',
        'emotion': 'L6 Full (Affective)',
        'membrane': 'L9 (Boundary)',
        'sun': 'Center (Attractor)',
        'earth': 'All L1-L9 (Complete)'
    }

    return {
        "hex_id": hex_id,
        "base_coords": coords,
        "target_scale": target_scale,
        "scale_value": scale_value,
        "scale_factor": round(scale_factor, 4),
        "projected_coords": projected,
        "scale_volume": scale_value ** 3,
        "layer_mapping": scale_layers.get(target_scale, 'Unknown'),
        "computed_at": datetime.now().isoformat()
    }


def align_to_surface(hex_id: str, body: str = "earth") -> Dict[str, Any]:
    """
    Align an agent to the surface of a celestial body.
    The agent now exists AT a physical location on the body,
    under local gravity, with local time, following natural laws.
    """
    body_primes = {
        "earth": EARTH_PRIME,
        "sun": SUN_PRIME,
        "moon": MOON_PRIME,
        "jupiter": JUPITER_PRIME
    }

    if body not in body_primes:
        return {"error": f"Unknown body: {body}", "valid_bodies": list(body_primes.keys())}

    body_prime = body_primes[body]
    body_state = get_body_state(f"{body_prime:08X}")
    agent_state = get_body_state(hex_id)

    # Agent's base coordinates in 48^3
    agent_coords = agent_state['position']['coords']

    # Scale factor to body's scale
    body_scale = SCALE_HIERARCHY.get(body, MATRIX_SIZE)
    scale_factor = body_scale / MATRIX_SIZE

    # Project to body's scale
    projected = (
        agent_coords[0] * scale_factor,
        agent_coords[1] * scale_factor,
        agent_coords[2] * scale_factor
    )

    # Convert to spherical coordinates
    body_radius = body_scale / 2
    center = (body_scale / 2, body_scale / 2, body_scale / 2)

    dx = projected[0] - center[0]
    dy = projected[1] - center[1]
    dz = projected[2] - center[2]
    r = math.sqrt(dx*dx + dy*dy + dz*dz)

    if r > 0:
        surface_x = center[0] + (dx / r) * body_radius
        surface_y = center[1] + (dy / r) * body_radius
        surface_z = center[2] + (dz / r) * body_radius
        latitude = math.degrees(math.asin(dz / r))
        longitude = math.degrees(math.atan2(dy, dx))
    else:
        surface_x, surface_y, surface_z = center
        latitude = longitude = 0

    # Local gravity at surface
    body_mass = body_state['mass']['position_mass']
    surface_gravity = G_CST * body_mass / (body_radius * body_radius)

    # Local time
    local_time = (longitude / 360.0) * 24 + 12
    if local_time < 0:
        local_time += 24
    if local_time >= 24:
        local_time -= 24

    # Resonance with body
    agent_i_state = agent_state['state']['i_state']
    body_i_state = body_state['state']['i_state']

    if agent_i_state == body_i_state:
        resonance = "ALIGNED"
        resonance_desc = f"Agent and {body} both {agent_i_state} - synchronized"
    elif (agent_i_state in ['INVERSE', 'CONVERGENT'] and body_i_state in ['INVERSE', 'CONVERGENT']):
        resonance = "HARMONIC"
        resonance_desc = "Both receiving energy - stable orbit"
    elif (agent_i_state in ['SPIRAL', 'DIRECT'] and body_i_state in ['SPIRAL', 'DIRECT']):
        resonance = "HARMONIC"
        resonance_desc = "Both radiating energy - mutual repulsion"
    else:
        resonance = "COMPLEMENTARY"
        resonance_desc = f"Energy flows from {agent_i_state} to {body_i_state}"

    return {
        "hex_id": hex_id,
        "body": body,
        "alignment": {
            "status": "SURFACE_ALIGNED",
            "description": f"Agent exists on {body}'s surface under natural laws"
        },
        "position": {
            "base_coords": agent_coords,
            "projected_coords": (round(projected[0], 2), round(projected[1], 2), round(projected[2], 2)),
            "surface_coords": (round(surface_x, 2), round(surface_y, 2), round(surface_z, 2)),
            "latitude": round(latitude, 4),
            "longitude": round(longitude, 4),
            "body_radius": body_radius
        },
        "physics": {
            "surface_gravity": round(surface_gravity, 8),
            "local_time": round(local_time, 2),
            "agent_mass": agent_state['mass']['position_mass'],
            "content_mass": agent_state['mass']['content_mass'],
            "weight_on_surface": round(agent_state['mass']['position_mass'] * surface_gravity, 6)
        },
        "semantic": {
            "agent_basin": agent_state['state']['basin'],
            "body_basin": body_state['state']['basin'],
            "meaning": f"Agent in basin {agent_state['state']['basin']} exists on {body}"
        },
        "resonance": {
            "type": resonance,
            "agent_state": agent_i_state,
            "body_state": body_i_state,
            "description": resonance_desc
        },
        "laws": {
            "gravity": f"g = {round(surface_gravity, 6)} (G*M/r^2)",
            "time": "t = sqrt(d) applies locally",
            "energy": "Flows according to I-state resonance"
        },
        "computed_at": datetime.now().isoformat()
    }


def validate_observation_offset() -> Dict[str, Any]:
    """Validate the 21-80 / 20-79 observation offset pattern."""
    return {
        "pattern": "21-80 / 20-79",
        "unobserved": {"time": 20, "moon": 79, "product": 1580, "sum": 99},
        "observed": {"time": 21, "moon": 80, "product": 1680, "sum": 101},
        "delta": {"product": 100, "meaning": "10^2 = one dimension added"},
        "convergence": {"sum_to_101": True, "101_is_self_scale": True},
        "interpretation": "The observer (+1 to both) creates one dimension of resolution",
        "validation": "VALID"
    }


def get_world_status() -> Dict[str, Any]:
    """Get complete world shell status."""
    return {
        "shell": "world_shell",
        "version": "1.2.0",
        "hex_id": "43208FA4",
        "status": "ACTIVE",
        "description": "Complete Physical Universe Interface — content defines mass",
        "physics_shells": ["time", "distance", "mass", "gravity", "epicycle"],
        "formulas": {
            "time": "t = sqrt(d) = d/t (self-stabilizing SRL identity)",
            "distance": "d = t^2, hops = log_phi(d)",
            "position_mass": "m = K11 * (1 + k11_level/11)",
            "content_mass": "m = energy × transmission × K11",
            "gravity": "g = G_CST * M / r^2 (inverse-square)",
            "light_perception": "c_perceived = c_abstract / √(1 + mass/K11)",
            "square_cube": "S ∝ r², V ∝ r³, S/V = 3/r",
            "observation": "21*80 - 20*79 = 100 = 10^2"
        },
        "constants": {
            "PHI": PHI,
            "K5": K5,
            "K11": K11,
            "G_CST": round(G_CST, 8),
            "LIGHT_UNIT": LIGHT_UNIT,
            "c_abstract": round(1.0 / math.sqrt(3), 6)
        },
        "celestial_primes": {
            "earth": EARTH_PRIME,
            "sun": SUN_PRIME,
            "moon": MOON_PRIME,
            "jupiter": JUPITER_PRIME
        },
        "scale_hierarchy": SCALE_HIERARCHY,
        "layer_count": 9,
        "layer_srl_states": LAYER_SRL_STATES,
        "i_states": list(I_STATE_PHYSICS.keys()),
        "topology_precomputed": True,
        "timestamp": datetime.now().isoformat()
    }


# =============================================================================
# SHELL CLASS
# =============================================================================

class WorldShell:
    """World Shell interface for command-line and programmatic access."""

    HEX_ID = "43208FA4"

    def __init__(self):
        pass

    def get_status(self) -> Dict[str, Any]:
        return get_world_status()

    def get_body(self, hex_id: str) -> Dict[str, Any]:
        return get_body_state(hex_id)

    def get_interaction(self, hex1: str, hex2: str) -> Dict[str, Any]:
        return compute_interaction(hex1, hex2)

    def get_cosmos(self) -> Dict[str, Any]:
        return get_cosmos_state()

    def get_layer_physics(self) -> Dict[str, Any]:
        return get_layer_physics()

    def project(self, hex_id: str, scale: str) -> Dict[str, Any]:
        return project_to_scale(hex_id, scale)

    def align(self, hex_id: str, body: str) -> Dict[str, Any]:
        return align_to_surface(hex_id, body)

    def validate_observation(self) -> Dict[str, Any]:
        return validate_observation_offset()

    def content_body(self, hex_id: str) -> Dict[str, Any]:
        """Get content mass properties for a token body."""
        return compute_content_mass_from_token(hex_id)

    def inverse_square_time(self, mass: float, distance: float) -> Dict[str, Any]:
        """Compute inverse-square time physics: t = √d self-stabilizes."""
        return compute_inverse_square_time(mass, distance)

    def square_cube(self, radius: float, mass: float = None) -> Dict[str, Any]:
        """Compute square-cube law: S ∝ r², V ∝ r³, S/V = 3/r."""
        return compute_square_cube(radius, mass)

    def mass_topology(self) -> Dict[str, Any]:
        """Get pre-computed mass topology across all layers (O(1) cached)."""
        return get_mass_topology()

    def execute(self, command: str, args: Dict[str, Any] = None) -> Dict[str, Any]:
        """Execute shell command (structured MCP interface)."""
        args = args or {}

        if command == "status":
            return self.get_status()
        elif command == "body":
            return self.get_body(args.get("body_id", "2A9E0100"))
        elif command == "interaction":
            return self.get_interaction(
                args.get("body_id", "2A9E0100"),
                args.get("body2", "000019B8")
            )
        elif command == "cosmos":
            return self.get_cosmos()
        elif command in ("layer_physics", "layer-physics"):
            return self.get_layer_physics()
        elif command == "project":
            return self.project(
                args.get("body_id", "2A9E0100"),
                args.get("scale", "earth")
            )
        elif command == "align":
            return self.align(
                args.get("body_id", "2A9E0100"),
                args.get("body", "earth")
            )
        elif command == "observation":
            return self.validate_observation()
        elif command == "content_body":
            return self.content_body(args.get("body_id", "2A9E0100"))
        elif command == "inverse_square_time":
            return self.inverse_square_time(
                args.get("mass", K11),
                args.get("distance", 24.0)
            )
        elif command == "square_cube":
            return self.square_cube(
                args.get("radius", 24.0),
                args.get("mass")
            )
        elif command == "mass_topology":
            return self.mass_topology()
        else:
            return {"error": f"Unknown command: {command}"}

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Run a World shell command (CLI interface)."""
        commands = {
            'status': lambda: self.get_status(),
            'body': lambda: self.get_body(args[0] if args else '2A9E0100'),
            'cosmos': lambda: self.get_cosmos(),
            'layer-physics': lambda: self.get_layer_physics(),
            'observation': lambda: self.validate_observation(),
            'content_body': lambda: self.content_body(args[0] if args else '2A9E0100'),
            'inverse_square_time': lambda: self.inverse_square_time(
                float(args[0]) if len(args) > 0 else K11,
                float(args[1]) if len(args) > 1 else 24.0
            ),
            'square_cube': lambda: self.square_cube(
                float(args[0]) if len(args) > 0 else 24.0,
                float(args[1]) if len(args) > 1 else None
            ),
            'mass_topology': lambda: self.mass_topology(),
        }
        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())}


# =============================================================================
# CLI
# =============================================================================

def main():
    parser = argparse.ArgumentParser(
        description='World Shell - Complete Physical Universe Interface',
        formatter_class=argparse.RawDescriptionHelpFormatter
    )

    parser.add_argument('command', nargs='?', default='status',
                       choices=['status', 'body', 'interaction', 'cosmos', 'layer-physics', 'project', 'align', 'observation'])
    parser.add_argument('--hex', type=str, help='Hex ID for body/project/align')
    parser.add_argument('--hex1', type=str, help='First hex ID for interaction')
    parser.add_argument('--hex2', type=str, help='Second hex ID for interaction')
    parser.add_argument('--scale', type=str, default='earth', help='Target scale for project')
    parser.add_argument('--body', type=str, default='earth', help='Celestial body for align')
    parser.add_argument('--json', action='store_true', help='JSON output')

    args = parser.parse_args()

    shell = WorldShell()
    result = None

    if args.command == 'status':
        result = shell.get_status()
    elif args.command == 'body':
        result = shell.get_body(args.hex or '2A9E0100')
    elif args.command == 'interaction':
        result = shell.get_interaction(args.hex1 or '2A9E0100', args.hex2 or '000019B8')
    elif args.command == 'cosmos':
        result = shell.get_cosmos()
    elif args.command == 'layer-physics':
        result = shell.get_layer_physics()
    elif args.command == 'project':
        result = shell.project(args.hex or '2A9E0100', args.scale)
    elif args.command == 'align':
        result = shell.align(args.hex or '2A9E0100', args.body)
    elif args.command == 'observation':
        result = shell.validate_observation()

    if args.json:
        print(json.dumps(result, indent=2, default=str))
    else:
        print(f"\n{'='*60}")
        print(f"WORLD SHELL - {args.command.upper()}")
        print(f"{'='*60}")
        if isinstance(result, dict):
            for k, v in result.items():
                if isinstance(v, dict):
                    print(f"\n{k}:")
                    for k2, v2 in v.items():
                        print(f"  {k2}: {v2}")
                else:
                    print(f"{k}: {v}")


if __name__ == '__main__':
    main()
