#!/usr/bin/env python3
"""
program_builder.py
------------------
Unified compiler front-end for BloomOS:
- Bio_Poetica → KRISPER gene → Codon IR → Helix8 program
- Dot syntax → direct codon ops
- sys.shell → direct mappings
- Natural language → intent → codon IR

This keeps compatibility with Helix64 VM (expects 8-letter program).
"""

from __future__ import annotations

import sys
from dataclasses import dataclass, field
from typing import List, Optional, Dict, Any

# Existing modules
from .programs import (
    BASE_PROGRAM,
    LETTER_TO_OP,
    OP_TO_LETTER,
    GATE_POSITION,
    HELIX8_POSITIONS,
)

# ============================================================================
# OPTIONAL IMPORTS (graceful degradation)
# ============================================================================

KRISPER_AVAILABLE = False
BIOPOETICA_AVAILABLE = False

try:
    sys.path.insert(0, "/home/voidstr3m33/echo_labs/krisper/github")
    from krisper_github.krisper_lowering import lower_to_krsp
    KRISPER_AVAILABLE = True
except Exception:
    lower_to_krsp = None

try:
    sys.path.insert(0, "/home/voidstr3m33/echo_labs/bio_poetica/sandbox")
    from bio_poetica_sandbox.bio_poetica_interpreter import BioPoeticaInterpreter
    BIOPOETICA_AVAILABLE = True
except Exception:
    BioPoeticaInterpreter = None


# ============================================================================
# CODON IR (Intermediate Representation)
# ============================================================================

@dataclass
class CodonIR:
    """Intermediate representation before lowering to Helix8."""
    ops: List[str] = field(default_factory=list)
    source: str = "unknown"
    metadata: Dict[str, Any] = field(default_factory=dict)


# ============================================================================
# BUILDERS (various input formats → CodonIR)
# ============================================================================

def build_from_dot(command: str) -> CodonIR:
    """
    Dot notation → CodonIR

    Example: list.all.files → [LIST, ALL, FILES, ...]
    Maps to closest Helix8 ops.
    """
    words = command.lower().split(".")

    # Map common dot words to Helix8 ops
    DOT_TO_OP = {
        "list": "CTX_GATHER",
        "all": "EMBED",
        "files": "PROJ_UP",
        "print": "EMIT_LOGITS",
        "working": "LAYER_NORM",
        "directory": "W1_MATMUL",
        "processes": "SOFTMAX",
        "show": "CTX_GATHER",
        "get": "CTX_GATHER",
        "set": "W1_MATMUL",
        "run": "BIAS_RELU",
        "execute": "BIAS_RELU",
    }

    ops = []
    for word in words:
        if word in DOT_TO_OP:
            ops.append(DOT_TO_OP[word])
        else:
            ops.append("BIAS_RELU")  # default

    return CodonIR(ops=ops, source="dot", metadata={"command": command})


def build_from_sys_shell(command: str) -> CodonIR:
    """
    sys.shell.* namespace → CodonIR

    Example: sys.shell.ls_a → sysop pipeline
    """
    # sys.shell commands are sysop tasks - use balanced pipeline
    return CodonIR(
        ops=list(HELIX8_POSITIONS),
        source="sys.shell",
        metadata={"command": command}
    )


def build_from_bash(command: str) -> CodonIR:
    """
    Raw bash command → CodonIR

    Example: ls -a → sysop pipeline
    """
    return CodonIR(
        ops=list(HELIX8_POSITIONS),
        source="bash",
        metadata={"command": command}
    )


def build_from_bio_poetica(text: str) -> CodonIR:
    """
    Poetic natural language → Bio_Poetica AST → KRISPER gene → CodonIR

    Falls back to intent-based building if Bio_Poetica unavailable.
    """
    if not BIOPOETICA_AVAILABLE or not KRISPER_AVAILABLE:
        # Fallback: use intent classification
        return build_from_intent(_classify_natural(text))

    try:
        # Parse with Bio_Poetica
        interpreter = BioPoeticaInterpreter(safe_mode=True)
        intents = interpreter.parse_intent(text)

        # Lower to KRISPER
        # Note: This is a simplified bridge - real impl would use full AST
        ops = []
        for intent_type, target in intents:
            if intent_type == "elevation_request":
                ops.append("PROJ_UP")
            elif intent_type == "boundary_crossing":
                ops.append("W1_MATMUL")
            elif intent_type == "protected_read":
                ops.append("CTX_GATHER")
            else:
                ops.append("BIAS_RELU")

        if not ops:
            ops = list(HELIX8_POSITIONS)

        return CodonIR(ops=ops, source="bio_poetica", metadata={"text": text})

    except Exception as e:
        # Fallback on error
        return build_from_intent(_classify_natural(text))


