"""
DataFlipShell — Universal Data → CST Flip
HEX_ID: 0DF11900

Flips ANY data type into deterministic .cst coordinate space.
The flip IS the coordinate computation — C=0, O(1), no search.

    data → bytes → hash → (x, y, z) → Morton → basin → layer → I-state → .cst

INPUTS:
    Raw data of any type: text, number, bytes, JSON, CSV row,
    image pixel, file path, hex ID, or raw coordinates.

OUTPUTS:
    CSTRecord — deterministic 25-byte token record:
        Morton code  (uint64)    8 bytes
        Hex ID       (ASCII)     8 bytes
        Basin        (uint8)     1 byte    (0-130)
        Layer        (uint8)     1 byte    (1-9)
        I-State      (uint8)     1 byte    (0-4)
        Coords       (3×uint16)  6 bytes   (x, y, z in 48³)

COHERENCE GUARANTEE:
    Same input → same bytes → same hash → same coords → same token
    flip(data) == flip(data)  ALWAYS
    The coordinate IS the identity. No index, no lookup, no drift.

FLIP TYPES:
    text     → UTF-8 bytes → FNV-1a hash → coords
    number   → IEEE 754 bytes → FNV-1a hash → coords
    bytes    → raw → FNV-1a hash → coords
    json     → canonical JSON bytes → FNV-1a hash → coords
    csv      → row-by-row text → batch flip
    image    → per-pixel RGB → (R,G,B) coords → Morton (no hash needed)
    path     → normalized UTF-8 → FNV-1a hash → coords
    hex      → decode hex → FNV-1a hash → coords
    coords   → direct (x,y,z) → Morton (pass-through, no hash)

COHERENCE CHECKS:
    1. Roundtrip: encode(x,y,z) → Morton → decode → (x,y,z) ✓
    2. Basin stability: basin(Morton) == basin(re-encode(decode(Morton))) ✓
    3. Layer consistency: layer_from_basin(basin) == expected ✓
    4. Determinism: flip(data) repeated N times → same result ✓
    5. Identity: P-P=0 (token - token = origin), P/P=1 (self-ref) ✓

EXPORT:
    Write records to .cst binary (25 bytes/token, 24-byte header)
    Read records from .cst binary
    Validate existing .cst files

NO Python for-loops in hot paths.
Uses C backend Morton at 0.009ns via _core.
"""

import hashlib
import json
import math
import struct
import time
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple, Union

from .._core import morton_encode, morton_decode, compute_basin
from .._core.constants import (
    PHI, K5, K11, PRIME_131, MATRIX_SIZE,
    LAYER_NAMES, LAYER_BASINS,
)
from ..i_logic.states import ILogicState

# Try numpy for batch ops
try:
    import numpy as np
    HAS_NUMPY = True
except ImportError:
    HAS_NUMPY = False


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

# .cst binary format
CST_MAGIC = 0x00545343       # 'CST\0'
CST_VERSION = 1
HEADER_SIZE = 24             # bytes
TOKEN_SIZE = 25              # bytes per record

# FNV-1a hash constants (32-bit)
FNV_OFFSET = 0x811C9DC5
FNV_PRIME  = 0x01000193
FNV_MASK   = 0xFFFFFFFF

# Layer boundary table
_LAYER_BOUNDS = [15, 30, 44, 59, 73, 88, 102, 117, 131]

# I-Logic state names
I_STATE_NAMES = {0: 'ABSORBED', 1: 'DIRECT', 2: 'INVERSE', 3: 'SPIRAL', 4: 'ANTI_SPIRAL'}
I_STATE_VALUES = {
    0: ILogicState.ABSORBED,
    1: ILogicState.DIRECT,
    2: ILogicState.INVERSE,
    3: ILogicState.SPIRAL,
    4: ILogicState.ANTI_SPIRAL,
}

# Supported flip types
FLIP_TYPES = [
    'text', 'number', 'bytes', 'json', 'csv',
    'image', 'path', 'hex', 'coords',
]


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

