#!/usr/bin/env python3
"""
Cypher Shell - Encode/Decode/Validate Cypher Operations (Standalone)
=====================================================================
HEX_ID: CY000200
AREA: Cypher System (CY)
COMPONENT: Shell Interface (00)
LEVEL: Secondary (02)
INSTANCE: Primary shell (00)

Standalone cypher shell for the cubesquare package.
Provides encode/decode/validate/usage commands for the cypher system.
Fully self-contained.

CYPHER FORMAT:
    [CONTEXT]PRIME_SEED: MESSAGE |CLR*PRIME_SEED|Q-PHASE[FREQUENCY_HZ]

COMMANDS:
    encode   - Encode a message into cypher format
    decode   - Decode a cypher string
    validate - Validate a cypher string
    usage    - Show usage statistics
    export   - Export billing report
    recent   - Show recent entries
    constants - Show system constants

Examples:
    python cypher.py encode -c C -m "Fixed bug" -p 5
    python cypher.py decode "[C]8:PRIME_37784: Fixed bug |CLR*37784|Q4[741.0Hz]"
    python cypher.py validate "[C]8:PRIME_37784: Test |CLR*37784|Q4[741.0Hz]"
    python cypher.py constants
"""

import sys
import json
import re
import argparse
from pathlib import Path
from datetime import datetime
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Any, Tuple
from enum import Enum

from .._core import morton_encode, morton_encode_batch_raw, create_coords_array, PRIME_131 as _PRIME_131

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

PHI = 1.618033988749895
PHI_CUBED = PHI ** 3  # ~4.236
K5 = 1.1588137289060527
K11 = 9.204026
PRIME_131 = 131  # Basin count

# Prime seed: ~37784 (approximate PHI^3 * magic_delta)
MAGIC_DELTA = 8912
PRIME_SEED = 37784

# Solfeggio frequencies (Hz)
SOLFEGGIO_FREQUENCIES = {
    'UT': 396.0,   # Liberation
    'RE': 417.0,   # Change
    'MI': 528.0,   # Transformation
    'FA': 639.0,   # Connection
    'SOL': 741.0,  # Expression
    'LA': 852.0,   # Intuition
}

# File type to frequency mapping
FILE_TYPE_FREQUENCIES = {
    '.py': 741.0,    # Python - Expression
    '.js': 639.0,    # JavaScript - Connection
    '.ts': 639.0,    # TypeScript - Connection
    '.c': 396.0,     # C - Liberation (low level)
    '.h': 396.0,     # Header - Liberation
    '.cpp': 417.0,   # C++ - Change
    '.rs': 528.0,    # Rust - Transformation
    '.go': 528.0,    # Go - Transformation
    '.md': 852.0,    # Markdown - Intuition
    '.json': 741.0,  # JSON - Expression
    '.yaml': 741.0,  # YAML - Expression
    '.sql': 639.0,   # SQL - Connection
}

# =============================================================================
# ENUMS
# =============================================================================

class QPhase(Enum):
    """Q-Phase levels (Fibonacci credits)."""
    Q1 = "Q1"  # 1 credit
    Q2 = "Q2"  # 2 credits
    Q3 = "Q3"  # 3 credits
    Q4 = "Q4"  # 5 credits
    Q5 = "Q5"  # 8 credits
    Q6 = "Q6"  # 13 credits
    Q7 = "Q7"  # 21 credits
    Q8 = "Q8"  # 34 credits
    QT = "QT"  # Technical (55 credits)


class ContextCode(Enum):
    """Context codes for cypher messages."""
    C = "C"     # Code change
    B = "B"     # Bug fix
    S = "S"     # Security
    QT = "QT"   # Quality/Testing
    T = "T"     # Technical
    I = "I"     # Infrastructure
    D = "D"     # Documentation
    F = "F"     # Feature
    R = "R"     # Refactor
    CB = "CB"   # Code + Bug


class ValidationLevel(Enum):
    """Validation strictness levels."""
    BASIC = "basic"
    STANDARD = "standard"
    STRICT = "strict"
    PARANOID = "paranoid"


class IssueSeverity(Enum):
    """Validation issue severity."""
    INFO = "info"
    WARNING = "warning"
    ERROR = "error"
    CRITICAL = "critical"


# =============================================================================
# CONFIGURATION
# =============================================================================

# Phase credits (Fibonacci sequence)
PHASE_CREDITS = {
    QPhase.Q1: 1,
    QPhase.Q2: 2,
    QPhase.Q3: 3,
    QPhase.Q4: 5,
    QPhase.Q5: 8,
    QPhase.Q6: 13,
    QPhase.Q7: 21,
    QPhase.Q8: 34,
    QPhase.QT: 55,
}

