"""
Distance Shell - Spatial O(1) Distance Computation
===================================================

HEX_ID: 43208FA1
AREA: Physical (43)
COMPONENT: Distance (20)
LEVEL: Shell (8F)
INSTANCE: Secondary (A1)

Core Principle: d = t^2 (inverse of t = sqrt(d))
Distance is the squared time. SRL hops computed via log_phi lookup.

The manifold has 48^3 = 110,592 cells. Each cell has a unique Morton code.
Distance between any two tokens is O(1) via Morton difference or coordinate Euclidean.

Commands:
    status          - Distance system status
    compute HEX1 HEX2  - Distance between two hex IDs
    radius HEX r    - Find tokens in radius
    nearest HEX n   - Find N nearest neighbors
    path HEX1 HEX2  - Compute optimal SRL path

Inputs/Outputs:
    status: None -> Dict (constants, metrics, celestial distances)
    compute: hex1:str, hex2:str -> Dict (euclidean, manhattan, chebyshev, morton, srl_hops)
    radius: hex_id:str, r:float -> Dict (theoretical_cells, volume_ratio)
    nearest: hex_id:str, n:int -> Dict (nearest neighbors list)
    path: hex1:str, hex2:str -> Dict (path points via phi-weighted interpolation)
"""

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


# =============================================================================
# DISTANCE CONSTANTS
# =============================================================================

# Phi depth lookup table for O(1) SRL hops
PHI_DEPTH_TABLE = [PHI ** n for n in range(32)]

# Celestial reference distances (in matrix units)
EARTH_DISTANCE = 1.0                     # Reference (AU equivalent)
MOON_DISTANCE = 0.00257                  # ~384,400 km / 149.6M km
SUN_DISTANCE = 0.0                       # Center
JUPITER_DISTANCE = 5.2                   # 5.2 AU


# =============================================================================
# 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  # Lower 20 bits
    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 hex_to_coords(hex_id: str) -> Tuple[int, int, int]:
    """Get coordinates from hex ID - O(1)."""
    m = hex_to_morton(hex_id)
    return morton_decode_wrapped(m)


