#!/usr/bin/env python3
"""
BloomOS ↔ Helix64 Integration Bridge
=====================================

This is the INTEGRATION SEAM between:
  - Full BloomOS kernel (echo_labs/bloom/os/bloom_os)
  - Helix64 position-4 gate system (helix-cdc)

Current mode: Direct HTTP to Helix64
Future mode: Call BloomOS kernel syscalls

Architecture:
    BloomOS Runtime → Bridge → Helix64 Backend
                            ↓
                       (later: BloomOS kernel)
"""

import os
import json
import requests
from typing import Dict, Any, List, Tuple, Optional

from .gates import GATE_POLICIES, resolve_anchors, choose_gate, apply_gate_to_program

# Configuration
HELIX64_BACKEND = os.getenv("HELIX64_BACKEND", "http://localhost:5065/execute")
BLOOMOS_MODE = os.getenv("BLOOMOS_MODE", "direct")  # "direct" or "kernel"
BLOOMOS_SOCKET = os.getenv("BLOOMOS_SOCKET", "/tmp/bloomd.sock")
BLOOMOS_KERNEL_TIMEOUT_MS = int(os.getenv("BLOOMOS_KERNEL_TIMEOUT_MS", "200"))

# BloomOS SDK import guard
try:
    from bloom_os.sdk.client import BloomClient
    BLOOMOS_CLIENT_AVAILABLE = True
except Exception:
    BloomClient = None  # type: ignore
    BLOOMOS_CLIENT_AVAILABLE = False


def run_helix64_task(
    task_type: str,
    description: str,
    anchors: Optional[List[str]] = None,
    gate_policy: Optional[str] = None,
    base_program: str = "ABCDEFGH",
    gate_position: int = 4,
) -> Dict[str, Any]:
    """
    Execute a task through Helix64 with BloomOS integration.

    This is the CORE INTEGRATION FUNCTION.

    Current implementation (BLOOMOS_MODE=direct):
        - Uses local Helix64 gate selection
        - Calls Helix64 HTTP backend directly
        - Returns raw results + gate metadata

    Future implementation (BLOOMOS_MODE=kernel):
        - Calls bloom_os.sdk.client().model_run_helix64()
        - Includes capsule packing, pheromone emission, IT-token spending
        - Returns full BloomOS receipt with kernel metadata

    Args:
        task_type: Task category (math, code, json, search, reason, text, query)
        description: Natural language task description
        anchors: List of anchor names to use (e.g., ["math", "code"])
        gate_policy: Override gate policy (math_bias, code_bias, json_bias, balanced)
        base_program: Base Helix64 program (default: "ABCDEFGH")
        gate_position: Position to mutate (default: 4, the anchor sensitivity gate)

    Returns:
        dict with:
            - task_type: Task category
            - description: Task description
            - anchors_resolved: List of anchors used
            - gate: Gate metadata (policy, codon, position, before/after programs)
            - backend_results: Dict of {anchor: result} from Helix64
            - logits_mean: Average logits across anchors
            - logits_std: Average std across anchors
            - bloomos_mode: Integration mode used
    """
    # Step 1: Resolve anchors
    anchors_resolved = resolve_anchors(task_type, anchors)

    # Step 2: Choose gate policy
    gate_policy_name, gate_config = choose_gate(task_type, gate_policy)

    # Step 3: Apply gate to program (mutate position 4)
    program_after = apply_gate_to_program(base_program, gate_config, gate_position)

    print(f"[Bridge] Task: {task_type}, Gate: {gate_policy_name}, "
          f"Anchors: {anchors_resolved}, Program: {base_program} → {program_after}")

    # Step 4: Execute on backend (mode-dependent)
    if BLOOMOS_MODE == "kernel":
        # Future: Call BloomOS kernel
        backend_results = _execute_via_bloomos_kernel(
            program_after, anchors_resolved, task_type, description
        )
    else:
        # Current: Direct HTTP to Helix64
        backend_results = _execute_via_helix64_http(
            program_after, anchors_resolved
        )

    # Step 5: Aggregate results
    all_logits_mean = []
    all_logits_std = []

    for anchor, result in backend_results.items():
        if result["status"] == "ok":
            all_logits_mean.append(result.get("logits_mean", 0.0))
            all_logits_std.append(result.get("logits_std", 0.0))

    # Compute averages
    avg_logits_mean = sum(all_logits_mean) / len(all_logits_mean) if all_logits_mean else 0.0
    avg_logits_std = sum(all_logits_std) / len(all_logits_std) if all_logits_std else 0.0

    # Return unified result
    return {
        "task_type": task_type,
        "description": description,
        "anchors_resolved": anchors_resolved,
        "gate": {
            "policy": gate_policy_name,
            "codon": gate_config["codon"],
            "position": gate_position,
            "program_before": base_program,
            "program_after": program_after,
            "expected_effects": gate_config["expected_effects"],
            "description": gate_config["description"]
        },
        "backend_results": backend_results,
        "logits_mean": avg_logits_mean,
        "logits_std": avg_logits_std,
        "bloomos_mode": BLOOMOS_MODE
    }