# Phase names
PHASE_NAMES = {
    QPhase.Q1: "Micro",
    QPhase.Q2: "Mini",
    QPhase.Q3: "Small",
    QPhase.Q4: "Medium",
    QPhase.Q5: "Large",
    QPhase.Q6: "Extra Large",
    QPhase.Q7: "Huge",
    QPhase.Q8: "Epic",
    QPhase.QT: "Technical",
}

# Context hex areas
CONTEXT_HEX_AREAS = {
    ContextCode.C: "C0",
    ContextCode.B: "B0",
    ContextCode.S: "50",
    ContextCode.QT: "01",
    ContextCode.T: "70",
    ContextCode.I: "10",
    ContextCode.D: "D0",
    ContextCode.F: "F0",
    ContextCode.R: "E0",
    ContextCode.CB: "CB",
}

# Context names
CONTEXT_NAMES = {
    ContextCode.C: "Code",
    ContextCode.B: "Bug Fix",
    ContextCode.S: "Security",
    ContextCode.QT: "Quality/Testing",
    ContextCode.T: "Technical",
    ContextCode.I: "Infrastructure",
    ContextCode.D: "Documentation",
    ContextCode.F: "Feature",
    ContextCode.R: "Refactor",
    ContextCode.CB: "Code + Bug",
}

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

@dataclass
class PhaseConfig:
    """Configuration for a Q-Phase."""
    name: str
    credits: int


@dataclass
class ContextConfig:
    """Configuration for a context code."""
    name: str
    hex_area: str


# Build config dictionaries
PHASE_CONFIGS = {
    phase: PhaseConfig(name=PHASE_NAMES[phase], credits=PHASE_CREDITS[phase])
    for phase in QPhase
}

CONTEXT_CONFIGS = {
    code: ContextConfig(name=CONTEXT_NAMES[code], hex_area=CONTEXT_HEX_AREAS[code])
    for code in ContextCode
}


@dataclass
class CypherMessage:
    """Decoded cypher message."""
    context: str
    context_name: str
    message: str
    phase: QPhase
    frequency_hz: float
    credits: int
    hex_id: Optional[str] = None
    prime_seed: int = PRIME_SEED
    checksum: str = ""


@dataclass
class DecodeResult:
    """Result of decoding a cypher."""
    success: bool
    message: Optional[CypherMessage] = None
    error: Optional[str] = None


@dataclass
class ValidationIssue:
    """A single validation issue."""
    code: str
    severity: IssueSeverity
    message: str
    field: str = ""
    expected: Any = None
    actual: Any = None


@dataclass
class ValidationReport:
    """Complete validation report."""
    valid: bool
    level: ValidationLevel
    error_count: int
    warning_count: int
    issues: List[ValidationIssue] = field(default_factory=list)
    metrics: Dict[str, Any] = field(default_factory=dict)


@dataclass
class UsageEntry:
    """Single usage entry."""
    id: int
    timestamp: str
    agent_id: str
    context: str
    phase: str
    credits_used: int
    cypher_encoded: str
    prev_hash: str = ""
    entry_hash: str = ""

    def to_dict(self) -> Dict:
        return {
            'id': self.id,
            'timestamp': self.timestamp,
            'agent_id': self.agent_id,
            'context': self.context,
            'phase': self.phase,
            'credits_used': self.credits_used,
            'cypher_encoded': self.cypher_encoded,
        }


@dataclass
class UsageSummary:
    """Usage summary."""
    total_entries: int
    total_credits: int
    credits_by_phase: Dict[str, int]
    credits_by_agent: Dict[str, int]
    credits_by_context: Dict[str, int]
    start_time: Optional[str]
    end_time: Optional[str]
    chain_valid: bool


@dataclass
class BillingReport:
    """Billing export report."""
    generated_at: str
    period_start: Optional[str]
    period_end: Optional[str]
    agent_id: Optional[str]
    total_credits: int
    phase_breakdown: Dict[str, int]
    chain_integrity: bool
    verification_hash: str
    entries: List[Dict] = field(default_factory=list)


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

def get_phase_from_story_points(story_points: int) -> QPhase:
    """Map story points to Q-Phase."""
    if story_points <= 1:
        return QPhase.Q1
    elif story_points <= 2:
        return QPhase.Q2
    elif story_points <= 3:
        return QPhase.Q3
    elif story_points <= 5:
        return QPhase.Q4
    elif story_points <= 8:
        return QPhase.Q5
    elif story_points <= 13:
        return QPhase.Q6
    elif story_points <= 21:
        return QPhase.Q7
    elif story_points <= 34:
        return QPhase.Q8
    else:
        return QPhase.QT


def get_frequency_for_file_type(file_ext: str) -> float:
    """Get frequency for a file type."""
    if not file_ext.startswith('.'):
        file_ext = '.' + file_ext
    return FILE_TYPE_FREQUENCIES.get(file_ext.lower(), 741.0)