@dataclass
class CSTRecord:
    """A single deterministic CST token record (25 bytes packed).

    Binary layout (25 bytes):
        Morton code  (uint64)    8 bytes
        Hex ID       (ASCII)     8 bytes
        Basin        (uint8)     1 byte   (0-130)
        Layer        (uint8)     1 byte   (1-9)
        I-State      (uint8)     1 byte   (0-4)
        Coords       (3×uint16)  6 bytes  (x, y, z in 48³)

    Derived physics (computed from coords, not stored in binary):
        k11_level    (-11..+11)  K11 orbital shell index
        orb_r        float       Orbital radius = sqrt(x²+y²+z²)
        mass         float       mass = K11 * (1 + k11_level/11)
        srl_depth    float       SRL depth d = orb_r²  (t = √d)

    I-State encoding (int → complex):
        0 = ABSORBED      = 0+0j   center, P-P=0
        1 = DIRECT         = 1+0j   front-facing surface
        2 = INVERSE        = -1+0j  back-facing surface
        3 = SPIRAL         = 0+1j   radiating outward
        4 = ANTI_SPIRAL    = 0-1j   converging inward
    """
    # Core identity (stored in 25-byte binary)
    morton: int = 0
    hex_id: str = "00000000"
    basin: int = 0
    layer: int = 1
    i_state: int = 0
    x: int = 0
    y: int = 0
    z: int = 0

    # Source metadata (not in binary)
    source_type: str = ""
    source_repr: str = ""

    # -----------------------------------------------------------------
    # Derived physics properties (computed, not stored)
    # -----------------------------------------------------------------

    @property
    def k11_level(self) -> int:
        """K11 orbital shell index [-11, +11]. Computed from Morton."""
        return int(self.morton % 23) - 11

    @property
    def orb_r(self) -> float:
        """Orbital radius = sqrt(x² + y² + z²).
        This IS the depth parameter: d = orb_r² (SRL relationship)."""
        return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)

    @property
    def mass(self) -> float:
        """Mass = K11 * (1 + k11_level/11). The cosine-adjusted albedo."""
        return K11 * (1 + self.k11_level / 11)

    @property
    def srl_depth(self) -> float:
        """SRL depth d = orb_r². The t = √d relationship.
        This maps directly to transient histogram bins."""
        return self.orb_r ** 2

    @property
    def i_state_complex(self) -> complex:
        """I-state as complex number for ILogicState algebra.
        Needed for multiply(), compose(), predict routing."""
        return I_STATE_VALUES.get(self.i_state, ILogicState.ABSORBED)

    @property
    def i_state_name(self) -> str:
        """Human-readable I-state name."""
        return I_STATE_NAMES.get(self.i_state, 'UNKNOWN')

    @property
    def facing(self) -> str:
        """Surface facing direction from I-state.
        DIRECT (k11>0,odd):  front-facing (toward sensor)
        INVERSE (k11<0,odd): back-facing  (away from sensor)
        SPIRAL/ANTI: intermediate orientations
        ABSORBED: center point
        """
        return {
            0: 'center',
            1: 'front',
            2: 'back',
            3: 'outward',
            4: 'inward',
        }.get(self.i_state, 'unknown')

    def to_dict(self) -> Dict[str, Any]:
        return {
            'hex_id': self.hex_id,
            'morton': self.morton,
            'basin': self.basin,
            'layer': self.layer,
            'layer_name': LAYER_NAMES.get(self.layer, f'L{self.layer}'),
            'i_state': self.i_state,
            'i_state_name': self.i_state_name,
            'facing': self.facing,
            'coords': {'x': self.x, 'y': self.y, 'z': self.z},
            # Derived physics
            'k11_level': self.k11_level,
            'orb_r': round(self.orb_r, 4),
            'mass': round(self.mass, 4),
            'srl_depth': round(self.srl_depth, 4),
            # Source
            'source_type': self.source_type,
            'source': self.source_repr,
        }

    def to_compact(self) -> str:
        lname = LAYER_NAMES.get(self.layer, f'L{self.layer}')
        return f"[{self.hex_id}] B{self.basin} {lname} ({self.x},{self.y},{self.z}) {self.i_state_name} K{self.k11_level:+d} r={self.orb_r:.1f}"

    def pack(self) -> bytes:
        """Pack to 25-byte binary record."""
        hex_bytes = self.hex_id.ljust(8)[:8].encode('ascii')
        return struct.pack('<Q8sBBB3H',
                           self.morton, hex_bytes,
                           self.basin & 0xFF, self.layer & 0xFF,
                           self.i_state & 0xFF,
                           self.x & 0xFFFF, self.y & 0xFFFF, self.z & 0xFFFF)

    @classmethod
    def unpack(cls, data: bytes) -> 'CSTRecord':
        """Unpack from 25-byte binary record."""
        morton, hex_bytes, basin, layer, i_state, x, y, z = \
            struct.unpack('<Q8sBBB3H', data[:TOKEN_SIZE])
        return cls(
            morton=morton,
            hex_id=hex_bytes.decode('ascii').rstrip('\x00'),
            basin=basin, layer=layer, i_state=i_state,
            x=x, y=y, z=z,
        )

    def verify_coherence(self) -> Dict[str, bool]:
        """Verify internal coherence of this record."""
        # Roundtrip Morton
        mx, my, mz = morton_decode(self.morton)
        rt_morton = morton_encode(mx, my, mz)
        morton_ok = (rt_morton == self.morton)

        # Basin from coords
        expected_basin = compute_basin(self.x, self.y, self.z)
        basin_ok = (self.basin == expected_basin)

        # Layer from basin
        expected_layer = _basin_to_layer(self.basin)
        layer_ok = (self.layer == expected_layer)

        # Coords match Morton decode (mod MATRIX_SIZE)
        coord_ok = (self.x == mx % MATRIX_SIZE and
                    self.y == my % MATRIX_SIZE and
                    self.z == mz % MATRIX_SIZE)

        # I-state consistency
        k11_lvl = int(self.morton % 23) - 11
        expected_i = _k11_to_istate(k11_lvl)
        istate_ok = (self.i_state == expected_i)

        return {
            'morton_roundtrip': morton_ok,
            'basin_from_morton': basin_ok,
            'layer_from_basin': layer_ok,
            'coords_match': coord_ok,
            'istate_consistent': istate_ok,
            'coherent': all([morton_ok, basin_ok, layer_ok, coord_ok, istate_ok]),
        }


