"""Hive Engine -- The main orchestration loop.

This is the entry point that boots up all agents, wires them together,
and runs the continuous improvement cycle. It is the beating heart of
the self-modifying system.

Usage:
    engine = HiveEngine(workspace_root=Path("/path/to/project"))
    await engine.start()    # Initialize agents, index codebase, start monitoring
    await engine.run_cycle() # Run one observe→propose cycle
    await engine.stop()     # Graceful shutdown
"""

from __future__ import annotations

import logging
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from pathlib import Path

from hive.agents.advisor import Advisor
from hive.agents.analyst import Analyst
from hive.agents.builder import Builder
from hive.agents.ceo import HiveCEO
from hive.agents.watcher import Watcher
from hive.llm.client import HiveLLMClient

logger = logging.getLogger("hive.engine")


class HiveEngine:
    """Main orchestration engine for the Hive.CEO agent system.

    Lifecycle:
    1. start() -- Initialize all agents, index codebase, discover providers
    2. run_cycle() -- Execute one improvement cycle (observe→research→propose)
    3. execute_approved() -- Build approved items (build→deploy)
    4. verify() -- Verify deployed changes (verify→learn)
    5. stop() -- Graceful shutdown, persist state

    The engine can run in several modes:
    - Full: All agents active (interactive sessions)
    - Monitor: Only Watcher active (production health monitoring)
    - Maintenance: Watcher + Builder (automated maintenance)
    - Chat: CEO + Analyst (admin Q&A without modification)
    """

    def __init__(
        self,
        workspace_root: Path,
        health_url: str = "http://localhost:4000/health",
        mode: str = "full",
    ) -> None:
        self._root = workspace_root
        self._mode = mode
        self._running = False
        self._llm = HiveLLMClient()

        # Initialize agents
        self._ceo = HiveCEO(workspace_root)
        self._analyst = Analyst(workspace_root)
        self._advisor = Advisor(workspace_root, llm=self._llm)
        self._builder = Builder(workspace_root, llm=self._llm)
        self._watcher = Watcher(workspace_root, health_url=health_url)

        # Register all agents with CEO
        self._ceo.register_agent(self._analyst)
        self._ceo.register_agent(self._advisor)
        self._ceo.register_agent(self._builder)
        self._ceo.register_agent(self._watcher)

        # Apply mode restrictions
        self._apply_mode(mode)

        self._session_log: list[dict[str, Any]] = []

    def _apply_mode(self, mode: str) -> None:
        """Enable/disable agents based on operating mode."""
        if mode == "monitor":
            self._analyst.disable()
            self._advisor.disable()
            self._builder.disable()
        elif mode == "maintenance":
            self._analyst.disable()
            self._advisor.disable()
        elif mode == "chat":
            self._advisor.disable()
            self._builder.disable()
            self._watcher.disable()
        # "full" mode: all enabled (default)

    async def start(self) -> dict[str, Any]:
        """Initialize the Hive engine.

        1. Check LLM availability
        2. Index codebase (Analyst)
        3. Run initial health check (Watcher)
        4. Load institutional memory
        5. Report ready status
        """
        logger.info("Hive.CEO engine starting in %s mode", self._mode)
        self._running = True
        start_info: dict[str, Any] = {"mode": self._mode, "started": datetime.now(timezone.utc).isoformat()}

        # Check LLM availability
        llm_available = await self._llm.is_available()
        start_info["llm_available"] = llm_available
        if not llm_available:
            logger.warning("No LLM provider available. LLM-powered features will be limited.")

        # Index codebase
        if self._analyst.is_enabled:
            codebase_map = self._analyst.index_codebase()
            start_info["codebase_files"] = codebase_map.get("stats", {}).get("total_files", 0)
            start_info["codebase_lines"] = codebase_map.get("stats", {}).get("total_lines", 0)
            logger.info(
                "Codebase indexed: %d files, %d lines",
                start_info["codebase_files"],
                start_info["codebase_lines"],
            )

        # Initial health check
        if self._watcher.is_enabled:
            health = await self._watcher.check_health()
            start_info["health_score"] = health.get("score", 0)
            start_info["health_status"] = health.get("status", "unknown")
            logger.info("Initial health: %.1f (%s)", health.get("score", 0), health.get("status", "unknown"))

        # Load pending approvals
        pending = self._ceo.get_approval_queue()
        start_info["pending_approvals"] = len(pending)

        # Check signal queue for evolution signals
        start_info["registered_agents"] = [
            agent.name for agent in [self._analyst, self._advisor, self._builder, self._watcher] if agent.is_enabled
        ]

        self._log_session("engine_started", start_info)
        logger.info("Hive.CEO engine ready. Agents: %s", start_info["registered_agents"])
        return start_info

    async def run_cycle(self) -> dict[str, Any]:
        """Run one improvement cycle (Steps 1-3: Observe→Research→Propose).

        This is the main method called periodically or on-demand.
        """
        if not self._running:
            return {"status": "not_running"}

        result = await self._ceo.run_improvement_cycle()
        self._log_session("improvement_cycle", result)
        return result

    async def execute_approved(self) -> list[dict[str, Any]]:
        """Execute all approved items (Steps 5-6: Build→Deploy)."""
        if not self._running:
            return [{"status": "not_running"}]

        results = await self._ceo.execute_approved_items()
        self._log_session("execute_approved", {"results": results})
        return results

    async def verify_deployments(self) -> list[dict[str, Any]]:
        """Verify recent deployments (Step 7: Verify)."""
        if not self._running:
            return [{"status": "not_running"}]

        results = await self._ceo.verify_deployments()
        self._log_session("verify_deployments", {"results": results})
        return results

    async def learn(self) -> None:
        """Record outcomes in memory (Step 8: Learn)."""
        if not self._running:
            return
        await self._ceo.learn_from_outcomes()
        self._log_session("learn", {"status": "complete"})

    async def health_check(self) -> dict[str, Any]:
        """Run a health check (can be called independently)."""
        if self._watcher.is_enabled:
            return await self._watcher.check_health()
        return {"status": "watcher_disabled"}

    async def chat(self, message: str) -> str:
        """Handle an admin chat message.

        Uses LLM with codebase context to answer questions.
        """
        # Build context
        identity = self._ceo.read_identity()
        soul = self._ceo.read_soul()
        agent_statuses = self._ceo.get_all_agent_statuses()

        system_prompt = f"""You are the Hive.CEO agent for this application.

{soul}

## Application Identity
{identity[:2000]}

## Current Agent Status
{agent_statuses}

## Rules
- Be concise and technical
- Cite evidence (metrics, file paths, code references)
- If you don't know, say so
- Suggest specific actions when appropriate
"""

        try:
            response = await self._llm.complete(
                messages=[{"role": "user", "content": message}],
                system=system_prompt,
                temperature=0.5,
            )
            return response.content
        except Exception as e:
            logger.error("Chat LLM call failed: %s", e)
            return f"I'm having trouble connecting to my language model. Error: {e}"

    def process_signal(self, signal: dict[str, Any]) -> dict[str, Any]:
        """Forward a signal to the CEO for processing."""
        return self._ceo.process_signal(signal)

    async def stop(self) -> None:
        """Graceful shutdown -- persist state and close connections."""
        logger.info("Hive.CEO engine shutting down")
        self._running = False

        # Save session log
        self._save_session_log()

        logger.info("Hive.CEO engine stopped")

    # ------------------------------------------------------------------
    # Status & Introspection
    # ------------------------------------------------------------------

    def get_status(self) -> dict[str, Any]:
        """Get full engine status."""
        return {
            "running": self._running,
            "mode": self._mode,
            "agents": self._ceo.get_all_agent_statuses(),
            "approval_queue": len(self._ceo.get_approval_queue()),
            "emergency_mode": self._ceo._emergency_mode,
            "llm_usage": self._llm.get_usage_summary(),
        }

    @property
    def ceo(self) -> HiveCEO:
        """Access the CEO agent directly."""
        return self._ceo

    @property
    def analyst(self) -> Analyst:
        """Access the Analyst agent directly."""
        return self._analyst

    @property
    def advisor(self) -> Advisor:
        """Access the Advisor agent directly."""
        return self._advisor

    @property
    def builder(self) -> Builder:
        """Access the Builder agent directly."""
        return self._builder

    @property
    def watcher(self) -> Watcher:
        """Access the Watcher agent directly."""
        return self._watcher

    # ------------------------------------------------------------------
    # Session Logging
    # ------------------------------------------------------------------

    def _log_session(self, event: str, data: Any) -> None:
        """Add an event to the session log."""
        self._session_log.append(
            {
                "event": event,
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "data": data,
            }
        )

    def _save_session_log(self) -> None:
        """Persist session log to .hive/memory/."""
        if not self._session_log:
            return

        date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d")
        memory_dir = self._root / ".hive" / "memory"
        memory_dir.mkdir(parents=True, exist_ok=True)
        log_path = memory_dir / f"{date_str}.md"

        content = f"\n## Session: {datetime.now(timezone.utc).isoformat()}\n"
        for entry in self._session_log:
            content += f"- [{entry['timestamp']}] {entry['event']}\n"

        with log_path.open("a", encoding="utf-8") as f:
            f.write(content)