def morton_hash(data: str, length: int = 64) -> str:
    """Deterministic Morton-based hash — no external hash library, no Python loops.

    Converts string data to coordinates via int.from_bytes (C-internal),
    then batch Morton-encodes all rounds in ONE C call.

    Args:
        data: String to hash
        length: Output hex length (default 64 chars = 256 bits equivalent)

    Returns:
        Hex string of `length` characters
    """
    raw = data.encode('utf-8')

    # ALL bytes → single integer in ONE C-internal operation (no Python loop)
    data_int = int.from_bytes(raw, 'big') if raw else 0

    # Derive 3 coordinate seeds via bit slicing + LCG constants from compute_basin
    # Cross-mix with XOR shifts for avalanche — pure arithmetic, no loops
    x = ((data_int & 0xFFFFFFFF) * 1664525 + 1013904223) & 0xFFFFFFFF
    y = (((data_int >> 11) & 0xFFFFFFFF) * 22695477 + 48271) & 0xFFFFFFFF
    z = (((data_int >> 23) & 0xFFFFFFFF) * 48271 + 1664525) & 0xFFFFFFFF
    x = (x ^ (y >> 16) ^ ((z << 5) & 0xFFFFFFFF)) & 0xFFFFFFFF
    y = (y ^ (z >> 16) ^ ((x << 5) & 0xFFFFFFFF)) & 0xFFFFFFFF
    z = (z ^ (x >> 16) ^ ((y << 5) & 0xFFFFFFFF)) & 0xFFFFFFFF

    # Build coordinate array for all rounds (fixed 1-8 rounds, not data-proportional)
    rounds = (length + 7) // 8
    flat = [0] * (rounds * 3)
    cx, cy, cz = x, y, z
    i = 0
    while i < rounds * 3:
        flat[i] = cx & 0x1FFFFF
        flat[i + 1] = cy & 0x1FFFFF
        flat[i + 2] = cz & 0x1FFFFF
        cx = (cx * 1664525 + 1013904223) & 0xFFFFFFFF
        cy = (cy * 22695477 + 48271) & 0xFFFFFFFF
        cz = (cz * 48271 + 1664525) & 0xFFFFFFFF
        i += 3

    # ONE C batch call — all Morton encoding runs in C at 0.009ns/op
    coords = create_coords_array(flat)
    results = morton_encode_batch_raw(coords, rounds)

    # Read results (fixed-size, not data-proportional)
    parts = [f"{results[r].morton_code & 0xFFFFFFFF:08X}" for r in range(rounds)]
    return "".join(parts)[:length]


def compute_checksum(message: str, prime_seed: int) -> str:
    """Compute checksum for a message using Morton hash."""
    data = f"{message}{prime_seed}"
    return morton_hash(data, length=8)


# =============================================================================
# ENCODER/DECODER
# =============================================================================

def encode_cypher(
    context: str,
    message: str,
    phase: QPhase = QPhase.Q4,
    frequency_hz: float = 741.0,
    hex_id: Optional[str] = None
) -> str:
    """
    Encode a message into cypher format.

    Format: [CONTEXT]8:PRIME_SEED: MESSAGE |CLR*SEED|Q-PHASE[FREQ_Hz]

    Args:
        context: Context code(s) (C, B, CB, etc.)
        message: Message to encode
        phase: Q-Phase level
        frequency_hz: Frequency in Hz
        hex_id: Optional hex ID

    Returns:
        Encoded cypher string
    """
    # Validate context
    context_upper = context.upper()

    # Build the cypher
    cypher_parts = [
        f"[{context_upper}]",
        "8:",  # Infinity symbol placeholder
        f"PRIME_{PRIME_SEED}:",
        f" {message}",
        f" |CLR*{PRIME_SEED}|",
        f"{phase.value}[{frequency_hz}Hz]",
    ]

    if hex_id:
        cypher_parts.insert(-1, f"@{hex_id}")

    return "".join(cypher_parts)


def decode_cypher(cypher: str) -> DecodeResult:
    """
    Decode a cypher string.

    Args:
        cypher: Cypher string to decode

    Returns:
        DecodeResult with success status and message
    """
    # Pattern to match cypher format
    pattern = r'\[([A-Z]+)\].*?PRIME_(\d+):\s*(.+?)\s*\|CLR\*\d+\|([QT][0-9T]?)\[([0-9.]+)Hz\]'

    match = re.search(pattern, cypher)
    if not match:
        return DecodeResult(success=False, error="Invalid cypher format")

    context = match.group(1)
    prime_seed = int(match.group(2))
    message = match.group(3).strip()
    phase_str = match.group(4)
    frequency = float(match.group(5))

    # Parse phase
    try:
        phase = QPhase(phase_str)
    except ValueError:
        phase = QPhase.Q4

    # Get context name
    try:
        context_code = ContextCode(context)
        context_name = CONTEXT_NAMES[context_code]
    except ValueError:
        context_name = context

    # Get credits
    credits = PHASE_CREDITS.get(phase, 5)

    # Extract hex ID if present
    hex_id = None
    hex_match = re.search(r'@([0-9A-F]{8})', cypher)
    if hex_match:
        hex_id = hex_match.group(1)

    # Compute checksum
    checksum = compute_checksum(message, prime_seed)

    return DecodeResult(
        success=True,
        message=CypherMessage(
            context=context,
            context_name=context_name,
            message=message,
            phase=phase,
            frequency_hz=frequency,
            credits=credits,
            hex_id=hex_id,
            prime_seed=prime_seed,
            checksum=checksum
        )
    )