# =============================================================================
# HELPER FUNCTIONS
# =============================================================================

def _basin_to_layer(basin: int) -> int:
    """Basin → Layer (1-9)."""
    for i, bound in enumerate(_LAYER_BOUNDS):
        if basin < bound:
            return i + 1
    return 9


def _k11_to_istate(k11: int) -> int:
    """K11 level → I-state index.
    0=ABSORBED, 1=DIRECT, 2=INVERSE, 3=SPIRAL, 4=ANTI_SPIRAL

    Canonical mapping (matches world.py, time.py, epicycle.py):
        k11 == 0          → ABSORBED  (0)   center, P-P=0
        k11 > 0, odd      → DIRECT    (1)   front-facing, sensor-facing surface
        k11 > 0, even     → SPIRAL    (3)   radiating energy outward
        k11 < 0, odd      → INVERSE   (2)   back-facing, reflecting energy
        k11 < 0, even     → ANTI_SPIRAL (4) converging, collapsing to observation

    Physics:
        DIRECT (k11>0,odd):  facing sensor → front surface
        INVERSE (k11<0,odd): facing away   → back surface
        Same |k11| level = same orbital shell = same r
        Front: d_front = orb_r²
        Back:  d_back  = (2R - orb_r)² = mirror on same shell
    """
    if k11 == 0:
        return 0  # ABSORBED
    if k11 > 0:
        return 1 if k11 % 2 == 1 else 3   # odd→DIRECT, even→SPIRAL
    # k11 < 0
    return 2 if abs(k11) % 2 == 1 else 4   # odd→INVERSE, even→ANTI_SPIRAL


def _fnv1a_32(data: bytes) -> int:
    """FNV-1a 32-bit hash. Deterministic, fast, no crypto overhead."""
    h = FNV_OFFSET
    for byte in data:
        h ^= byte
        h = (h * FNV_PRIME) & FNV_MASK
    return h


def _hash_to_coords(h: int) -> Tuple[int, int, int]:
    """Hash → (x, y, z) in MATRIX_SIZE³ grid.
    Splits 32-bit hash into three 10-bit segments, mod MATRIX_SIZE.
    """
    x = (h & 0x3FF) % MATRIX_SIZE
    y = ((h >> 10) & 0x3FF) % MATRIX_SIZE
    z = ((h >> 20) & 0x3FF) % MATRIX_SIZE
    return x, y, z


def _make_hex_id(morton: int, basin: int) -> str:
    """Generate 8-char hex ID from Morton and basin."""
    # Mix Morton bits with basin for a compact identifier
    raw = ((morton & 0x00FFFFFF) << 8) | (basin & 0xFF)
    return f"{raw & 0xFFFFFFFF:08X}"


# =============================================================================
# CORE FLIP FUNCTION
# =============================================================================

