"""
Chat Agent - LLM-backed conversational agent using EchoCogRuntime

WO-ECHO-CHAT-AGENT-01: Wraps the cognitive runtime to provide
natural language responses for queries that don't match tool intents.
"""

import asyncio
import hashlib
import json
import os
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, Optional

from . import (
    AgentResult,
    DispatchableAgent,
    PROJECT_ROOT,
    generate_agent_id,
)

# Lazy import to avoid hard dependency
_runtime_instance: Optional[Any] = None


def _get_runtime():
    """Lazy-load EchoCogRuntime singleton."""
    global _runtime_instance
    if _runtime_instance is not None:
        return _runtime_instance

    try:
        from echo_cog import EchoCogRuntime, get_helix_regen_config
    except ImportError:
        return None

    # Check for model path
    model_path = os.environ.get("ECHO_MODEL_PATH")
    if not model_path:
        # Try to load from index
        try:
            cfg = get_helix_regen_config()
        except Exception:
            return None
    else:
        from echo_cog import EchoCogConfig
        llama_cli = Path(os.environ.get(
            "ECHO_LLAMA_CLI",
            Path.home() / "llama.cpp/build/bin/llama-cli"
        ))
        cfg = EchoCogConfig(
            llama_cli=llama_cli,
            model_path=Path(model_path),
        )

    # Apply environment overrides
    cfg.backend = os.environ.get("ECHO_BACKEND", "llama_cpp")
    cfg.threads = int(os.environ.get("ECHO_THREADS", "8"))
    cfg.ctx_size = int(os.environ.get("ECHO_CTX_SIZE", "2048"))
    cfg.max_tokens = int(os.environ.get("ECHO_MAX_TOKENS", "256"))

    _runtime_instance = EchoCogRuntime(cfg)
    return _runtime_instance


class ChatAgent(DispatchableAgent):
    """
    LLM-backed conversational agent.

    Uses EchoCogRuntime (llama_cpp or llama_cli backend) for natural
    language responses. Falls back gracefully if no model available.
    """

    @property
    def agent_type(self) -> str:
        return "chat"

    def __init__(self, receipt_dir: str = "receipts/chat_agent"):
        self.receipt_dir = PROJECT_ROOT / receipt_dir
        self._available: Optional[bool] = None

    def available(self) -> bool:
        if self._available is None:
            runtime = _get_runtime()
            self._available = runtime is not None
        return self._available

    async def execute(self, query: str, context: Dict[str, Any]) -> AgentResult:
        start_time = time.perf_counter()
        agent_id = generate_agent_id()

        runtime = _get_runtime()
        if runtime is None:
            return AgentResult(
                agent_id=agent_id,
                agent_type=self.agent_type,
                query=query,
                result=None,
                sources=[],
                confidence=0.0,
                elapsed_ms=0.0,
                error="EchoCogRuntime not available (check ECHO_MODEL_PATH or model index)",
            )

        # Run inference in thread pool (blocking call)
        loop = asyncio.get_event_loop()
        try:
            answer, latency_sec = await loop.run_in_executor(
                None,
                lambda: runtime.answer_question(query, task="echo_terminal"),
            )
        except Exception as e:
            elapsed_ms = (time.perf_counter() - start_time) * 1000
            return AgentResult(
                agent_id=agent_id,
                agent_type=self.agent_type,
                query=query,
                result=None,
                sources=[],
                confidence=0.0,
                elapsed_ms=elapsed_ms,
                error=f"Inference failed: {e}",
            )

        elapsed_ms = (time.perf_counter() - start_time) * 1000

        # Build receipt
        receipt = self._build_receipt(query, answer, latency_sec, agent_id)
        receipt_path = self._save_receipt(receipt)

        result_data = {
            "answer": answer,
            "latency_sec": latency_sec,
            "backend": os.environ.get("ECHO_BACKEND", "llama_cpp"),
            "receipt_path": str(receipt_path) if receipt_path else None,
        }

        return AgentResult(
            agent_id=agent_id,
            agent_type=self.agent_type,
            query=query,
            result=result_data,
            sources=[f"receipt:{receipt_path}"] if receipt_path else [],
            confidence=0.9,  # LLM always has something to say
            elapsed_ms=elapsed_ms,
            error=None,
        )

    def _build_receipt(self, query: str, answer: str, latency_sec: float, agent_id: str) -> Dict:
        timestamp = datetime.now(timezone.utc)
        query_sha = hashlib.sha256(query.encode()).hexdigest()
        answer_sha = hashlib.sha256(answer.encode()).hexdigest()

        return {
            "receipt_id": f"chat_{timestamp.strftime('%Y%m%d')}_{agent_id[:8]}",
            "timestamp": timestamp.isoformat(),
            "agent_type": "chat",
            "query_sha256": query_sha,
            "answer_sha256": answer_sha,
            "latency_sec": latency_sec,
            "backend": os.environ.get("ECHO_BACKEND", "llama_cpp"),
            "model_path": os.environ.get("ECHO_MODEL_PATH", "index:helix_regen"),
        }

    def _save_receipt(self, receipt: Dict) -> Optional[Path]:
        try:
            self.receipt_dir.mkdir(parents=True, exist_ok=True)
            path = self.receipt_dir / f"{receipt['receipt_id']}.json"
            with open(path, "w") as f:
                json.dump(receipt, f, indent=2)
            return path
        except Exception:
            return None


# Auto-register on import
from . import register_agent as _register
try:
    _register(ChatAgent())
except ValueError:
    pass  # Already registered