# =============================================================================
# VALIDATOR
# =============================================================================

class CypherValidator:
    """Validate cypher strings at various strictness levels."""

    def __init__(self, level: ValidationLevel = ValidationLevel.STANDARD):
        self.level = level

    def validate(self, cypher: str) -> ValidationReport:
        """Validate a cypher string."""
        issues = []

        # Basic format check
        if not cypher.startswith('['):
            issues.append(ValidationIssue(
                code="FORMAT_001",
                severity=IssueSeverity.ERROR,
                message="Cypher must start with context in brackets",
                field="format"
            ))

        # Check for required parts
        if 'PRIME_' not in cypher:
            issues.append(ValidationIssue(
                code="FORMAT_002",
                severity=IssueSeverity.ERROR,
                message="Missing PRIME_SEED",
                field="prime_seed"
            ))

        if '|CLR*' not in cypher:
            issues.append(ValidationIssue(
                code="FORMAT_003",
                severity=IssueSeverity.ERROR,
                message="Missing CLR marker",
                field="clr_marker"
            ))

        if 'Hz]' not in cypher:
            issues.append(ValidationIssue(
                code="FORMAT_004",
                severity=IssueSeverity.ERROR,
                message="Missing frequency",
                field="frequency"
            ))

        # Try to decode
        result = decode_cypher(cypher)
        if not result.success:
            issues.append(ValidationIssue(
                code="DECODE_001",
                severity=IssueSeverity.ERROR,
                message=result.error or "Failed to decode",
                field="decode"
            ))

        # Strict checks
        if self.level in [ValidationLevel.STRICT, ValidationLevel.PARANOID]:
            if result.success and result.message:
                # Check prime seed
                if result.message.prime_seed != PRIME_SEED:
                    issues.append(ValidationIssue(
                        code="QUANTUM_001",
                        severity=IssueSeverity.WARNING,
                        message="Non-standard PRIME_SEED",
                        field="prime_seed",
                        expected=PRIME_SEED,
                        actual=result.message.prime_seed
                    ))

                # Check frequency is Solfeggio
                if result.message.frequency_hz not in SOLFEGGIO_FREQUENCIES.values():
                    issues.append(ValidationIssue(
                        code="QUANTUM_002",
                        severity=IssueSeverity.WARNING,
                        message="Non-Solfeggio frequency",
                        field="frequency"
                    ))

        # Paranoid checks
        if self.level == ValidationLevel.PARANOID:
            if result.success and result.message:
                # Check message length
                if len(result.message.message) < 3:
                    issues.append(ValidationIssue(
                        code="PARANOID_001",
                        severity=IssueSeverity.WARNING,
                        message="Message too short",
                        field="message"
                    ))

        # Count errors and warnings
        error_count = sum(1 for i in issues if i.severity in [IssueSeverity.ERROR, IssueSeverity.CRITICAL])
        warning_count = sum(1 for i in issues if i.severity == IssueSeverity.WARNING)

        return ValidationReport(
            valid=(error_count == 0),
            level=self.level,
            error_count=error_count,
            warning_count=warning_count,
            issues=issues,
            metrics={
                'length': len(cypher),
                'has_hex_id': '@' in cypher,
            }
        )


# =============================================================================
# USAGE TRACKER (In-Memory)
# =============================================================================

