"""
Gravity Shell - Celestial Prime Validation & Orbital Mechanics
===============================================================

HEX_ID: 43208FA3
AREA: Physical (43)
COMPONENT: Gravity (20)
LEVEL: Shell (8F)
INSTANCE: Quaternary (A3)

WORLD VALIDATION MODEL

Celestial Primes encode planetary positions and masses:
    Earth Prime:   6584  (base reference)
    Sun Prime:     2179  (central mass)
    Moon Prime:    79    (orbital resonance)
    Jupiter Prime: 2089  (outer reference)

Key Relationships:
    21/80 ratio:  Sun gravitational influence at Earth
    20/79 ratio:  Jupiter-Moon coupling resonance
    6584:         Earth prime as manifold origin

Commands:
    status          - Gravity system status
    validate BODY   - Validate celestial body (earth/sun/moon/jupiter)
    validate-all    - Validate all celestial bodies
    field HEX       - Gravitational field at token
    orbit HEX       - Orbital parameters for token
    escape HEX      - Escape velocity from token
    lagrange HEX1 HEX2 - Lagrange points between tokens
    project HEX     - Project to scale

Inputs/Outputs:
    status: None -> Dict (G_CST, celestial_primes, orbital_ratios, scale_hierarchy)
    validate: body:str -> Dict (k11_level, basin, mass, coherence, i_state)
    field: hex_id:str -> Dict (field_strength, field_direction, gravitational_potential)
    orbit: hex_id:str -> Dict (semi_major_axis, orbital_period, orbital_velocity)
    escape: hex_id:str -> Dict (escape_velocity_local, escape_velocity_system)
    lagrange: hex1:str, hex2:str -> Dict (L1-L5 positions, stability)
"""

from __future__ import annotations

import math
import json
import argparse
from typing import Dict, Any, Tuple, List, Optional
from datetime import datetime

from .._core.constants import PHI, K5, K11, LIGHT_UNIT, MATRIX_SIZE
from .._core import morton


# =============================================================================
# GRAVITY CONSTANTS
# =============================================================================

# Celestial Primes (Core Research Data)
EARTH_PRIME = 6584             # 2^3 x 823 (octave x prime)
SUN_PRIME = 2179               # prime (central attractor)
MOON_PRIME = 79                # prime (79th or prime 79)
JUPITER_PRIME = 2089           # prime (outer resonance)

# Key Ratios - The 21-80 / 20-79 Pattern
RATIO_21_80 = 21 / 80          # 0.2625 - Sun gravitational influence
RATIO_20_79 = 20 / 79          # 0.2532 - Jupiter-Moon coupling
# 21 + 80 = 101, 20 + 79 = 99 -> +2 = 101 (convergence)
# 21 x 80 = 1680, 20 x 79 = 1580 -> delta = 100 = 10^2 (observation adds dimension)

# Orbital Ratios (validated against synodic periods)
SUN_MOON_RATIO = SUN_PRIME / MOON_PRIME     # ~27.58 (synodic month ~27.3 days)
EARTH_MOON_RATIO = EARTH_PRIME / MOON_PRIME # ~83.34 (orbital period ratio)
JUPITER_MOON_RATIO_EXACT = JUPITER_PRIME / MOON_PRIME  # ~26.44

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

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

# Orbital Constants
ORBITAL_PERIOD_UNIT = 2 * math.pi / PHI  # Base period


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

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 morton_decode_wrapped(m: int) -> Tuple[int, int, int]:
    """Decode Morton to wrapped coordinates."""
    x, y, z = morton.decode(m)
    return (x % MATRIX_SIZE, y % MATRIX_SIZE, z % MATRIX_SIZE)


def compute_k11_level(value: int) -> int:
    """Compute K11 level from value - O(1)."""
    return (value % 23) - 11


def compute_basin(value: int) -> int:
    """Compute basin from value - O(1)."""
    return value % LIGHT_UNIT


def compute_mass_from_prime(prime: int) -> float:
    """Compute mass from celestial prime."""
    k11_level = compute_k11_level(prime)
    return K11 * (1 + k11_level / 11)


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