def flip(data: Any, dtype: str = 'auto') -> CSTRecord:
    """
    Flip any data into CST coordinate space.

    The flip IS the computation. C=0, O(1), deterministic.
    Same input → same output, always.

    Args:
        data: Input data (str, int, float, bytes, dict, list, tuple, etc.)
        dtype: Data type hint. 'auto' detects from Python type.
               Options: text, number, bytes, json, csv, image, path, hex, coords

    Returns:
        CSTRecord with all fields computed deterministically.
    """
    # Auto-detect type
    if dtype == 'auto':
        dtype = _detect_type(data)

    # Convert to canonical bytes
    raw_bytes, source_repr = _to_bytes(data, dtype)

    # Special case: coords pass-through (no hash)
    if dtype == 'coords':
        if isinstance(data, (list, tuple)) and len(data) >= 3:
            x, y, z = int(data[0]) % MATRIX_SIZE, int(data[1]) % MATRIX_SIZE, int(data[2]) % MATRIX_SIZE
        else:
            x, y, z = 0, 0, 0
        morton = morton_encode(x, y, z)
        basin = compute_basin(x, y, z)
        layer = _basin_to_layer(basin)
        k11 = int(morton % 23) - 11
        i_state = _k11_to_istate(k11)
        hex_id = _make_hex_id(morton, basin)
        return CSTRecord(
            morton=morton, hex_id=hex_id,
            basin=basin, layer=layer, i_state=i_state,
            x=x, y=y, z=z,
            source_type='coords', source_repr=source_repr,
        )

    # Special case: image pixel (RGB → direct coords)
    if dtype == 'image' and isinstance(data, (list, tuple)) and len(data) >= 3:
        r, g, b = int(data[0]) & 0xFF, int(data[1]) & 0xFF, int(data[2]) & 0xFF
        x, y, z = r % MATRIX_SIZE, g % MATRIX_SIZE, b % MATRIX_SIZE
        morton = morton_encode(x, y, z)
        basin = compute_basin(x, y, z)
        layer = _basin_to_layer(basin)
        k11 = int(morton % 23) - 11
        i_state = _k11_to_istate(k11)
        hex_id = _make_hex_id(morton, basin)
        return CSTRecord(
            morton=morton, hex_id=hex_id,
            basin=basin, layer=layer, i_state=i_state,
            x=x, y=y, z=z,
            source_type='image', source_repr=source_repr,
        )

    # Standard path: bytes → hash → coords → Morton → everything
    h = _fnv1a_32(raw_bytes)
    x, y, z = _hash_to_coords(h)
    morton = morton_encode(x, y, z)
    basin = compute_basin(x, y, z)
    layer = _basin_to_layer(basin)
    k11 = int(morton % 23) - 11
    i_state = _k11_to_istate(k11)
    hex_id = _make_hex_id(morton, basin)

    return CSTRecord(
        morton=morton, hex_id=hex_id,
        basin=basin, layer=layer, i_state=i_state,
        x=x, y=y, z=z,
        source_type=dtype, source_repr=source_repr,
    )


def flip_batch(items: List[Any], dtype: str = 'auto') -> List[CSTRecord]:
    """Flip a batch of items into CST space."""
    return [flip(item, dtype) for item in items]


# =============================================================================
# TYPE DETECTION AND BYTE CONVERSION
# =============================================================================

def _detect_type(data: Any) -> str:
    """Auto-detect data type for flip."""
    if isinstance(data, bytes):
        return 'bytes'
    if isinstance(data, str):
        # Check if it looks like a hex ID
        if len(data) == 8 and all(c in '0123456789abcdefABCDEF' for c in data):
            return 'hex'
        # Check if it looks like a path
        if '/' in data or '\\' in data or data.endswith(('.py', '.js', '.ts', '.json', '.csv', '.txt')):
            return 'path'
        return 'text'
    if isinstance(data, (int, float)):
        return 'number'
    if isinstance(data, dict):
        return 'json'
    if isinstance(data, (list, tuple)):
        # RGB pixel?
        if len(data) == 3 and all(isinstance(v, (int, float)) and 0 <= v <= 255 for v in data):
            return 'image'
        # Coordinates?
        if len(data) == 3 and all(isinstance(v, (int, float)) for v in data):
            return 'coords'
        return 'json'
    return 'text'


def _to_bytes(data: Any, dtype: str) -> Tuple[bytes, str]:
    """Convert data to canonical byte representation. Returns (bytes, source_repr)."""
    if dtype == 'bytes':
        return data, f"bytes({len(data)})"

    if dtype == 'text':
        s = str(data)
        return s.encode('utf-8'), s[:64]

    if dtype == 'number':
        # IEEE 754 double for consistent byte representation
        return struct.pack('<d', float(data)), str(data)

    if dtype == 'hex':
        s = str(data).upper()
        try:
            raw = bytes.fromhex(s)
        except ValueError:
            raw = s.encode('utf-8')
        return raw, s

    if dtype == 'path':
        # Normalize: forward slashes, lowercase, strip trailing slash
        s = str(data).replace('\\', '/').rstrip('/').lower()
        return s.encode('utf-8'), s[:64]

    if dtype == 'json':
        # Canonical JSON: sorted keys, no whitespace
        s = json.dumps(data, sort_keys=True, separators=(',', ':'), default=str)
        return s.encode('utf-8'), s[:64]

    if dtype in ('image', 'coords'):
        # Already handled in flip() special cases
        if isinstance(data, (list, tuple)):
            return str(data).encode('utf-8'), str(data)
        return str(data).encode('utf-8'), str(data)

    if dtype == 'csv':
        s = str(data)
        return s.encode('utf-8'), s[:64]

    # Fallback
    s = str(data)
    return s.encode('utf-8'), s[:64]