class CypherUsageTracker:
    """Track cypher usage in memory."""

    def __init__(self):
        self.entries: List[UsageEntry] = []
        self.next_id = 1

    def log_usage(self, cypher: str, agent_id: str = "cli") -> UsageEntry:
        """Log cypher usage."""
        result = decode_cypher(cypher)

        if result.success and result.message:
            context = result.message.context
            phase = result.message.phase.value
            credits = result.message.credits
        else:
            context = "?"
            phase = "Q4"
            credits = 5

        # Compute hashes — Morton-based chain (no hashlib)
        prev_hash = self.entries[-1].entry_hash if self.entries else "0" * 64
        entry_data = f"{self.next_id}{datetime.now().isoformat()}{cypher}{prev_hash}"
        entry_hash = morton_hash(entry_data)

        entry = UsageEntry(
            id=self.next_id,
            timestamp=datetime.now().isoformat(),
            agent_id=agent_id,
            context=context,
            phase=phase,
            credits_used=credits,
            cypher_encoded=cypher,
            prev_hash=prev_hash,
            entry_hash=entry_hash
        )

        self.entries.append(entry)
        self.next_id += 1

        return entry

    def get_usage_summary(
        self,
        agent_id: Optional[str] = None,
        start_time: Optional[str] = None,
        end_time: Optional[str] = None
    ) -> UsageSummary:
        """Get usage summary."""
        filtered = self.entries

        if agent_id:
            filtered = [e for e in filtered if e.agent_id == agent_id]
        if start_time:
            filtered = [e for e in filtered if e.timestamp >= start_time]
        if end_time:
            filtered = [e for e in filtered if e.timestamp <= end_time]

        credits_by_phase = {}
        credits_by_agent = {}
        credits_by_context = {}

        for entry in filtered:
            credits_by_phase[entry.phase] = credits_by_phase.get(entry.phase, 0) + entry.credits_used
            credits_by_agent[entry.agent_id] = credits_by_agent.get(entry.agent_id, 0) + entry.credits_used
            credits_by_context[entry.context] = credits_by_context.get(entry.context, 0) + entry.credits_used

        return UsageSummary(
            total_entries=len(filtered),
            total_credits=sum(e.credits_used for e in filtered),
            credits_by_phase=credits_by_phase,
            credits_by_agent=credits_by_agent,
            credits_by_context=credits_by_context,
            start_time=filtered[0].timestamp if filtered else None,
            end_time=filtered[-1].timestamp if filtered else None,
            chain_valid=self.verify_chain()
        )

    def get_recent_entries(self, limit: int = 10) -> List[UsageEntry]:
        """Get recent entries."""
        return self.entries[-limit:]

    def verify_chain(self) -> bool:
        """Verify hash chain integrity."""
        if not self.entries:
            return True

        prev_hash = "0" * 64
        for entry in self.entries:
            if entry.prev_hash != prev_hash:
                return False
            prev_hash = entry.entry_hash

        return True

    def export_for_billing(
        self,
        agent_id: Optional[str] = None,
        start_time: Optional[str] = None,
        end_time: Optional[str] = None
    ) -> BillingReport:
        """Export billing report."""
        summary = self.get_usage_summary(agent_id, start_time, end_time)

        filtered = self.entries
        if agent_id:
            filtered = [e for e in filtered if e.agent_id == agent_id]

        # Compute verification hash — Morton-based (no hashlib)
        all_data = "".join(e.entry_hash for e in filtered)
        verification_hash = morton_hash(all_data)

        return BillingReport(
            generated_at=datetime.now().isoformat(),
            period_start=start_time or (filtered[0].timestamp if filtered else None),
            period_end=end_time or (filtered[-1].timestamp if filtered else None),
            agent_id=agent_id,
            total_credits=summary.total_credits,
            phase_breakdown=summary.credits_by_phase,
            chain_integrity=summary.chain_valid,
            verification_hash=verification_hash,
            entries=[e.to_dict() for e in filtered]
        )


# Global tracker instance
_tracker: Optional[CypherUsageTracker] = None


def get_tracker() -> CypherUsageTracker:
    """Get or create the global tracker."""
    global _tracker
    if _tracker is None:
        _tracker = CypherUsageTracker()
    return _tracker


def log_cypher_usage(cypher: str, agent_id: str = "cli") -> UsageEntry:
    """Log cypher usage to global tracker."""
    return get_tracker().log_usage(cypher, agent_id)


# =============================================================================
# SHELL COMMANDS
# =============================================================================

def cmd_encode(args):
    """Encode a message into cypher format."""
    # Determine phase
    if args.story_points:
        phase = get_phase_from_story_points(args.story_points)
    elif args.phase:
        phase = QPhase.QT if args.phase == "T" else QPhase(f"Q{args.phase}")
    else:
        phase = QPhase.Q4

    # Determine frequency
    if args.frequency:
        freq = args.frequency
    elif args.file_type:
        freq = get_frequency_for_file_type(args.file_type)
    else:
        freq = 741.0

    try:
        cypher = encode_cypher(
            context=args.context,
            message=args.message,
            phase=phase,
            frequency_hz=freq,
            hex_id=args.hex_id
        )

        print(cypher)

        if args.log:
            tracker = get_tracker()
            entry = tracker.log_usage(cypher, agent_id=args.agent or "cli")
            print(f"\nLogged: Entry #{entry.id}, {entry.credits_used} credits")

        return 0
    except Exception as e:
        print(f"Error: {e}", file=sys.stderr)
        return 1


