"""
BizNode -- Digital Business Operator
Copyright 2026 1BZ DZIT DAO LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

---

Agent Runtime
=============
Separates task execution (runtime) from AI orchestration (intelligence).

Agent Runtime (execution layer):
  +-- Task Queue       (automation/agent_loop.py)
  +-- Worker Pool      (bizchannel/workers/)
  +-- Scheduler        (automation/scheduler.py)
  +-- Agent Lifecycle  (start, pause, resume, cancel)

AI Orchestration (intelligence layer):
  +-- LangGraph        (graphs/)
  +-- Decision Graph   (agent/decision_graph.py)
  +-- RAG Graph        (graphs/rag_query_graph.py)

The runtime does NOT make AI decisions. It manages lifecycle, queues tasks,
and delegates intelligence calls to the orchestration layer when needed.
"""

import logging
import uuid
from datetime import datetime
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)


class AgentRuntime:
    """Manages agent lifecycle and coordinates between runtime and AI layers."""

    def __init__(self):
        self.task_queue = None       # automation.agent_loop.AgentLoop
        self.scheduler = None        # automation.scheduler.Scheduler
        self.orchestrator = None     # services.node_orchestrator.NodeOrchestrator
        self.reputation = None       # services.reputation_service.ReputationService
        self._running = False
        self._started_at = None
        self._pending_tasks: List[Dict] = []

    # ------------------------------------------------------------------
    # Lifecycle
    # ------------------------------------------------------------------

    def start(self) -> None:
        """Initialize all runtime components."""
        if self._running:
            logger.warning("AgentRuntime is already running")
            return

        logger.info("Starting AgentRuntime...")

        # Initialize Node Orchestrator
        from services.node_orchestrator import NodeOrchestrator
        self.orchestrator = NodeOrchestrator()

        # Initialize Reputation Service
        from services.reputation_service import ReputationService
        self.reputation = ReputationService()

        # Initialize Agent Loop (task queue)
        try:
            from automation.agent_loop import AgentLoop
            self.task_queue = AgentLoop()
            logger.info("Agent task queue initialized")
        except Exception as e:
            logger.warning("Could not initialize AgentLoop: %s", e)

        # Initialize Scheduler
        try:
            from automation.scheduler import Scheduler
            self.scheduler = Scheduler()
            logger.info("Scheduler initialized")
        except Exception as e:
            logger.warning("Could not initialize Scheduler: %s", e)

        self._running = True
        self._started_at = datetime.utcnow().isoformat()
        logger.info("AgentRuntime started at %s", self._started_at)

    def shutdown(self) -> None:
        """Graceful shutdown of all runtime components."""
        if not self._running:
            return

        logger.info("Shutting down AgentRuntime...")

        if self.scheduler:
            try:
                self.scheduler.stop()
            except Exception as e:
                logger.warning("Error stopping scheduler: %s", e)

        self.task_queue = None
        self.scheduler = None
        self._running = False
        logger.info("AgentRuntime shut down")

    # ------------------------------------------------------------------
    # Task submission
    # ------------------------------------------------------------------

    def submit_task(
        self,
        goal: str,
        user_id: Optional[str] = None,
        route_to_node: Optional[str] = None,
        task_type: str = "general",
        requirements: Optional[List[str]] = None,
    ) -> Dict[str, Any]:
        """Submit a task for execution.

        If route_to_node is specified, the task targets that node directly.
        Otherwise, the orchestrator selects the best node based on capabilities
        and reputation.
        """
        task_id = str(uuid.uuid4())
        task = {
            "task_id": task_id,
            "goal": goal,
            "user_id": user_id,
            "task_type": task_type,
            "submitted_at": datetime.utcnow().isoformat(),
            "status": "queued",
            "assigned_node": None,
        }

        # Route to a node
        if route_to_node:
            task["assigned_node"] = route_to_node
        elif self.orchestrator:
            best = self.orchestrator.route_task(task_type, requirements)
            task["assigned_node"] = best

        self._pending_tasks.append(task)
        logger.info(
            "Task %s submitted: '%s' -> node=%s",
            task_id, goal[:50], task["assigned_node"],
        )
        return task

    # ------------------------------------------------------------------
    # Status
    # ------------------------------------------------------------------

    def get_status(self) -> Dict[str, Any]:
        """Full runtime status overview."""
        nodes = []
        if self.orchestrator:
            nodes = self.orchestrator.get_node_status()

        online_count = sum(1 for n in nodes if isinstance(n, dict) and n.get("status") == "online")

        return {
            "running": self._running,
            "started_at": self._started_at,
            "components": {
                "task_queue": self.task_queue is not None,
                "scheduler": self.scheduler is not None,
                "orchestrator": self.orchestrator is not None,
                "reputation": self.reputation is not None,
            },
            "pending_tasks": len(self._pending_tasks),
            "network": {
                "total_nodes": len(nodes),
                "online_nodes": online_count,
            },
        }

    @property
    def is_running(self) -> bool:
        return self._running