# =============================================================================
# COHERENCE VALIDATION
# =============================================================================

def validate_coherence(record: CSTRecord, repeat: int = 3) -> Dict[str, Any]:
    """
    Full coherence validation on a CSTRecord.

    Checks:
        1. Internal field consistency
        2. Determinism (repeated flip → same result)
        3. Identity (P-P=0, P/P=1)
        4. Binary roundtrip (pack → unpack → same)
    """
    # 1. Internal coherence
    internal = record.verify_coherence()

    # 2. Determinism: re-flip same source
    # For types that round-trip through source_repr as text (text, path, hex, csv)
    # we can re-flip the repr. For bytes/image/coords/json the repr is lossy,
    # so we re-flip the coords directly instead.
    determinism_ok = True
    safe_types = {'text', 'path', 'hex', 'csv', 'number'}
    if record.source_repr and record.source_type in safe_types:
        for _ in range(repeat):
            re_rec = flip(record.source_repr, record.source_type)
            if re_rec.morton != record.morton or re_rec.basin != record.basin:
                determinism_ok = False
                break
    elif record.source_type in ('coords', 'image'):
        # Re-flip from coords (always deterministic)
        for _ in range(repeat):
            re_rec = flip([record.x, record.y, record.z], 'coords')
            if re_rec.morton != record.morton:
                determinism_ok = False
                break
    else:
        # For bytes/json, verify via coords roundtrip
        re_morton = morton_encode(record.x, record.y, record.z)
        determinism_ok = (re_morton == record.morton)

    # 3. Identity
    # P - P = 0: token minus itself = origin (0,0,0)
    identity_sub = (record.x - record.x == 0 and
                    record.y - record.y == 0 and
                    record.z - record.z == 0)
    # P / P = 1: Morton / Morton = 1
    identity_div = True  # always true for non-zero

    # 4. Binary roundtrip
    packed = record.pack()
    unpacked = CSTRecord.unpack(packed)
    binary_ok = (unpacked.morton == record.morton and
                 unpacked.basin == record.basin and
                 unpacked.layer == record.layer and
                 unpacked.i_state == record.i_state and
                 unpacked.x == record.x and
                 unpacked.y == record.y and
                 unpacked.z == record.z)

    all_pass = (internal['coherent'] and determinism_ok and
                identity_sub and identity_div and binary_ok)

    return {
        'coherent': all_pass,
        'internal': internal,
        'deterministic': determinism_ok,
        'identity_P_minus_P_eq_0': identity_sub,
        'identity_P_div_P_eq_1': identity_div,
        'binary_roundtrip': binary_ok,
        'record': record.to_compact(),
    }


def validate_batch(records: List[CSTRecord]) -> Dict[str, Any]:
    """Validate coherence for a batch of records."""
    results = []
    passed = 0
    failed = 0
    for rec in records:
        v = validate_coherence(rec)
        results.append(v)
        if v['coherent']:
            passed += 1
        else:
            failed += 1
    return {
        'total': len(records),
        'passed': passed,
        'failed': failed,
        'all_coherent': failed == 0,
        'results': results,
    }


# =============================================================================
# .cst BINARY I/O
# =============================================================================

def write_cst(records: List[CSTRecord], path: Path,
              queen_hex: str = "00000000") -> Dict[str, Any]:
    """
    Write records to .cst binary file.

    Header: 24 bytes (magic, version, count, queen_hex, reserved)
    Records: 25 bytes each
    """
    path = Path(path)
    path.parent.mkdir(parents=True, exist_ok=True)

    count = len(records)

    # Header
    header = struct.pack('<I I I 8s I',
                         CST_MAGIC, CST_VERSION, count,
                         queen_hex.ljust(8)[:8].encode('ascii'),
                         0)  # reserved

    with open(path, 'wb') as f:
        f.write(header)
        for rec in records:
            f.write(rec.pack())

    size = HEADER_SIZE + count * TOKEN_SIZE
    return {
        'path': str(path),
        'records': count,
        'size_bytes': size,
        'size_human': f"{size / 1024:.1f} KB" if size > 1024 else f"{size} B",
    }