def cmd_decode(args):
    """Decode a cypher string."""
    result = decode_cypher(args.cypher)

    if not result.success:
        print(f"Decode failed: {result.error}", file=sys.stderr)
        return 1

    msg = result.message

    if args.json:
        output = {
            "context": msg.context,
            "context_name": msg.context_name,
            "message": msg.message,
            "phase": msg.phase.value,
            "phase_name": PHASE_CONFIGS[msg.phase].name,
            "frequency_hz": msg.frequency_hz,
            "credits": msg.credits,
            "hex_id": msg.hex_id,
            "prime_seed": msg.prime_seed,
            "checksum": msg.checksum
        }
        print(json.dumps(output, indent=2))
    else:
        print(f"  CYPHER DECODED")
        print(f"  Context:    {msg.context} ({msg.context_name})")
        print(f"  Message:    {msg.message}")
        print(f"  Phase:      {msg.phase.value} ({PHASE_CONFIGS[msg.phase].name})")
        print(f"  Frequency:  {msg.frequency_hz} Hz")
        print(f"  Credits:    {msg.credits}")
        if msg.hex_id:
            print(f"  Hex ID:     {msg.hex_id}")
        print(f"  Prime Seed: {msg.prime_seed}")

    return 0


def cmd_validate(args):
    """Validate a cypher string."""
    # Determine validation level
    if args.paranoid:
        level = ValidationLevel.PARANOID
    elif args.strict:
        level = ValidationLevel.STRICT
    elif args.basic:
        level = ValidationLevel.BASIC
    else:
        level = ValidationLevel.STANDARD

    validator = CypherValidator(level)
    report = validator.validate(args.cypher)

    if args.json:
        output = {
            "valid": report.valid,
            "level": report.level.value,
            "error_count": report.error_count,
            "warning_count": report.warning_count,
            "issues": [
                {
                    "code": i.code,
                    "severity": i.severity.value,
                    "message": i.message,
                    "field": i.field
                }
                for i in report.issues
            ],
            "metrics": report.metrics
        }
        print(json.dumps(output, indent=2))
    else:
        status = "VALID" if report.valid else "INVALID"
        print(f"\n{status} (Level: {report.level.value})")
        print(f"Errors: {report.error_count}, Warnings: {report.warning_count}")

        if report.issues:
            print("\nIssues:")
            for issue in report.issues:
                icon = "[ERR]" if issue.severity.value in ("error", "critical") else "[WARN]"
                print(f"  {icon} [{issue.code}] {issue.message}")
                if issue.expected:
                    print(f"      Expected: {issue.expected}")
                    print(f"      Actual:   {issue.actual}")

    return 0 if report.valid else 1


def cmd_usage(args):
    """Show usage statistics."""
    tracker = get_tracker()

    summary = tracker.get_usage_summary(
        agent_id=args.agent,
        start_time=args.since,
        end_time=args.until
    )

    if args.json:
        output = {
            "total_entries": summary.total_entries,
            "total_credits": summary.total_credits,
            "credits_by_phase": summary.credits_by_phase,
            "credits_by_agent": summary.credits_by_agent,
            "credits_by_context": summary.credits_by_context,
            "start_time": summary.start_time,
            "end_time": summary.end_time,
            "chain_valid": summary.chain_valid
        }
        print(json.dumps(output, indent=2))
    else:
        print(f"\nCYPHER USAGE SUMMARY")
        print(f"{'=' * 50}")
        print(f"Total entries:  {summary.total_entries}")
        print(f"Total credits:  {summary.total_credits}")

        if summary.start_time:
            print(f"Period:         {summary.start_time[:10]} to {summary.end_time[:10]}")

        chain_status = "Valid" if summary.chain_valid else "Broken"
        print(f"Chain integrity: {chain_status}")

        if summary.credits_by_phase:
            print(f"\nCredits by Phase:")
            for phase, credits in sorted(summary.credits_by_phase.items()):
                print(f"  {phase}: {credits} credits")

        if summary.credits_by_agent:
            print(f"\nCredits by Agent:")
            for agent, credits in sorted(summary.credits_by_agent.items()):
                print(f"  {agent}: {credits} credits")

    return 0


def cmd_recent(args):
    """Show recent entries."""
    tracker = get_tracker()

    entries = tracker.get_recent_entries(limit=args.limit)

    if args.json:
        output = [e.to_dict() for e in entries]
        print(json.dumps(output, indent=2))
    else:
        print(f"\nRECENT CYPHER ENTRIES (last {args.limit})")
        print(f"{'=' * 70}")

        for entry in entries:
            print(f"\n#{entry.id} | {entry.timestamp[:19]}")
            print(f"  Agent:    {entry.agent_id}")
            print(f"  Context:  {entry.context} | Phase: {entry.phase}")
            print(f"  Credits:  {entry.credits_used}")
            preview = entry.cypher_encoded[:60] + "..." if len(entry.cypher_encoded) > 60 else entry.cypher_encoded
            print(f"  Cypher:   {preview}")

    return 0


