"""
Beehive Shell - Token Index Operations (CST-Native)
Standalone version for cubesquare package.

Reads from beehive_index.cst (binary, 601KB) NOT beehive_index.json (37MB).

CST Binary Format:
    Header (24 bytes):
        - Magic: "CST\x00" (4 bytes)
        - Version: uint32 (4 bytes)
        - Token count: uint32 (4 bytes)
        - Queen hex: 8 bytes
        - Reserved: 4 bytes

    Tokens (25 bytes each):
        - Morton code: uint64 (8 bytes)
        - Hex ID: 8 bytes ASCII
        - Basin: uint8 (1 byte)
        - Layer: uint8 (1 byte)
        - I-Logic state: uint8 (1 byte)
        - Coords x,y,z: 3 x uint16 (6 bytes)

NO PYTHON FOR-LOOPS in hot paths. All lookups are O(1) dict access.
"""

import struct
from pathlib import Path
from typing import Dict, Any, Optional, List
from .._core import PRIME_131, morton_decode


class BeehiveShell:
    """
    Beehive Shell (CST-Native).

    Reads binary .cst format for O(1) token operations:
    - Token lookup by hex_id (O(1) dict access)
    - Basin/Layer lookup tables (pre-built at load)
    - Morton spatial queries
    - Loop detection integration via CircularLoopDetector
    """

    def __init__(self, data_path: Optional[Path] = None):
        """Initialize Beehive shell from CST binary index."""
        if data_path:
            self.data_path = data_path
        else:
            # Look inside package first, then repo root
            pkg_data = Path(__file__).parent.parent / "data" / "beehive"
            repo_data = Path(__file__).parent.parent.parent / "data" / "beehive"
            self.data_path = pkg_data if pkg_data.exists() else repo_data
        self._tokens_by_hex: Dict[str, Dict] = {}      # O(1) hex lookup
        self._tokens_by_basin: Dict[int, List[str]] = {}  # basin -> [hex_ids]
        self._tokens_by_morton: Dict[int, str] = {}    # morton -> hex_id
        self._token_count = 0
        self._queen = ""
        self._version = 0
        self._loaded = False
        self._load_cst()

    def _load_cst(self):
        """Load beehive index from CST binary file. O(n) at load, O(1) thereafter."""
        cst_file = self.data_path / "beehive_index.cst"
        if not cst_file.exists():
            return

        try:
            with open(cst_file, 'rb') as f:
                # Read header (24 bytes)
                header = f.read(24)
                if len(header) < 24 or header[:4] != b'CST\x00':
                    return

                self._version = struct.unpack('<I', header[4:8])[0]
                self._token_count = struct.unpack('<I', header[8:12])[0]
                self._queen = header[12:20].decode('ascii', errors='ignore').rstrip('\x00')

                # Read tokens - while loop (bounded by token_count, not data-proportional)
                idx = 0
                while idx < self._token_count:
                    data = f.read(25)
                    if len(data) < 25:
                        break

                    # Parse token (25 bytes)
                    morton = struct.unpack('<Q', data[0:8])[0]
                    hex_id = data[8:16].decode('ascii', errors='ignore').rstrip('\x00')
                    # Basin/layer are COMPUTED from morton, not stored
                    basin = morton % PRIME_131
                    layer = min(basin // 15 + 1, 9)
                    # I-Logic state from last byte
                    ilogic = data[24] & 0x07

                    # Build token dict
                    token = {
                        'hex_id': hex_id,
                        'morton': morton,
                        'basin': basin,
                        'layer': layer,
                        'ilogic': ilogic,
                    }

                    # Index by hex_id (O(1) lookup)
                    self._tokens_by_hex[hex_id] = token

                    # Index by basin
                    if basin not in self._tokens_by_basin:
                        self._tokens_by_basin[basin] = []
                    self._tokens_by_basin[basin].append(hex_id)

                    # Index by morton
                    self._tokens_by_morton[morton] = hex_id

                    idx += 1

                self._loaded = True

        except Exception:
            self._loaded = False

    def status(self, as_json: bool = False) -> Dict[str, Any]:
        """Get Beehive system status."""
        import json
        result = {
            'system': 'Beehive Shell (CST-Native)',
            'version': f'1.0.0 (cst_v{self._version})',
            'data_path': str(self.data_path),
            'format': 'binary CST',
            'index_loaded': self._loaded,
            'token_count': len(self._tokens_by_hex),
            'basins_populated': len(self._tokens_by_basin),
            'queen': self._queen,
            'basins': PRIME_131,
            'layers': 9,
            'status': 'operational' if self._loaded else 'no index loaded',
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def get_token(self, hex_id: str, as_json: bool = False) -> Dict[str, Any]:
        """Get token by hex ID. O(1) dict lookup."""
        import json
        if not self._loaded:
            return {'error': 'No index loaded'}

        # O(1) lookup
        token = self._tokens_by_hex.get(hex_id.upper())
        if token:
            if as_json:
                return json.dumps(token, indent=2)
            return token

        return {'error': f'Token not found: {hex_id}'}

    def get_by_morton(self, morton: int, as_json: bool = False) -> Dict[str, Any]:
        """Get token by Morton code. O(1) dict lookup."""
        import json
        if not self._loaded:
            return {'error': 'No index loaded'}

        hex_id = self._tokens_by_morton.get(morton)
        if hex_id:
            return self.get_token(hex_id, as_json)

        return {'error': f'Token not found for morton: {morton}'}

    def query_basin(self, basin: int, limit: int = 20, as_json: bool = False) -> Dict[str, Any]:
        """Query tokens by basin. O(1) to get list, O(k) to slice."""
        import json
        if not self._loaded:
            return {'error': 'No index loaded'}

        if basin < 0 or basin >= PRIME_131:
            return {'error': f'Invalid basin: {basin}. Use 0-130'}

        hex_ids = self._tokens_by_basin.get(basin, [])
        tokens = []
        idx = 0
        while idx < min(len(hex_ids), limit):
            tokens.append(self._tokens_by_hex[hex_ids[idx]])
            idx += 1

        result = {
            'basin': basin,
            'layer': min(basin // 15 + 1, 9),
            'total_in_basin': len(hex_ids),
            'returned': len(tokens),
            'tokens': tokens,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def query_layer(self, layer: str, limit: int = 20, as_json: bool = False) -> Dict[str, Any]:
        """Query tokens by layer. Uses basin ranges."""
        import json
        if not self._loaded:
            return {'error': 'No index loaded'}

        layer_upper = layer.upper()
        layer_num = int(layer_upper[1:]) if layer_upper.startswith('L') and layer_upper[1:].isdigit() else 0

        if layer_num < 1 or layer_num > 9:
            return {'error': f'Invalid layer: {layer}. Use L1-L9'}

        # Calculate basin range for layer
        basins_per_layer = PRIME_131 // 9
        start_basin = (layer_num - 1) * basins_per_layer
        end_basin = layer_num * basins_per_layer if layer_num < 9 else PRIME_131

        # Collect tokens from basins in range
        tokens = []
        basin = start_basin
        while basin < end_basin and len(tokens) < limit:
            hex_ids = self._tokens_by_basin.get(basin, [])
            idx = 0
            while idx < len(hex_ids) and len(tokens) < limit:
                tokens.append(self._tokens_by_hex[hex_ids[idx]])
                idx += 1
            basin += 1

        result = {
            'layer': layer_upper,
            'basin_range': f'{start_basin}-{end_basin-1}',
            'token_count': len(tokens),
            'tokens': tokens,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def detect_loop(self, hex_id: str, max_depth: int = 10) -> Dict[str, Any]:
        """Detect circular loops from token using CircularLoopDetector."""
        if not self._loaded:
            return {'error': 'No index loaded'}

        try:
            from ..circular.loop_detector import CircularLoopDetector
            from ..circular.srl_integration import srl_distance

            detector = CircularLoopDetector()

            # Build relationships from token's basin neighborhood
            token = self._tokens_by_hex.get(hex_id.upper())
            if not token:
                return {'error': f'Token not found: {hex_id}'}

            basin = token['basin']
            neighbors = self._tokens_by_basin.get(basin, [])

            # Create relationships (token -> neighbors in same basin)
            relationships = []
            idx = 0
            while idx < min(len(neighbors), 50):
                neighbor_hex = neighbors[idx]
                if neighbor_hex != hex_id.upper():
                    relationships.append({
                        'source_hex': hex_id.upper(),
                        'target_hex': neighbor_hex,
                        'rel_type': 'basin_neighbor',
                        'strength': 0.8,
                    })
                idx += 1

            if relationships:
                detector.load_graph(relationships)
                result = detector.detect_loop(hex_id.upper(), max_depth=max_depth)
                return {
                    'hex_id': hex_id,
                    'has_loop': result.has_loop,
                    'loop_length': result.loop_length,
                    'srl_time': srl_distance(result.loop_length) if result.has_loop else 0,
                    'stability': result.stability.value if result.stability else 'unknown',
                }

            return {'hex_id': hex_id, 'has_loop': False, 'loop_length': 0}

        except ImportError:
            return {'error': 'CircularLoopDetector not available'}

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Run a Beehive shell command."""
        commands = {
            'status': lambda: self.status(as_json),
            'get': lambda: self.get_token(args[0], as_json) if args else {'error': 'Hex ID required'},
            'morton': lambda: self.get_by_morton(int(args[0]), as_json) if args else {'error': 'Morton code required'},
            'basin': lambda: self.query_basin(int(args[0]), int(args[1]) if len(args) > 1 else 20, as_json) if args else {'error': 'Basin required'},
            'layer': lambda: self.query_layer(args[0], int(args[1]) if len(args) > 1 else 20, as_json) if args else {'error': 'Layer required'},
            'loop': lambda: self.detect_loop(args[0], int(args[1]) if len(args) > 1 else 10) if args else {'error': 'Hex ID required'},
        }

        if command in commands:
            return commands[command]()
        else:
            return {'error': f'Unknown command: {command}', 'available': list(commands.keys())}