def euclidean_distance(p1: Tuple[int, int, int], p2: Tuple[int, int, int]) -> float:
    """Euclidean distance between two points - 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 manhattan_distance(p1: Tuple[int, int, int], p2: Tuple[int, int, int]) -> int:
    """Manhattan distance between two points - O(1)."""
    return abs(p1[0] - p2[0]) + abs(p1[1] - p2[1]) + abs(p1[2] - p2[2])


def chebyshev_distance(p1: Tuple[int, int, int], p2: Tuple[int, int, int]) -> int:
    """Chebyshev (chessboard) distance - O(1)."""
    return max(abs(p1[0] - p2[0]), abs(p1[1] - p2[1]), abs(p1[2] - p2[2]))


def morton_distance(m1: int, m2: int) -> int:
    """Distance in Morton space - O(1)."""
    return abs(m1 - m2)


def srl_hops(distance: float) -> int:
    """
    Compute SRL hops via phi-depth lookup - O(1).
    Hops = floor(log_phi(distance))
    """
    if distance <= 1:
        return 0
    for i, phi_power in enumerate(PHI_DEPTH_TABLE):
        if phi_power > distance:
            return i - 1
    return len(PHI_DEPTH_TABLE) - 1


def time_from_distance(distance: float) -> float:
    """SRL formula: t = sqrt(d) - O(1)."""
    if distance <= 0:
        return 0.0
    return math.sqrt(distance)


def distance_from_time(t: float) -> float:
    """Inverse SRL: d = t^2 - O(1)."""
    return t * t


# =============================================================================
# DISTANCE OPERATIONS
# =============================================================================

def compute_distance(hex1: str, hex2: str) -> Dict[str, Any]:
    """
    Compute all distance metrics between two hex IDs.
    All operations O(1).
    """
    m1 = hex_to_morton(hex1)
    m2 = hex_to_morton(hex2)
    c1 = morton_decode_wrapped(m1)
    c2 = morton_decode_wrapped(m2)

    # All distances
    euclidean = euclidean_distance(c1, c2)
    manhattan = manhattan_distance(c1, c2)
    chebyshev = chebyshev_distance(c1, c2)
    morton_diff = morton_distance(m1, m2)

    # SRL metrics
    hops = srl_hops(euclidean)
    time = time_from_distance(euclidean)

    # Basin difference
    basin1 = m1 % LIGHT_UNIT
    basin2 = m2 % LIGHT_UNIT
    basin_diff = abs(basin1 - basin2)

    return {
        "hex1": hex1,
        "hex2": hex2,
        "coords1": c1,
        "coords2": c2,
        "morton1": m1,
        "morton2": m2,
        "distances": {
            "euclidean": round(euclidean, 4),
            "manhattan": manhattan,
            "chebyshev": chebyshev,
            "morton": morton_diff
        },
        "srl": {
            "hops": hops,
            "time": round(time, 4),
            "phi_depth": hops
        },
        "basins": {
            "basin1": basin1,
            "basin2": basin2,
            "difference": basin_diff
        },
        "computed_at": datetime.now().isoformat()
    }


def find_in_radius(hex_id: str, radius: float) -> Dict[str, Any]:
    """
    Find all tokens within radius of hex_id.
    Without index, returns theoretical cell count.
    """
    center = hex_to_coords(hex_id)

    # Calculate theoretical cells in sphere
    # Volume = (4/3) * pi * r^3
    volume = (4/3) * math.pi * (radius ** 3)
    cells_in_sphere = min(int(volume), MATRIX_SIZE ** 3)

    # Without full index, return theoretical result
    return {
        "center": hex_id,
        "center_coords": center,
        "radius": radius,
        "theoretical_cells": cells_in_sphere,
        "volume_ratio": cells_in_sphere / (MATRIX_SIZE ** 3),
        "note": "Full enumeration requires beehive index",
        "computed_at": datetime.now().isoformat()
    }


def find_nearest(hex_id: str, n: int = 5) -> Dict[str, Any]:
    """
    Find N nearest neighbors using Morton locality.
    Morton codes preserve spatial locality - nearby codes = nearby positions.
    """
    m = hex_to_morton(hex_id)
    coords = morton_decode_wrapped(m)

    # Generate nearby Morton codes (O(n) but constant small n)
    neighbors = []
    for dx in range(-2, 3):
        for dy in range(-2, 3):
            for dz in range(-2, 3):
                if dx == 0 and dy == 0 and dz == 0:
                    continue
                nx = (coords[0] + dx) % MATRIX_SIZE
                ny = (coords[1] + dy) % MATRIX_SIZE
                nz = (coords[2] + dz) % MATRIX_SIZE
                nm = morton.encode(nx, ny, nz)
                dist = euclidean_distance(coords, (nx, ny, nz))
                neighbors.append({
                    "morton": nm,
                    "hex": f"{nm:08X}",
                    "coords": (nx, ny, nz),
                    "distance": round(dist, 4)
                })

    # Sort by distance and take top N
    neighbors.sort(key=lambda x: x["distance"])
    nearest = neighbors[:n]

    return {
        "center": hex_id,
        "center_coords": coords,
        "n_requested": n,
        "nearest": nearest,
        "computed_at": datetime.now().isoformat()
    }


def compute_path(hex1: str, hex2: str) -> Dict[str, Any]:
    """
    Compute optimal SRL path between two tokens.
    Uses phi-recursive hopping for O(log_phi(d)) traversal.
    """
    m1 = hex_to_morton(hex1)
    m2 = hex_to_morton(hex2)
    c1 = morton_decode_wrapped(m1)
    c2 = morton_decode_wrapped(m2)

    euclidean = euclidean_distance(c1, c2)
    hops = srl_hops(euclidean)

    # Build path via phi-steps
    path = [{"step": 0, "hex": hex1, "coords": c1}]

    if hops > 0:
        # Interpolate using phi-division
        for i in range(1, hops + 1):
            t = i / (hops + 1)
            # Phi-weighted interpolation
            phi_t = t ** (1 / PHI)
            ix = int(c1[0] + (c2[0] - c1[0]) * phi_t)
            iy = int(c1[1] + (c2[1] - c1[1]) * phi_t)
            iz = int(c1[2] + (c2[2] - c1[2]) * phi_t)
            im = morton.encode(ix % MATRIX_SIZE, iy % MATRIX_SIZE, iz % MATRIX_SIZE)
            path.append({
                "step": i,
                "hex": f"{im:08X}",
                "coords": (ix % MATRIX_SIZE, iy % MATRIX_SIZE, iz % MATRIX_SIZE),
                "t": round(phi_t, 4)
            })

    path.append({"step": len(path), "hex": hex2, "coords": c2})

    return {
        "start": hex1,
        "end": hex2,
        "distance": round(euclidean, 4),
        "srl_hops": hops,
        "path_length": len(path),
        "path": path,
        "computed_at": datetime.now().isoformat()
    }


def get_system_status() -> Dict[str, Any]:
    """Get distance shell system status."""
    return {
        "shell": "distance_shell",
        "version": "1.0.0",
        "hex_id": "43208FA1",
        "status": "ACTIVE",
        "constants": {
            "PHI": PHI,
            "MATRIX_SIZE": MATRIX_SIZE,
            "LIGHT_UNIT": LIGHT_UNIT,
            "TOTAL_CELLS": MATRIX_SIZE ** 3
        },
        "metrics": [
            "euclidean",
            "manhattan",
            "chebyshev",
            "morton"
        ],
        "srl_formula": "d = t^2 | hops = log_phi(d)",
        "phi_depth_levels": len(PHI_DEPTH_TABLE),
        "celestial_distances": {
            "earth": EARTH_DISTANCE,
            "moon": MOON_DISTANCE,
            "sun": SUN_DISTANCE,
            "jupiter": JUPITER_DISTANCE
        },
        "timestamp": datetime.now().isoformat()
    }


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

class DistanceShell:
    """Distance Shell - Spatial O(1) Distance Computation."""

    def __init__(self):
        self.name = "distance"
        self.hex_id = "43208FA1"
        self.version = "1.0.0"

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

    def compute(self, hex1: str, hex2: str) -> Dict[str, Any]:
        """Compute distance between two hex IDs."""
        return compute_distance(hex1, hex2)

    def radius(self, hex_id: str, r: float = 5.0) -> Dict[str, Any]:
        """Find tokens in radius."""
        return find_in_radius(hex_id, r)

    def nearest(self, hex_id: str, n: int = 5) -> Dict[str, Any]:
        """Find N nearest neighbors."""
        return find_nearest(hex_id, n)

    def path(self, hex1: str, hex2: str) -> Dict[str, Any]:
        """Compute optimal SRL path."""
        return compute_path(hex1, hex2)

    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 == "compute":
            return self.compute(
                args.get("hex1", "2A9E0100"),
                args.get("hex2", "43208FAC")
            )
        elif command == "radius":
            return self.radius(
                args.get("hex_id", "2A9E0100"),
                args.get("r", 5.0)
            )
        elif command == "nearest":
            return self.nearest(
                args.get("hex_id", "2A9E0100"),
                args.get("n", 5)
            )
        elif command == "path":
            return self.path(
                args.get("hex1", "2A9E0100"),
                args.get("hex2", "43208FAC")
            )
        else:
            return {"error": f"Unknown command: {command}"}

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Run a Distance shell command (CLI interface)."""
        result = self.execute(command, {"hex_id": args[0] if args else "2A9E0100"})
        if as_json:
            return json.dumps(result, indent=2)
        return result


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