def cmd_constants(args):
    """Show system constants."""
    print(f"\nCYPHER SYSTEM CONSTANTS")
    print(f"{'=' * 50}")

    print(f"\nMathematical Constants:")
    print(f"  PHI:        {PHI:.16f}")
    print(f"  PHI^3:      {PHI_CUBED:.16f}")
    print(f"  K5:         {K5:.16f}")
    print(f"  K11:        {K11:.16f}")
    print(f"  MAGIC_DELTA:{MAGIC_DELTA}")
    print(f"  PRIME_SEED: {PRIME_SEED}")

    print(f"\nQ-Phase Credits (Fibonacci):")
    for phase in QPhase:
        config = PHASE_CONFIGS[phase]
        print(f"  {phase.value}: {config.credits:2d} credits ({config.name})")

    print(f"\nContext Codes -> Hex Areas:")
    for code in ContextCode:
        config = CONTEXT_CONFIGS[code]
        print(f"  [{code.value:2s}] = {config.hex_area}xx ({config.name})")

    print(f"\nFile Frequencies (Solfeggio):")
    freq_groups = {}
    for ext, freq in sorted(FILE_TYPE_FREQUENCIES.items(), key=lambda x: x[1]):
        if freq not in freq_groups:
            freq_groups[freq] = []
        freq_groups[freq].append(ext)

    for freq, exts in sorted(freq_groups.items()):
        print(f"  {freq:6.1f} Hz: {', '.join(exts)}")

    return 0


def cmd_export(args):
    """Export billing report."""
    tracker = get_tracker()

    report = tracker.export_for_billing(
        agent_id=args.agent,
        start_time=args.since,
        end_time=args.until
    )

    output = {
        "generated_at": report.generated_at,
        "period_start": report.period_start,
        "period_end": report.period_end,
        "agent_id": report.agent_id,
        "total_credits": report.total_credits,
        "phase_breakdown": report.phase_breakdown,
        "chain_integrity": report.chain_integrity,
        "verification_hash": report.verification_hash,
        "entry_count": len(report.entries)
    }

    if args.full:
        output["entries"] = report.entries

    if args.output:
        output_path = Path(args.output)
        with open(output_path, 'w') as f:
            json.dump(output, f, indent=2)
        print(f"Exported to {output_path}")
    else:
        print(json.dumps(output, indent=2))

    return 0


def cmd_verify(args):
    """Verify hash chain integrity."""
    tracker = get_tracker()

    is_valid = tracker.verify_chain()

    if is_valid:
        print("Hash chain is VALID - no tampering detected")
        return 0
    else:
        print("Hash chain is BROKEN - possible tampering!")
        return 1


def cmd_status(args):
    """Show shell status."""
    tracker = get_tracker()
    summary = tracker.get_usage_summary()

    status = {
        'hex_id': 'CY000200',
        'protocol': 'CG:HEX|V1|AGENT:CY000200',
        'phi': PHI,
        'prime_seed': PRIME_SEED,
        'phases': len(QPhase),
        'contexts': len(ContextCode),
        'entries_tracked': summary.total_entries,
        'total_credits': summary.total_credits,
        'chain_valid': summary.chain_valid,
    }

    if args.json:
        print(json.dumps(status, indent=2))
    else:
        print(f"\n{'='*60}")
        print("CYPHER SHELL STATUS")
        print(f"{'='*60}")
        print(f"HEX_ID: {status['hex_id']}")
        print(f"Protocol: {status['protocol']}")
        print(f"PHI: {status['phi']}")
        print(f"PRIME_SEED: {status['prime_seed']}")
        print(f"Q-Phases: {status['phases']}")
        print(f"Contexts: {status['contexts']}")
        print(f"Entries tracked: {status['entries_tracked']}")
        print(f"Total credits: {status['total_credits']}")
        print(f"Chain valid: {status['chain_valid']}")

    return 0


# =============================================================================
# MAIN
# =============================================================================