def _execute_via_helix64_http(program: str, anchors: List[str]) -> Dict[str, Dict]:
    """
    Execute via direct HTTP to Helix64 backend.

    Current implementation - will be replaced when BLOOMOS_MODE=kernel.
    """
    results = {}

    for anchor in anchors:
        try:
            resp = requests.post(
                HELIX64_BACKEND,
                json={"program": program, "anchor": anchor},
                timeout=10
            )
            resp.raise_for_status()
            data = resp.json()

            # Check for VM errors
            if "error" in data:
                results[anchor] = {
                    "status": "vm_error",
                    "error": data.get("detail", data.get("error"))
                }
                continue

            # Success
            results[anchor] = {
                "status": "ok",
                "logits_mean": data.get("logits_mean"),
                "logits_std": data.get("logits_std")
            }

        except requests.RequestException as e:
            results[anchor] = {
                "status": "error",
                "error": f"Backend request failed: {str(e)}"
            }

    return results


def _map_task_to_syscall(task_type: str, description: str) -> Optional[str]:
    """Map task type to kernel syscall name."""
    desc_lower = description.lower()

    # Check for CDNA operations
    if "decode" in desc_lower and "cdna" in desc_lower:
        return "cdna.decode.v1"
    if "encode" in desc_lower and "cdna" in desc_lower:
        return "cdna.encode.v1"

    # Check for routing
    if "route" in desc_lower and "tensor" in desc_lower:
        return "routing.route_tensor.v1"

    # Check for chunk vault
    if "chunk" in desc_lower:
        if "get" in desc_lower:
            return "chunkvault.get.v1"
        if "store" in desc_lower:
            return "chunkvault.store.v1"

    return None


def _execute_via_bloomos_kernel(
    program: str,
    anchors: List[str],
    task_type: str,
    description: str
) -> Dict[str, Dict]:
    """
    Execute via BloomOS kernel using the local kernel module.

    When BLOOMOS_MODE=kernel, this will:
        1. Map task to kernel syscall
        2. Pre-authorize IT tokens
        3. Execute syscall handler
        4. Commit IT tokens and generate receipt
        5. Return results with kernel metadata

    Falls back to HTTP if kernel unavailable or syscall not found.
    """
    # Try local kernel first
    try:
        from .kernel import get_kernel

        kernel = get_kernel()
        syscall = _map_task_to_syscall(task_type, description)

        if syscall:
            # Build inputs from task description
            inputs = {
                "program": program,
                "task_type": task_type,
                "description": description,
            }

            result, receipt = kernel.registry.invoke(syscall, "bridge", inputs)

            # Return in expected format
            return {
                anchor: {
                    "status": "ok" if receipt.status == "OK" else "error",
                    "logits_mean": 0.0,  # Kernel doesn't produce logits
                    "logits_std": 0.0,
                    "kernel_receipt": receipt.to_dict(),
                }
                for anchor in anchors
            }

    except ImportError:
        pass  # Kernel not available
    except Exception as e:
        print(f"[Bridge] Local kernel error: {e}")

    # Try remote BloomClient
    if not BLOOMOS_CLIENT_AVAILABLE:
        print(f"[Bridge] WARNING: BloomOS client not available, "
              f"falling back to direct HTTP")
        return _execute_via_helix64_http(program, anchors)

    try:
        client = BloomClient(socket_path=BLOOMOS_SOCKET)

        # Pack payload as capsule structure
        payload = {
            "program": program,
            "anchors": anchors,
            "task_type": task_type,
            "description": description,
        }

        # Call kernel service
        resp = client.call(
            service="helix64.run",
            payload=payload,
            timeout_ms=BLOOMOS_KERNEL_TIMEOUT_MS,
        )

        if resp.get("status") != "ok":
            raise RuntimeError(f"kernel error: {resp.get('error', 'unknown')}")

        # Expect resp["anchors"] = {anchor: {"status": ..., "logits_mean": ..., "logits_std": ...}}
        return resp.get("anchors", {})

    except Exception as e:
        print(f"[Bridge] Kernel execution failed: {e}, falling back to HTTP")
        return _execute_via_helix64_http(program, anchors)


def get_bloomos_status() -> Dict[str, Any]:
    """
    Get current BloomOS integration status.

    Returns:
        dict with mode, backend URL, available features
    """
    return {
        "mode": BLOOMOS_MODE,
        "helix64_backend": HELIX64_BACKEND,
        "bloomos_socket": BLOOMOS_SOCKET,
        "bloomos_client_available": BLOOMOS_CLIENT_AVAILABLE,
        "kernel_timeout_ms": BLOOMOS_KERNEL_TIMEOUT_MS,
        "features": {
            "position4_gates": True,
            "fibpi3d_anchors": True,
            "bloomos_kernel": BLOOMOS_MODE == "kernel" and BLOOMOS_CLIENT_AVAILABLE,
            "capsules": True,  # Always available via sym.py
            "pheromones": BLOOMOS_MODE == "kernel" and BLOOMOS_CLIENT_AVAILABLE,
            "it_tokens": BLOOMOS_MODE == "kernel" and BLOOMOS_CLIENT_AVAILABLE,
            "persistent_state": True,  # Always available via state.py
        }
    }