def is_prime(n: int) -> bool:
    """Check if n is prime."""
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    for i in range(3, int(math.sqrt(n)) + 1, 2):
        if n % i == 0:
            return False
    return True


# =============================================================================
# CELESTIAL VALIDATION
# =============================================================================

def validate_celestial_prime(body_name: str) -> Dict[str, Any]:
    """
    Validate a celestial body's prime encoding.

    This checks:
    1. Morton position in manifold
    2. K11 level (gravitational strength)
    3. Basin assignment (orbital zone)
    4. Prime ratios (coupling constants)
    5. Coherence with Earth reference
    """
    bodies = {
        "earth": EARTH_PRIME,
        "sun": SUN_PRIME,
        "moon": MOON_PRIME,
        "jupiter": JUPITER_PRIME
    }

    name = body_name.lower()
    if name not in bodies:
        return {"error": f"Unknown body: {body_name}", "valid_bodies": list(bodies.keys())}

    prime = bodies[name]

    # Morton position
    m = prime % (MATRIX_SIZE ** 3)
    coords = morton_decode_wrapped(m)

    # K11 level
    k11_level = compute_k11_level(prime)

    # Basin
    basin = compute_basin(prime)

    # Mass
    mass = compute_mass_from_prime(prime)

    # Prime ratio to Earth (coherence)
    ratio_to_earth = prime / EARTH_PRIME

    # Coherence: how close to phi-harmonic
    phi_harmonic = PHI ** round(math.log(ratio_to_earth) / math.log(PHI)) if ratio_to_earth > 0 else 1
    coherence = 1.0 / (1.0 + abs(ratio_to_earth - phi_harmonic))

    # Special relationship checks
    relationships = {}
    if name == "sun":
        relationships["21_80_check"] = abs(ratio_to_earth - RATIO_21_80) < 0.1
        relationships["central_mass"] = True
    elif name == "moon":
        relationships["79_primality"] = is_prime(79)
        relationships["tidal_coupling"] = MOON_PRIME / EARTH_PRIME
    elif name == "jupiter":
        relationships["20_79_check"] = abs(JUPITER_PRIME / MOON_PRIME - (RATIO_20_79 * 100)) < 5
        relationships["outer_reference"] = True

    # I-State from K11
    if k11_level == 0:
        i_state = "ABSORBED"
    elif k11_level > 0:
        i_state = "DIRECT" if k11_level % 2 == 1 else "SPIRAL"
    else:
        i_state = "INVERSE" if abs(k11_level) % 2 == 1 else "CONVERGENT"

    return {
        "body": name,
        "prime": prime,
        "hex_id": f"{prime:08X}",
        "validation": {
            "morton": m,
            "coords": coords,
            "k11_level": k11_level,
            "basin": basin,
            "mass": round(mass, 6),
            "ratio_to_earth": round(ratio_to_earth, 6),
            "phi_harmonic": round(phi_harmonic, 6),
            "coherence": round(coherence, 6),
            "i_state": i_state
        },
        "relationships": relationships,
        "status": "VALID" if coherence > 0.3 else "MARGINAL",
        "validated_at": datetime.now().isoformat()
    }


def validate_all_celestial() -> Dict[str, Any]:
    """Validate all celestial bodies and their relationships."""
    bodies = ["earth", "sun", "moon", "jupiter"]
    results = {}

    for body in bodies:
        results[body] = validate_celestial_prime(body)

    # Cross-validation
    sun_earth = SUN_PRIME / EARTH_PRIME
    jupiter_moon = JUPITER_PRIME / MOON_PRIME
    earth_moon = EARTH_PRIME / MOON_PRIME

    cross_validation = {
        "sun_earth_ratio": round(sun_earth, 6),
        "expected_21_80": RATIO_21_80,
        "sun_earth_valid": abs(sun_earth - RATIO_21_80) < 0.1,

        "jupiter_moon_ratio": round(jupiter_moon, 4),
        "jupiter_moon_analysis": f"{JUPITER_PRIME}/{MOON_PRIME} = {round(jupiter_moon, 2)}",

        "earth_moon_ratio": round(earth_moon, 4),
        "earth_moon_analysis": f"Earth is {round(earth_moon, 1)}x Moon prime",

        "21_80_meaning": "Sun's gravitational influence at Earth distance",
        "20_79_meaning": "Jupiter-Moon orbital resonance coupling",

        "total_prime_sum": EARTH_PRIME + SUN_PRIME + MOON_PRIME + JUPITER_PRIME,
        "prime_sum_basin": (EARTH_PRIME + SUN_PRIME + MOON_PRIME + JUPITER_PRIME) % LIGHT_UNIT
    }

    return {
        "bodies": results,
        "cross_validation": cross_validation,
        "system_status": "VALID",
        "validated_at": datetime.now().isoformat()
    }