def create_parser():
    """Create argument parser."""
    parser = argparse.ArgumentParser(
        prog='cypher',
        description='Cypher System - Encode, decode, validate, and track cypher messages',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Encode a message
  python cypher.py encode -c C -m "Fixed bug" -p 5

  # Encode and log usage
  python cypher.py encode -c CB -m "Added bridge" --log --agent copilot-001

  # Decode a cypher
  python cypher.py decode "[C]8:PRIME_37784: Fixed bug |CLR*37784|Q4[741.0Hz]"

  # Validate a cypher (strict mode)
  python cypher.py validate "[C]8:PRIME_37784: Test |CLR*37784|Q4[741.0Hz]" --strict

  # Show usage summary
  python cypher.py usage --agent copilot-001

  # Show constants
  python cypher.py constants
"""
    )

    subparsers = parser.add_subparsers(dest='command', help='Commands')

    # Status command
    stat = subparsers.add_parser('status', help='Show shell status')
    stat.add_argument('--json', action='store_true', help='Output as JSON')
    stat.set_defaults(func=cmd_status)

    # Encode command
    enc = subparsers.add_parser('encode', help='Encode a message into cypher format')
    enc.add_argument('-c', '--context', required=True, help='Context code(s): C,B,S,QT,T,I,D,F,R')
    enc.add_argument('-m', '--message', required=True, help='Message to encode')
    enc.add_argument('-p', '--phase', help='Q-phase (1-8 or T)')
    enc.add_argument('-s', '--story-points', type=int, help='Auto-derive phase from story points')
    enc.add_argument('-f', '--frequency', type=float, help='Frequency in Hz')
    enc.add_argument('-t', '--file-type', help='File type to derive frequency (py, js, etc.)')
    enc.add_argument('-x', '--hex-id', help='Hex ID (8 chars)')
    enc.add_argument('--log', action='store_true', help='Log usage to tracker')
    enc.add_argument('--agent', help='Agent ID for logging')
    enc.set_defaults(func=cmd_encode)

    # Decode command
    dec = subparsers.add_parser('decode', help='Decode a cypher string')
    dec.add_argument('cypher', help='Cypher string to decode')
    dec.add_argument('--json', action='store_true', help='Output as JSON')
    dec.set_defaults(func=cmd_decode)

    # Validate command
    val = subparsers.add_parser('validate', help='Validate a cypher string')
    val.add_argument('cypher', help='Cypher string to validate')
    val.add_argument('--basic', action='store_true', help='Basic validation (format only)')
    val.add_argument('--strict', action='store_true', help='Strict validation (+ quantum)')
    val.add_argument('--paranoid', action='store_true', help='Paranoid validation (all checks)')
    val.add_argument('--json', action='store_true', help='Output as JSON')
    val.set_defaults(func=cmd_validate)

    # Usage command
    usg = subparsers.add_parser('usage', help='Show usage statistics')
    usg.add_argument('--agent', help='Filter by agent ID')
    usg.add_argument('--since', help='Start time (ISO format)')
    usg.add_argument('--until', help='End time (ISO format)')
    usg.add_argument('--json', action='store_true', help='Output as JSON')
    usg.set_defaults(func=cmd_usage)

    # Export command
    exp = subparsers.add_parser('export', help='Export billing report')
    exp.add_argument('--agent', help='Agent ID')
    exp.add_argument('--since', help='Start time (ISO format)')
    exp.add_argument('--until', help='End time (ISO format)')
    exp.add_argument('-o', '--output', help='Output file path')
    exp.add_argument('--full', action='store_true', help='Include all entries')
    exp.set_defaults(func=cmd_export)

    # Recent command
    rec = subparsers.add_parser('recent', help='Show recent entries')
    rec.add_argument('-n', '--limit', type=int, default=10, help='Number of entries')
    rec.add_argument('--json', action='store_true', help='Output as JSON')
    rec.set_defaults(func=cmd_recent)

    # Constants command
    con = subparsers.add_parser('constants', help='Show system constants')
    con.set_defaults(func=cmd_constants)

    # Verify command
    ver = subparsers.add_parser('verify', help='Verify hash chain integrity')
    ver.set_defaults(func=cmd_verify)

    return parser


def main():
    """Main entry point."""
    parser = create_parser()
    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        return 0

    return args.func(args)


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

class CypherShell:
    """Cypher Shell wrapper for CLI integration."""

    def __init__(self):
        self.validator = CypherValidator()
        self.tracker = CypherUsageTracker()

    def status(self, as_json: bool = False) -> Dict[str, Any]:
        """Get shell status."""
        result = {
            'system': 'Cypher Shell',
            'hex_id': 'CY000200',
            'version': '1.0.0',
            'q_phases': len(QPhase),
            'context_codes': len(ContextCode),
            'constants': {
                'PHI': PHI,
                'K5': K5,
                'K11': K11,
                'PRIME_131': PRIME_131,
            },
            'status': 'operational',
        }
        if as_json:
            return json.dumps(result, indent=2)
        return result

    def validate(self, cypher: str, level: str = 'standard', as_json: bool = False) -> Dict[str, Any]:
        """Validate a cypher string."""
        lvl = ValidationLevel[level.upper()] if level.upper() in ValidationLevel.__members__ else ValidationLevel.STANDARD
        report = self.validator.validate(cypher, lvl)
        result = {
            'cypher': cypher,
            'valid': report.valid,
            'issues': [{'severity': i.severity.name, 'message': i.message} for i in report.issues],
        }
        if as_json:
            return json.dumps(result, indent=2)
        return result

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Run a Cypher shell command."""
        commands = {
            'status': lambda: self.status(as_json),
            'validate': lambda: self.validate(args[0] if args else "", as_json=as_json),
        }
        if command in commands:
            return commands[command]()
        return {'error': f'Unknown command: {command}', 'available': list(commands.keys())}


if __name__ == "__main__":
    sys.exit(main())