def build_from_intent(task_type: str) -> CodonIR:
    """
    Map intent (task_type) → canonical program structure.

    This is the core semantic mapping:
    - math: Replace position 4 with SOFTMAX (boosts math reasoning)
    - code: Replace position 4 with CTX_GATHER (re-gather context)
    - json: Replace position 4 with EMBED (structural output)
    - balanced: Keep standard pipeline
    """
    # Start with base pipeline
    ops = list(HELIX8_POSITIONS)

    # Apply task-specific mutations at GATE_POSITION
    if task_type == "math":
        ops[GATE_POSITION] = "SOFTMAX"  # E→G
    elif task_type == "code":
        ops[GATE_POSITION] = "CTX_GATHER"  # E→A
    elif task_type == "json":
        ops[GATE_POSITION] = "EMBED"  # E→B
    elif task_type == "search":
        ops[GATE_POSITION] = "CTX_GATHER"  # like code
    elif task_type == "reason":
        ops[GATE_POSITION] = "SOFTMAX"  # like math
    # else: keep BIAS_RELU (balanced)

    return CodonIR(ops=ops, source="intent", metadata={"task_type": task_type})


# ============================================================================
# NATURAL LANGUAGE CLASSIFIER (local copy to avoid circular import)
# ============================================================================

def _classify_natural(text: str) -> str:
    """Simple heuristic task classifier for natural language."""
    t = text.lower()

    # Strong math hints
    math_tokens = ["integral", "derivative", "limit", "matrix", "probability",
                   "theorem", "equation", "solve for", "∫", "+", "-", "×", "÷"]
    if any(tok in t for tok in math_tokens):
        return "math"

    # Strong code hints
    code_tokens = ["python", "function", "class", "javascript", "code",
                   "write a script", "refactor", "bug", "compile"]
    if any(tok in t for tok in code_tokens):
        return "code"

    # JSON / schema hints
    json_tokens = ["json", "schema", "fields", "keys", "api response", "object"]
    if any(tok in t for tok in json_tokens):
        return "json"

    # Search / retrieval hints
    search_tokens = ["search", "find", "lookup", "look up", "google", "query"]
    if any(tok in t for tok in search_tokens):
        return "search"

    # Sysop-ish hints
    sysop_tokens = ["list files", "current directory", "processes", "kill process",
                    "cpu", "memory usage", "disk space"]
    if any(tok in t for tok in sysop_tokens):
        return "sysop"

    # Default: treat as code-style reasoning
    return "code"


# ============================================================================
# LOWERING (CodonIR → Helix8 letter program)
# ============================================================================

def lower_to_helix8(ir: CodonIR) -> str:
    """
    Lower CodonIR into Helix8 letters:
    - Truncate/expand to 8 positions
    - Map each op → closest Helix8 operation letter
    """
    ops = ir.ops[:8]

    # Pad to 8 if needed
    while len(ops) < 8:
        ops.append(HELIX8_POSITIONS[len(ops)])

    letters = []
    for op in ops:
        op_upper = op.upper()
        if op_upper in OP_TO_LETTER:
            letters.append(OP_TO_LETTER[op_upper])
        else:
            # Try fuzzy match
            for known_op, letter in OP_TO_LETTER.items():
                if known_op in op_upper or op_upper in known_op:
                    letters.append(letter)
                    break
            else:
                letters.append("E")  # fallback → safe BIAS_RELU

    return "".join(letters)


# ============================================================================
# UNIFIED ENTRYPOINT
# ============================================================================

def build_program(text: str) -> str:
    """
    Unified entrypoint used by dispatcher/backplane.
    Detects the format → builds CodonIR → lowers to Helix8.

    Args:
        text: Input command/query in any format

    Returns:
        8-letter Helix8 program string (e.g., "ABCDGFGH")
    """
    text = text.strip()

    if not text:
        return BASE_PROGRAM

    # 1. sys.shell namespace
    if text.startswith("sys.shell"):
        ir = build_from_sys_shell(text)
        return lower_to_helix8(ir)

    # 2. Dot notation (but not sys.shell)
    if "." in text and " " not in text:
        ir = build_from_dot(text)
        return lower_to_helix8(ir)

    # 3. Raw bash commands (short commands with no spaces or common patterns)
    bash_patterns = ["ls", "pwd", "ps", "kill", "cd", "cat", "grep", "find"]
    first_word = text.split()[0] if text.split() else ""
    if first_word in bash_patterns:
        ir = build_from_bash(text)
        return lower_to_helix8(ir)

    # 4. Poetic / natural language → Bio_Poetica path
    poetic_hints = ["unveil", "reveal", "breathe", "bloom", "crystal", "seed"]
    if any(hint in text.lower() for hint in poetic_hints):
        ir = build_from_bio_poetica(text)
        return lower_to_helix8(ir)

    # 5. Default: classify intent → build from intent
    task_type = _classify_natural(text)
    ir = build_from_intent(task_type)
    return lower_to_helix8(ir)


# ============================================================================
# CLI DEMO
# ============================================================================

def _demo():
    """Demonstrate program building."""
    print("=" * 70)
    print("PROGRAM BUILDER DEMO")
    print("=" * 70)
    print()

    print(f"KRISPER available: {KRISPER_AVAILABLE}")
    print(f"Bio_Poetica available: {BIOPOETICA_AVAILABLE}")
    print()

    tests = [
        "list.all.files",
        "sys.shell.ls_a",
        "ls -a",
        "solve 2x + 5 = 13",
        "write a python function",
        "return JSON schema",
        "unveil all secrets",
        "search for files",
    ]

    print("INPUT → PROGRAM")
    print("-" * 70)
    for t in tests:
        program = build_program(t)
        task_type = _classify_natural(t)
        print(f"{t:40} → {program} ({task_type})")

    print()
    print("=" * 70)


if __name__ == "__main__":
    _demo()