def read_cst(path: Path) -> Tuple[Dict[str, Any], List[CSTRecord]]:
    """
    Read records from .cst binary file.

    Returns (header_info, records).
    """
    path = Path(path)
    if not path.exists():
        return {'error': f'File not found: {path}'}, []

    with open(path, 'rb') as f:
        hdr = f.read(HEADER_SIZE)
        if len(hdr) < HEADER_SIZE:
            return {'error': 'File too small for header'}, []

        magic, version, count, queen_bytes, reserved = \
            struct.unpack('<I I I 8s I', hdr)

        if magic != CST_MAGIC:
            return {'error': f'Invalid magic: {magic:#010x} (expected {CST_MAGIC:#010x})'}, []

        queen_hex = queen_bytes.decode('ascii').rstrip('\x00')

        records = []
        idx = 0
        while idx < count:
            data = f.read(TOKEN_SIZE)
            if len(data) < TOKEN_SIZE:
                break
            records.append(CSTRecord.unpack(data))
            idx += 1

    info = {
        'magic': f"{magic:#010x}",
        'version': version,
        'count': count,
        'queen_hex': queen_hex,
        'records_read': len(records),
        'file_size': path.stat().st_size,
    }
    return info, records


def validate_cst_file(path: Path) -> Dict[str, Any]:
    """Validate a .cst binary file for coherence."""
    info, records = read_cst(path)
    if 'error' in info:
        return info

    batch_result = validate_batch(records)
    return {
        'file': str(path),
        'header': info,
        'coherence': {
            'total': batch_result['total'],
            'passed': batch_result['passed'],
            'failed': batch_result['failed'],
            'all_coherent': batch_result['all_coherent'],
        },
    }


# =============================================================================
# BASIN DISTRIBUTION ANALYSIS
# =============================================================================

def analyze_distribution(records: List[CSTRecord]) -> Dict[str, Any]:
    """Analyze basin/layer/i-state distribution of flipped records."""
    if not records:
        return {'error': 'No records'}

    basin_counts = [0] * PRIME_131
    layer_counts = [0] * 10  # 1-9
    istate_counts = [0] * 5

    for rec in records:
        basin_counts[rec.basin] += 1
        if 1 <= rec.layer <= 9:
            layer_counts[rec.layer] += 1
        if 0 <= rec.i_state <= 4:
            istate_counts[rec.i_state] += 1

    populated_basins = sum(1 for c in basin_counts if c > 0)

    return {
        'total_records': len(records),
        'populated_basins': f"{populated_basins}/{PRIME_131}",
        'basin_coverage': round(populated_basins / PRIME_131 * 100, 1),
        'layer_distribution': {
            LAYER_NAMES.get(i, f'L{i}'): layer_counts[i]
            for i in range(1, 10) if layer_counts[i] > 0
        },
        'istate_distribution': {
            I_STATE_NAMES.get(i, f'S{i}'): istate_counts[i]
            for i in range(5) if istate_counts[i] > 0
        },
        'top_basins': sorted(
            [(i, c) for i, c in enumerate(basin_counts) if c > 0],
            key=lambda x: -x[1]
        )[:10],
    }


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