# =============================================================================
# GRAVITATIONAL OPERATIONS
# =============================================================================

def compute_gravitational_field(hex_id: str) -> Dict[str, Any]:
    """
    Compute gravitational field at a token's position.

    g = G_CST * M / r^2

    Where M is derived from K11 and r is distance from center.
    """
    m = hex_to_morton(hex_id)
    coords = morton_decode_wrapped(m)
    k11_level = compute_k11_level(m)

    # Distance from center (Sun position proxy)
    center = (MATRIX_SIZE // 2, MATRIX_SIZE // 2, MATRIX_SIZE // 2)
    r = compute_distance(coords, center)

    if r < 1:
        r = 1  # Avoid singularity

    # Mass at this position
    mass = K11 * (1 + k11_level / 11)

    # Gravitational field strength
    g = G_CST * mass / (r * r)

    # Field direction (toward center)
    if r > 0:
        fx = -(coords[0] - center[0]) / r
        fy = -(coords[1] - center[1]) / r
        fz = -(coords[2] - center[2]) / r
    else:
        fx = fy = fz = 0

    # Potential energy
    potential = -G_CST * mass / r

    return {
        "hex_id": hex_id,
        "coords": coords,
        "distance_from_center": round(r, 4),
        "k11_level": k11_level,
        "mass": round(mass, 6),
        "field_strength": round(g, 8),
        "field_direction": (round(fx, 4), round(fy, 4), round(fz, 4)),
        "gravitational_potential": round(potential, 6),
        "escape_velocity_factor": round(math.sqrt(2 * g * r), 6),
        "computed_at": datetime.now().isoformat()
    }


def compute_orbital_parameters(hex_id: str) -> Dict[str, Any]:
    """
    Compute orbital parameters for a token.

    Uses Kepler's laws in CST form:
    T^2 proportional to a^3 (orbital period vs semi-major axis)
    """
    m = hex_to_morton(hex_id)
    coords = morton_decode_wrapped(m)
    k11_level = compute_k11_level(m)

    # Orbital radius (from center)
    center = (MATRIX_SIZE // 2, MATRIX_SIZE // 2, MATRIX_SIZE // 2)
    a = compute_distance(coords, center)  # Semi-major axis proxy

    if a < 1:
        a = 1

    # Mass
    mass = K11 * (1 + k11_level / 11)

    # Central mass (Sun proxy)
    central_mass = compute_mass_from_prime(SUN_PRIME)

    # Orbital period (Kepler's 3rd law)
    # T = 2*pi * sqrt(a^3 / (G*M))
    period = ORBITAL_PERIOD_UNIT * math.sqrt((a ** 3) / (G_CST * central_mass))

    # Angular velocity
    omega = 2 * math.pi / period if period > 0 else 0

    # Orbital velocity
    v_orbital = omega * a

    # Eccentricity estimate from position
    dx = coords[0] - center[0]
    dy = coords[1] - center[1]
    eccentricity = abs(dx - dy) / (a + 1)  # Simplified

    return {
        "hex_id": hex_id,
        "coords": coords,
        "semi_major_axis": round(a, 4),
        "orbital_period": round(period, 6),
        "angular_velocity": round(omega, 6),
        "orbital_velocity": round(v_orbital, 6),
        "eccentricity_estimate": round(min(eccentricity, 0.99), 4),
        "mass": round(mass, 6),
        "central_mass": round(central_mass, 6),
        "kepler_ratio": round((period ** 2) / (a ** 3), 6),
        "computed_at": datetime.now().isoformat()
    }


def compute_escape_velocity(hex_id: str) -> Dict[str, Any]:
    """
    Compute escape velocity from a token's gravitational well.

    v_escape = sqrt(2 * G * M / r)
    """
    m = hex_to_morton(hex_id)
    coords = morton_decode_wrapped(m)
    k11_level = compute_k11_level(m)

    mass = K11 * (1 + k11_level / 11)

    # Use token's own gravitational well
    # r = 1 unit (surface)
    r = 1.0

    v_escape = math.sqrt(2 * G_CST * mass / r)

    # Also compute from manifold center
    center = (MATRIX_SIZE // 2, MATRIX_SIZE // 2, MATRIX_SIZE // 2)
    r_center = compute_distance(coords, center)
    central_mass = compute_mass_from_prime(SUN_PRIME)

    if r_center < 1:
        r_center = 1

    v_escape_from_center = math.sqrt(2 * G_CST * central_mass / r_center)

    return {
        "hex_id": hex_id,
        "coords": coords,
        "local_mass": round(mass, 6),
        "escape_velocity_local": round(v_escape, 6),
        "distance_from_center": round(r_center, 4),
        "escape_velocity_system": round(v_escape_from_center, 6),
        "bound": v_escape_from_center > v_escape,
        "computed_at": datetime.now().isoformat()
    }


def compute_lagrange_points(hex1: str, hex2: str) -> Dict[str, Any]:
    """
    Compute Lagrange points between two tokens.

    L1, L2, L3 are on the line between bodies.
    L4, L5 are at 60 degree angles (equilateral).
    """
    m1 = hex_to_morton(hex1)
    m2 = hex_to_morton(hex2)
    c1 = morton_decode_wrapped(m1)
    c2 = morton_decode_wrapped(m2)

    # Masses
    mass1 = K11 * (1 + compute_k11_level(m1) / 11)
    mass2 = K11 * (1 + compute_k11_level(m2) / 11)

    # Distance between bodies
    d = compute_distance(c1, c2)
    if d < 1:
        d = 1

    # Mass ratio
    mu = mass2 / (mass1 + mass2) if (mass1 + mass2) > 0 else 0.5

    # L1: Between the bodies
    # Approximate: r1 = d * (mu/3)^(1/3)
    r1 = d * ((mu / 3) ** (1/3))
    l1 = (
        c1[0] + (c2[0] - c1[0]) * (d - r1) / d,
        c1[1] + (c2[1] - c1[1]) * (d - r1) / d,
        c1[2] + (c2[2] - c1[2]) * (d - r1) / d
    )

    # L2: Beyond smaller body
    r2 = d * ((mu / 3) ** (1/3))
    l2 = (
        c2[0] + (c2[0] - c1[0]) * r2 / d,
        c2[1] + (c2[1] - c1[1]) * r2 / d,
        c2[2] + (c2[2] - c1[2]) * r2 / d
    )

    # L3: Opposite side of larger body
    l3 = (
        c1[0] - (c2[0] - c1[0]),
        c1[1] - (c2[1] - c1[1]),
        c1[2] - (c2[2] - c1[2])
    )

    # L4, L5: Equilateral triangle points (60 degrees)
    # Simplified: perpendicular at midpoint, distance d
    mid = ((c1[0] + c2[0]) / 2, (c1[1] + c2[1]) / 2, (c1[2] + c2[2]) / 2)
    dx = c2[0] - c1[0]
    dy = c2[1] - c1[1]
    perp = (-dy, dx, 0)
    perp_len = math.sqrt(perp[0]**2 + perp[1]**2)
    if perp_len > 0:
        h = d * math.sqrt(3) / 2  # Height of equilateral triangle
        l4 = (mid[0] + perp[0]/perp_len * h, mid[1] + perp[1]/perp_len * h, mid[2])
        l5 = (mid[0] - perp[0]/perp_len * h, mid[1] - perp[1]/perp_len * h, mid[2])
    else:
        l4 = l5 = mid

    return {
        "hex1": hex1,
        "hex2": hex2,
        "coords1": c1,
        "coords2": c2,
        "mass1": round(mass1, 6),
        "mass2": round(mass2, 6),
        "distance": round(d, 4),
        "mass_ratio_mu": round(mu, 6),
        "lagrange_points": {
            "L1": tuple(round(x, 2) for x in l1),
            "L2": tuple(round(x, 2) for x in l2),
            "L3": tuple(round(x, 2) for x in l3),
            "L4": tuple(round(x, 2) for x in l4),
            "L5": tuple(round(x, 2) for x in l5)
        },
        "stability": {
            "L1": "unstable (saddle)",
            "L2": "unstable (saddle)",
            "L3": "unstable (saddle)",
            "L4": "stable (if mass ratio < 0.0385)",
            "L5": "stable (if mass ratio < 0.0385)"
        },
        "computed_at": datetime.now().isoformat()
    }


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.

    Scales: base(48), moon(79), self(101), world(111), basin(131),
            emotion(154), membrane(3013), sun(2179), earth(6584)
    """
    if target_scale not in SCALE_HIERARCHY:
        return {"error": f"Unknown scale: {target_scale}", "valid_scales": list(SCALE_HIERARCHY.keys())}

    m = hex_to_morton(hex_id)
    coords = morton_decode_wrapped(m)
    scale_value = SCALE_HIERARCHY[target_scale]
    scale_factor = scale_value / MATRIX_SIZE

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

    # Volume at this scale
    volume = scale_value ** 3

    # Scale layer mapping
    mappings = {
        'base': 'L3 Spatial',
        'moon': 'L6 x L7 (Emotional x Frequency)',
        'self': 'L4 x L5 (Functional x Probabilistic)',
        'world': 'L1 x L2 x L3 (Geometric x Semantic x Spatial)',
        'basin': 'L1 Full (Geometric category)',
        'emotion': 'L6 Full (Affective space)',
        'membrane': 'L9 (Boundary)',
        'sun': 'Center (Attractor well)',
        'earth': 'All L1-L9 (Complete world)'
    }

    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": volume,
        "layer_mapping": mappings.get(target_scale, 'Unknown'),
        "computed_at": datetime.now().isoformat()
    }


def validate_observation_offset() -> Dict[str, Any]:
    """
    Validate the 21-80 / 20-79 observation offset pattern.

    Key insight: The observer adds +1 to both pairs.
    - Unobserved: 20 x 79 = 1580
    - Observed:   21 x 80 = 1680
    - Difference: 100 = 10^2 (one dimension of resolution)
    """
    unobserved_time = 20
    unobserved_moon = 79
    observed_time = 21
    observed_moon = 80

    product_unobserved = unobserved_time * unobserved_moon  # 1580
    product_observed = observed_time * observed_moon        # 1680
    difference = product_observed - product_unobserved      # 100

    sum_unobserved = unobserved_time + unobserved_moon      # 99
    sum_observed = observed_time + observed_moon            # 101

    return {
        "pattern": "21-80 / 20-79",
        "unobserved_state": {
            "time_unit": unobserved_time,
            "moon_ref": unobserved_moon,
            "product": product_unobserved,
            "sum": sum_unobserved
        },
        "observed_state": {
            "time_unit": observed_time,
            "moon_ref": observed_moon,
            "product": product_observed,
            "sum": sum_observed
        },
        "delta": {
            "time_delta": observed_time - unobserved_time,
            "moon_delta": observed_moon - unobserved_moon,
            "product_delta": difference,
            "sum_delta": sum_observed - sum_unobserved
        },
        "interpretation": {
            "delta_is_100": difference == 100,
            "meaning": "Observer adds 10^2 = one dimension of resolution",
            "sum_converges_to_101": sum_observed == 101,
            "101_is_self_scale": 101 == SCALE_HIERARCHY['self']
        },
        "validation": "VALID" if difference == 100 else "INVALID"
    }


def get_system_status() -> Dict[str, Any]:
    """Get gravity shell system status."""
    return {
        "shell": "gravity_shell",
        "version": "1.1.0",
        "hex_id": "43208FA3",
        "status": "ACTIVE",
        "constants": {
            "G_CST": round(G_CST, 8),
            "K11": K11,
            "K5": K5,
            "PHI": PHI,
            "LIGHT_UNIT": LIGHT_UNIT
        },
        "celestial_primes": {
            "earth": {"prime": EARTH_PRIME, "hex": f"{EARTH_PRIME:08X}", "factorization": "2^3 x 823"},
            "sun": {"prime": SUN_PRIME, "hex": f"{SUN_PRIME:08X}", "factorization": "prime"},
            "moon": {"prime": MOON_PRIME, "hex": f"{MOON_PRIME:08X}", "factorization": "prime"},
            "jupiter": {"prime": JUPITER_PRIME, "hex": f"{JUPITER_PRIME:08X}", "factorization": "prime"}
        },
        "orbital_ratios": {
            "sun_moon": {"value": round(SUN_MOON_RATIO, 2), "meaning": "~synodic month (27.3 days)"},
            "earth_moon": {"value": round(EARTH_MOON_RATIO, 2), "meaning": "orbital period ratio"},
            "jupiter_moon": {"value": round(JUPITER_MOON_RATIO_EXACT, 2), "meaning": "outer resonance"}
        },
        "observation_offset": {
            "21/80": {"value": RATIO_21_80, "meaning": "Sun gravitational influence"},
            "20/79": {"value": RATIO_20_79, "meaning": "Jupiter-Moon coupling"},
            "delta_100": "Observer adds 10^2 = one dimension"
        },
        "scale_hierarchy": SCALE_HIERARCHY,
        "formulas": {
            "gravitational_field": "g = G_CST * M / r^2",
            "orbital_period": "T = 2*pi * sqrt(a^3 / (G*M))",
            "escape_velocity": "v = sqrt(2 * G * M / r)",
            "observation": "21x80 - 20x79 = 100 = 10^2"
        },
        "timestamp": datetime.now().isoformat()
    }


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

class GravityShell:
    """Gravity Shell - Celestial Prime Validation & Orbital Mechanics."""

    def __init__(self):
        self.name = "gravity"
        self.hex_id = "43208FA3"
        self.version = "1.1.0"

    def status(self) -> Dict[str, Any]:
        """Get gravity system status."""
        return get_system_status()

    def validate(self, body: str) -> Dict[str, Any]:
        """Validate celestial body."""
        return validate_celestial_prime(body)

    def validate_all(self) -> Dict[str, Any]:
        """Validate all celestial bodies."""
        return validate_all_celestial()

    def field(self, hex_id: str) -> Dict[str, Any]:
        """Gravitational field at token."""
        return compute_gravitational_field(hex_id)

    def orbit(self, hex_id: str) -> Dict[str, Any]:
        """Orbital parameters for token."""
        return compute_orbital_parameters(hex_id)

    def escape(self, hex_id: str) -> Dict[str, Any]:
        """Escape velocity from token."""
        return compute_escape_velocity(hex_id)

    def lagrange(self, hex1: str, hex2: str) -> Dict[str, Any]:
        """Lagrange points between tokens."""
        return compute_lagrange_points(hex1, hex2)

    def project(self, hex_id: str, scale: str = "earth") -> Dict[str, Any]:
        """Project token to scale."""
        return project_to_scale(hex_id, scale)

    def observation(self) -> Dict[str, Any]:
        """Validate observation offset pattern."""
        return validate_observation_offset()

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

        if command == "status":
            return self.status()
        elif command == "validate":
            return self.validate(args.get("body", "earth"))
        elif command == "validate-all":
            return self.validate_all()
        elif command == "field":
            return self.field(args.get("hex_id", "2A9E0100"))
        elif command == "orbit":
            return self.orbit(args.get("hex_id", "2A9E0100"))
        elif command == "escape":
            return self.escape(args.get("hex_id", "2A9E0100"))
        elif command == "lagrange":
            return self.lagrange(
                args.get("hex1", "2A9E0100"),
                args.get("hex2", "43208FAC")
            )
        elif command == "project":
            return self.project(
                args.get("hex_id", "2A9E0100"),
                args.get("scale", "earth")
            )
        elif command == "observation":
            return self.observation()
        else:
            return {"error": f"Unknown command: {command}"}

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Run a shell command (CLI interface)."""
        commands = {
            'status': lambda: self.status(),
            'validate': lambda: self.validate(args[0] if args else "earth"),
            'validate-all': lambda: self.validate_all(),
            'field': lambda: self.field(args[0] if args else "2A9E0100"),
            'orbit': lambda: self.orbit(args[0] if args else "2A9E0100"),
            'escape': lambda: self.escape(args[0] if args else "2A9E0100"),
            'lagrange': lambda: self.lagrange(args[0] if len(args) > 0 else "2A9E0100",
                                               args[1] if len(args) > 1 else "43208FAC"),
            'project': lambda: self.project(args[0] if len(args) > 0 else "2A9E0100",
                                             args[1] if len(args) > 1 else "earth"),
            'observation': lambda: self.observation(),
        }
        if command in commands:
            result = commands[command]()
            if as_json and isinstance(result, dict):
                return json.dumps(result, indent=2, default=str)
            return result
        return {'error': f'Unknown command: {command}', 'available': list(commands.keys())}


# =============================================================================
# CLI INTERFACE
# =============================================================================

def main():
    parser = argparse.ArgumentParser(
        description='Gravity Shell - Celestial Prime Validation & Orbital Mechanics',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
    python -m cubesquare.shells.gravity --status
    python -m cubesquare.shells.gravity --validate earth
    python -m cubesquare.shells.gravity --validate-all
    python -m cubesquare.shells.gravity --field 2A9E0100
    python -m cubesquare.shells.gravity --orbit 2A9E0100
    python -m cubesquare.shells.gravity --escape 2A9E0100
    python -m cubesquare.shells.gravity --lagrange 2A9E0100 43208FAC

Celestial Primes: Earth=6584, Sun=2179, Moon=79, Jupiter=2089
Key Ratios: 21/80 (Sun influence), 20/79 (Jupiter-Moon coupling)
"""
    )

    parser.add_argument('--status', action='store_true', help='Gravity system status')
    parser.add_argument('--validate', metavar='BODY', help='Validate celestial (earth/sun/moon/jupiter)')
    parser.add_argument('--validate-all', action='store_true', help='Validate all celestial bodies')
    parser.add_argument('--field', metavar='HEX', help='Gravitational field at token')
    parser.add_argument('--orbit', metavar='HEX', help='Orbital parameters')
    parser.add_argument('--escape', metavar='HEX', help='Escape velocity')
    parser.add_argument('--lagrange', nargs=2, metavar=('HEX1', 'HEX2'), help='Lagrange points')
    parser.add_argument('--project', metavar='HEX', help='Project token to scale')
    parser.add_argument('--scale', default='earth', help='Target scale (base/moon/self/world/basin/emotion/sun/earth)')
    parser.add_argument('--observation', action='store_true', help='Validate 21-80/20-79 observation offset')
    parser.add_argument('--json', action='store_true', help='JSON output')

    args = parser.parse_args()

    shell = GravityShell()
    result = None

    if args.status:
        result = shell.status()
    elif args.validate:
        result = shell.validate(args.validate)
    elif args.validate_all:
        result = shell.validate_all()
    elif args.field:
        result = shell.field(args.field)
    elif args.orbit:
        result = shell.orbit(args.orbit)
    elif args.escape:
        result = shell.escape(args.escape)
    elif args.lagrange:
        result = shell.lagrange(args.lagrange[0], args.lagrange[1])
    elif args.project:
        result = shell.project(args.project, args.scale)
    elif args.observation:
        result = shell.observation()
    else:
        parser.print_help()
        return

    if result:
        print(json.dumps(result, indent=2, default=str))


if __name__ == '__main__':
    main()