def main():
    parser = argparse.ArgumentParser(
        description='Distance Shell - Spatial O(1) Distance Computation',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
    python -m cubesquare.shells.distance --status
    python -m cubesquare.shells.distance --compute 2A9E0100 43208FAC
    python -m cubesquare.shells.distance --radius 2A9E0100 --r 10
    python -m cubesquare.shells.distance --nearest 2A9E0100 --n 5
    python -m cubesquare.shells.distance --path 2A9E0100 43208FAC

Core Formula: d = t^2, hops = log_phi(d)
"""
    )

    parser.add_argument('--status', action='store_true', help='Distance system status')
    parser.add_argument('--compute', nargs=2, metavar=('HEX1', 'HEX2'), help='Distance between hexes')
    parser.add_argument('--radius', metavar='HEX', help='Find tokens in radius')
    parser.add_argument('--r', type=float, default=5.0, help='Radius value (default: 5.0)')
    parser.add_argument('--nearest', metavar='HEX', help='Find N nearest neighbors')
    parser.add_argument('--n', type=int, default=5, help='Number of neighbors (default: 5)')
    parser.add_argument('--path', nargs=2, metavar=('HEX1', 'HEX2'), help='Compute optimal path')
    parser.add_argument('--json', action='store_true', help='JSON output')

    args = parser.parse_args()

    shell = DistanceShell()
    result = None

    if args.status:
        result = shell.status()
    elif args.compute:
        result = shell.compute(args.compute[0], args.compute[1])
    elif args.radius:
        result = shell.radius(args.radius, args.r)
    elif args.nearest:
        result = shell.nearest(args.nearest, args.n)
    elif args.path:
        result = shell.path(args.path[0], args.path[1])
    else:
        parser.print_help()
        return

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


if __name__ == '__main__':
    main()