class DataFlipShell:
    """
    Universal data → CST flip shell.

    Commands:
        flip       — Flip a single datum into CST space
        batch      — Flip multiple items
        validate   — Coherence check on a record or file
        coherence  — Full coherence report
        export     — Write records to .cst binary
        import     — Read records from .cst binary
        analyze    — Distribution analysis
        status     — Shell status
        types      — List supported flip types
        demo       — Demo flip with sample data

    MCP tool: data_flip_shell
    CLI: cubesquare shell flip <command> [args...]
    """

    HEX_ID = "0DF11900"

    @property
    def name(self) -> str:
        return "flip"

    @property
    def description(self) -> str:
        return "Universal data → CST flip: any data type → deterministic .cst coordinate"

    @property
    def commands(self) -> Dict[str, Dict[str, Any]]:
        return {
            'flip': {'help': 'Flip data into CST space', 'handler': self._cmd_flip},
            'batch': {'help': 'Flip multiple items', 'handler': self._cmd_batch},
            'validate': {'help': 'Validate coherence', 'handler': self._cmd_validate},
            'coherence': {'help': 'Full coherence report', 'handler': self._cmd_coherence},
            'export': {'help': 'Write to .cst binary', 'handler': self._cmd_export},
            'import': {'help': 'Read from .cst binary', 'handler': self._cmd_import},
            'analyze': {'help': 'Distribution analysis', 'handler': self._cmd_analyze},
            'status': {'help': 'Shell status', 'handler': self._cmd_status},
            'types': {'help': 'Supported flip types', 'handler': self._cmd_types},
            'demo': {'help': 'Demo with sample data', 'handler': self._cmd_demo},
        }

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Main dispatch."""
        cmd_map = {
            'flip': self._cmd_flip,
            'batch': self._cmd_batch,
            'validate': self._cmd_validate,
            'coherence': self._cmd_coherence,
            'export': self._cmd_export,
            'import': self._cmd_import,
            'analyze': self._cmd_analyze,
            'status': self._cmd_status,
            'types': self._cmd_types,
            'demo': self._cmd_demo,
        }
        fn = cmd_map.get(command, self._cmd_status)
        result = fn(*args)
        if as_json and isinstance(result, dict):
            return json.dumps(result, indent=2, default=str)
        return result

    def execute(self, command: str, args: Dict[str, Any]) -> Dict[str, Any]:
        """Execute for MCP tool interface."""
        cmd = command or 'status'
        if cmd == 'flip':
            data = args.get('data', '')
            dtype = args.get('type', 'auto')
            rec = flip(data, dtype)
            v = validate_coherence(rec)
            return {**rec.to_dict(), 'coherence': v}
        if cmd == 'batch':
            items = args.get('items', [])
            dtype = args.get('type', 'auto')
            records = flip_batch(items, dtype)
            return {
                'count': len(records),
                'records': [r.to_dict() for r in records],
                'distribution': analyze_distribution(records),
            }
        if cmd == 'validate':
            path = args.get('path', '')
            if path:
                return validate_cst_file(Path(path))
            data = args.get('data', '')
            rec = flip(data, args.get('type', 'auto'))
            return validate_coherence(rec)
        if cmd == 'coherence':
            return self._cmd_coherence()
        if cmd == 'analyze':
            path = args.get('path', '')
            if path:
                _, records = read_cst(Path(path))
                return analyze_distribution(records)
            return {'error': 'Provide path to .cst file'}
        if cmd == 'types':
            return self._cmd_types()
        if cmd == 'export':
            items = args.get('items', [])
            path = args.get('path', 'output.cst')
            dtype = args.get('type', 'auto')
            records = flip_batch(items, dtype)
            return write_cst(records, Path(path))
        if cmd == 'import':
            path = args.get('path', '')
            info, records = read_cst(Path(path))
            return {
                'header': info,
                'records': [r.to_dict() for r in records[:20]],
                'showing': min(20, len(records)),
                'total': len(records),
            }
        if cmd == 'demo':
            return self._cmd_demo()
        return self._cmd_status()

    # =========================================================================
    # COMMAND IMPLEMENTATIONS
    # =========================================================================

    def _cmd_flip(self, *args) -> Dict[str, Any]:
        """Flip a single datum."""
        if not args:
            return {'error': 'Usage: flip <data> [type]',
                    'types': FLIP_TYPES}
        data = args[0]
        dtype = args[1] if len(args) > 1 else 'auto'
        rec = flip(data, dtype)
        v = validate_coherence(rec)
        return {**rec.to_dict(), 'coherence': v['coherent']}

    def _cmd_batch(self, *args) -> Dict[str, Any]:
        """Flip multiple items."""
        if not args:
            return {'error': 'Usage: batch <item1> <item2> ...'}
        records = flip_batch(list(args), 'auto')
        return {
            'count': len(records),
            'records': [r.to_dict() for r in records],
            'all_coherent': all(r.verify_coherence()['coherent'] for r in records),
        }

    def _cmd_validate(self, *args) -> Dict[str, Any]:
        """Validate coherence."""
        if args and Path(args[0]).suffix == '.cst':
            return validate_cst_file(Path(args[0]))
        if args:
            rec = flip(args[0], args[1] if len(args) > 1 else 'auto')
            return validate_coherence(rec)
        return {'error': 'Usage: validate <data|path.cst> [type]'}

    def _cmd_coherence(self, *args) -> Dict[str, Any]:
        """Full coherence demonstration with diverse data types."""
        test_data = [
            ('hello world', 'text'),
            (42, 'number'),
            (3.14159, 'number'),
            (b'\xDE\xAD\xBE\xEF', 'bytes'),
            ('2A9E0100', 'hex'),
            ('/src/auth/login.py', 'path'),
            ({'key': 'value', 'n': 42}, 'json'),
            ([128, 64, 32], 'image'),
            ([24, 24, 24], 'coords'),
        ]
        results = []
        all_pass = True
        for data, dtype in test_data:
            rec = flip(data, dtype)
            v = validate_coherence(rec)
            results.append({
                'input': str(data)[:40],
                'type': dtype,
                'record': rec.to_compact(),
                'coherent': v['coherent'],
            })
            if not v['coherent']:
                all_pass = False

        return {
            'tests': len(results),
            'all_coherent': all_pass,
            'results': results,
            'identity': {
                'P_minus_P_eq_0': True,
                'P_div_P_eq_1': True,
            },
        }

    def _cmd_export(self, *args) -> Dict[str, Any]:
        """Export records to .cst binary."""
        if len(args) < 2:
            return {'error': 'Usage: export <path.cst> <data1> [data2] ...'}
        path = Path(args[0])
        records = flip_batch(list(args[1:]), 'auto')
        return write_cst(records, path)

    def _cmd_import(self, *args) -> Dict[str, Any]:
        """Import records from .cst binary."""
        if not args:
            return {'error': 'Usage: import <path.cst>'}
        info, records = read_cst(Path(args[0]))
        return {
            'header': info,
            'count': len(records),
            'records': [r.to_compact() for r in records[:20]],
        }

    def _cmd_analyze(self, *args) -> Dict[str, Any]:
        """Analyze distribution of records."""
        if not args:
            return {'error': 'Usage: analyze <path.cst>'}
        _, records = read_cst(Path(args[0]))
        return analyze_distribution(records)

    def _cmd_status(self, *args) -> Dict[str, Any]:
        """Shell status."""
        return {
            'name': self.name,
            'hex_id': self.HEX_ID,
            'description': self.description,
            'commands': list(self.commands.keys()),
            'flip_types': FLIP_TYPES,
            'cst_format': {
                'magic': f"{CST_MAGIC:#010x} (CST\\0)",
                'version': CST_VERSION,
                'header_size': HEADER_SIZE,
                'token_size': TOKEN_SIZE,
            },
            'constants': {
                'MATRIX_SIZE': MATRIX_SIZE,
                'PRIME_131': PRIME_131,
                'PHI': round(PHI, 6),
                'K5': round(K5, 6),
                'K11': K11,
            },
            'coherence_checks': [
                'Morton roundtrip',
                'Basin from Morton',
                'Layer from basin',
                'Coords match decode',
                'I-state consistency',
                'Determinism (repeated flip)',
                'Identity P-P=0',
                'Identity P/P=1',
                'Binary roundtrip (pack/unpack)',
            ],
        }

    def _cmd_types(self, *args) -> Dict[str, Any]:
        """List supported flip types."""
        return {
            'types': {
                'text': 'String → UTF-8 bytes → FNV-1a → coords',
                'number': 'int/float → IEEE 754 bytes → FNV-1a → coords',
                'bytes': 'Raw bytes → FNV-1a → coords',
                'json': 'Dict/list → canonical JSON → FNV-1a → coords',
                'csv': 'CSV row → text → FNV-1a → coords',
                'image': 'RGB pixel [R,G,B] → direct coords (no hash)',
                'path': 'File path → normalized → FNV-1a → coords',
                'hex': 'Hex string → decoded bytes → FNV-1a → coords',
                'coords': 'Direct (x,y,z) → Morton (pass-through)',
            },
            'auto_detect': 'Python type → flip type (str→text, int→number, dict→json, etc.)',
        }

    def _cmd_demo(self, *args) -> Dict[str, Any]:
        """Demo flip with diverse data types."""
        demos = [
            ("Hello CST", 'text'),
            (42, 'number'),
            (b'\xCA\xFE', 'bytes'),
            ('/src/main.py', 'path'),
            ('FF00FF00', 'hex'),
            ({'task': 'flip', 'priority': 1}, 'json'),
            ([255, 128, 64], 'image'),
            ([24, 12, 36], 'coords'),
        ]
        results = []
        t0 = time.perf_counter()
        for data, dtype in demos:
            rec = flip(data, dtype)
            results.append({
                'input': str(data)[:32],
                'type': dtype,
                'compact': rec.to_compact(),
                'coherent': rec.verify_coherence()['coherent'],
            })
        elapsed = (time.perf_counter() - t0) * 1000

        return {
            'demo_count': len(results),
            'elapsed_ms': round(elapsed, 2),
            'results': results,
            'all_coherent': all(r['coherent'] for r in results),
        }


# =============================================================================
# CONVENIENCE FUNCTIONS (module-level)
# =============================================================================

def flip_text(text: str) -> CSTRecord:
    """Flip text into CST space."""
    return flip(text, 'text')


def flip_path(path: str) -> CSTRecord:
    """Flip a file path into CST space."""
    return flip(path, 'path')


def flip_json(obj: Any) -> CSTRecord:
    """Flip a JSON-serializable object into CST space."""
    return flip(obj, 'json')


def flip_pixel(r: int, g: int, b: int) -> CSTRecord:
    """Flip an RGB pixel into CST space."""
    return flip([r, g, b], 'image')


def flip_coords(x: int, y: int, z: int) -> CSTRecord:
    """Flip direct coordinates into CST space."""
    return flip([x, y, z], 'coords')
