#!/usr/bin/env python3
"""
loop.py
-------
Regen -> Execution -> Pheromone Feedback Loop

The complete BloomOS task execution cycle:
1. Parse/compile command
2. Execute via backplane
3. Record in ledger
4. Emit pheromone signals (success/failure)
5. Return result with feedback metadata
"""

from __future__ import annotations

import os
import time
import json
from pathlib import Path
from typing import Dict, Any, List, Optional

from .backplane import DEFAULT_BACKPLANE
from .state import (
    append_ledger_entry,
    create_persistent_reflex_anchor,
    STATE_ROOT,
)

# Pheromone state file
PHEROMONE_FILE = Path(os.getenv(
    "BLOOMOS_PHEROMONE_FILE",
    str(STATE_ROOT / "pheromone.jsonl")
))

# Try to import external pheromone module if available
try:
    from helix_cdc.bloomos.pheromone import PheromoneBoard
    PHE_AVAILABLE = True
except Exception:
    PHE_AVAILABLE = False
    PheromoneBoard = None  # type: ignore


class SimplePheromoneBoard:
    """
    Simple file-based pheromone board for feedback signals.

    Pheromones are biological signals that reinforce successful paths.
    In BloomOS, they track:
    - task.success: Successful task completions
    - task.failure: Failed tasks
    - gate.used: Which gates were effective
    - anchor.hit: Which anchors were activated
    """

    def __init__(self, filepath: Path):
        self.filepath = filepath
        self.filepath.parent.mkdir(parents=True, exist_ok=True)

    def emit(self, signal_type: str, payload: Dict[str, Any]) -> None:
        """
        Emit a pheromone signal.

        Args:
            signal_type: Type of signal (task.success, task.failure, etc.)
            payload: Signal data
        """
        entry = {
            "ts": time.time(),
            "type": signal_type,
            "payload": payload,
        }
        with self.filepath.open("a", encoding="utf-8") as f:
            f.write(json.dumps(entry, sort_keys=True, default=str) + "\n")

    def read_signals(
        self,
        signal_type: Optional[str] = None,
        limit: int = 100
    ) -> List[Dict[str, Any]]:
        """Read pheromone signals from file."""
        if not self.filepath.exists():
            return []

        signals = []
        with self.filepath.open("r", encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                try:
                    entry = json.loads(line)
                    if signal_type is None or entry.get("type") == signal_type:
                        signals.append(entry)
                except Exception:
                    continue

        return signals[-limit:]

    def stats(self) -> Dict[str, Any]:
        """Get pheromone statistics."""
        signals = self.read_signals(limit=10000)

        if not signals:
            return {"total": 0, "types": {}}

        types: Dict[str, int] = {}
        for s in signals:
            t = s.get("type", "unknown")
            types[t] = types.get(t, 0) + 1

        return {
            "total": len(signals),
            "types": types,
            "first_ts": signals[0].get("ts") if signals else None,
            "last_ts": signals[-1].get("ts") if signals else None,
        }


# Initialize pheromone board
if PHE_AVAILABLE and PheromoneBoard is not None:
    _pheromone_board = PheromoneBoard(str(PHEROMONE_FILE))
else:
    _pheromone_board = SimplePheromoneBoard(PHEROMONE_FILE)


def run_task_with_feedback(
    raw_command: str,
    *,
    task_type_hint: Optional[str] = None,
    anchors: Optional[List[str]] = None,
    gate_policy: Optional[str] = None,
    return_capsule: bool = False,
) -> Dict[str, Any]:
    """
    Run a task with full pheromone feedback loop.

    This is the complete BloomOS execution cycle:
    1. Create reflex anchor for task
    2. Dispatch/compile/execute via backplane
    3. Record in ledger
    4. Emit pheromone based on success/failure
    5. Return result with feedback metadata

    Args:
        raw_command: Natural language or dot-notation command
        task_type_hint: Override task type classification
        anchors: Override anchors to use
        gate_policy: Override gate policy
        return_capsule: Include full .sym capsule in result

    Returns:
        Result dict with:
        - All backplane results
        - pheromone: Feedback metadata
        - elapsed: Execution time
    """
    t0 = time.time()

    # Lazy import to avoid cycles
    from .dispatcher import dispatch_command, parse_command

    # Parse to get task_id seed
    intent = parse_command(raw_command)
    task_id = f"{intent.task_type}_{int(t0 * 1000)}"

    # Create reflex anchor
    reflex = create_persistent_reflex_anchor(
        task_id=task_id,
        context=raw_command[:100],
        anchor_type="loop_reflex",
        metadata={"gate_policy": gate_policy, "anchors": anchors}
    )

    # Execute
    try:
        result = dispatch_command(
            raw_command,
            anchors=anchors,
            gate_policy=gate_policy,
            return_capsule=return_capsule,
        )
        status = "ok"
        error_msg = None
    except Exception as e:
        result = {"error": str(e)}
        status = "error"
        error_msg = str(e)

    dt = time.time() - t0

    # Extract gate info
    gate_used = result.get("gate", {}).get("policy", "unknown")
    anchors_used = result.get("anchors_resolved", [])
    logits_mean = result.get("logits_mean", 0.0)

    # Record in ledger
    append_ledger_entry({
        "task_id": task_id,
        "task_type": intent.task_type,
        "description": intent.description,
        "raw_command": raw_command,
        "gate_used": gate_used,
        "anchors_used": anchors_used,
        "logits_mean": logits_mean,
        "logits_std": result.get("logits_std", 0.0),
        "reflex_anchor_id": reflex.anchor_id,
        "status": status,
        "elapsed": dt,
        "source": "bloomos.loop",
    })

    # Emit pheromone
    phe_payload = {
        "task_id": task_id,
        "command": raw_command,
        "status": status,
        "gate": gate_used,
        "anchors": anchors_used,
        "logits_mean": float(logits_mean),
        "elapsed": dt,
    }

    if status == "ok":
        _pheromone_board.emit("task.success", phe_payload)

        # Also emit gate-specific signal
        if gate_used:
            _pheromone_board.emit(f"gate.{gate_used}", phe_payload)
    else:
        phe_payload["error"] = error_msg
        _pheromone_board.emit("task.failure", phe_payload)

    # Attach feedback metadata to result
    result["pheromone"] = {
        "signal": "task.success" if status == "ok" else "task.failure",
        "gate_signal": f"gate.{gate_used}" if status == "ok" else None,
        "reflex_anchor_id": reflex.anchor_id,
    }
    result["elapsed"] = dt
    result["task_id"] = task_id

    return result


def get_pheromone_stats() -> Dict[str, Any]:
    """Get pheromone board statistics."""
    return _pheromone_board.stats()


def get_recent_signals(
    signal_type: Optional[str] = None,
    limit: int = 10
) -> List[Dict[str, Any]]:
    """Get recent pheromone signals."""
    return _pheromone_board.read_signals(signal_type, limit)


# ============================================================================
# DEMO
# ============================================================================

def _demo():
    """Demonstrate the pheromone feedback loop."""
    print("=" * 70)
    print("BLOOMOS PHEROMONE FEEDBACK LOOP DEMO")
    print("=" * 70)
    print()

    print(f"PHEROMONE_FILE: {PHEROMONE_FILE}")
    print(f"PHE_AVAILABLE: {PHE_AVAILABLE}")
    print()

    # Run some tasks
    commands = [
        "solve 2x + 5 = 13",
        "write a python function",
        "list.all.files",
    ]

    for cmd in commands:
        print(f"COMMAND: {cmd}")
        result = run_task_with_feedback(cmd)
        print(f"  Task ID: {result.get('task_id')}")
        print(f"  Gate: {result.get('gate', {}).get('policy')}")
        print(f"  Pheromone: {result.get('pheromone', {}).get('signal')}")
        print(f"  Elapsed: {result.get('elapsed', 0):.3f}s")
        print()

    # Show stats
    print("PHEROMONE STATS:")
    stats = get_pheromone_stats()
    print(f"  Total signals: {stats.get('total')}")
    print(f"  By type: {stats.get('types')}")
    print()

    print("=" * 70)


if __name__ == "__main__":
    _demo()
