"""Swarm Shell — MCP wrapper for the 19-agent I-Logic swarm.

Exposes Swarm dispatch, batch, compose, resolve, list, and summary
through the standard shell run() interface.

NO FOR-LOOPS in hot paths. All routing is O(1) I-Logic multiplication.
"""

from typing import Any, Dict, List, Optional

from ..swarm import Swarm, DispatchResult, WorkflowResult
from ..i_logic.states import ILogicState


class SwarmShell:
    """MCP shell wrapper for the Swarm agent router."""

    HEX_ID = "53574D00"  # SWM\0
    name = "swarm"
    version = "1.0.0"

    def __init__(self):
        self._swarm = Swarm()
        # Auto-connect ToolRegistry so agents can call tools.
        # Lazy import breaks the circular dependency
        # (mcp/tools.py imports SwarmShell at module load).
        from ..mcp.tools import ToolRegistry
        self._swarm.set_registry(ToolRegistry())

    # ------------------------------------------------------------------
    # COMMANDS
    # ------------------------------------------------------------------

    def status(self) -> Dict[str, Any]:
        """Swarm status: agent count, state distribution, layers."""
        return self._swarm.summary()

    def dispatch(self, task: str) -> Dict[str, Any]:
        """Dispatch a single task to the best agent.

        Args:
            task: Natural-language task description.

        Returns:
            Agent name, I-Logic outcome, resolution status, reasoning.
        """
        result = self._swarm.dispatch(task)
        return _dispatch_to_dict(result)

    def batch(self, *tasks: str) -> Dict[str, Any]:
        """Batch-dispatch multiple tasks. One call, all results.

        Args:
            tasks: Task descriptions (comma-separated in CLI).
        """
        task_list = list(tasks)
        if len(task_list) == 1 and "," in task_list[0]:
            task_list = list(map(str.strip, task_list[0].split(",")))
        results = self._swarm.dispatch_batch(task_list)
        resolved_count = len(list(filter(lambda r: r.resolved, results)))
        return {
            "dispatched": len(results),
            "results": list(map(_dispatch_to_dict, results)),
            "resolved": resolved_count,
            "resolution_rate": round(
                resolved_count / max(len(results), 1), 3
            ),
        }

    def compose(self, *agent_names: str) -> Dict[str, Any]:
        """Compose a multi-agent workflow.

        Args:
            agent_names: Agent names in execution order.
        """
        names = list(agent_names)
        if len(names) == 1 and "," in names[0]:
            names = list(map(str.strip, names[0].split(",")))
        wf = self._swarm.compose_workflow(names)
        return {
            "agents": list(map(lambda a: a.name, wf.agents)),
            "states": list(map(lambda s: ILogicState.name(s), wf.states)),
            "final_state": wf.final_state_name,
            "steps": wf.steps,
            "resolved": wf.resolved,
            "path": wf.path_description,
        }

    def resolve(self, state_name: str = "INVERSE") -> Dict[str, Any]:
        """Find the agent that resolves a condition to DIRECT.

        Args:
            state_name: I-Logic state name (DIRECT, INVERSE, SPIRAL, ANTI_SPIRAL).
        """
        state = ILogicState.from_name(state_name.upper())
        agent = self._swarm.find_resolver(state)
        if agent:
            return {
                "condition": state_name.upper(),
                "resolver": agent.name,
                "agent_id": agent.agent_id,
                "agent_state": ILogicState.name(agent.i_logic_state),
                "algebra": f"{state_name} × {ILogicState.name(agent.i_logic_state)} = DIRECT",
            }
        path = self._swarm.find_resolution_path(state)
        if path and path.resolved:
            return {
                "condition": state_name.upper(),
                "single_resolver": None,
                "chain": path.path_description,
                "agents": [a.name for a in path.agents],
                "resolved": True,
            }
        return {"condition": state_name.upper(), "resolver": None, "resolved": False}

    def agents(self, layer: str = "") -> Dict[str, Any]:
        """List agents, optionally filtered by layer.

        Args:
            layer: Layer number (1-10) or empty for all.
        """
        if layer and layer.isdigit():
            agents = self._swarm.get_agents_for_layer(int(layer))
        else:
            agents = self._swarm.list_agents()
        return {
            "count": len(agents),
            "agents": list(map(
                lambda a: {
                    "id": a.agent_id,
                    "name": a.name,
                    "layer": a.layer,
                    "layer_name": a.layer_name,
                    "role": a.role,
                    "state": ILogicState.name(a.i_logic_state),
                    "specialization": a.specialization,
                },
                agents,
            )),
        }

    def basin(self, basin_id: str = "0") -> Dict[str, Any]:
        """Find agents whose basin range covers a basin.

        Args:
            basin_id: Basin number (0-130).
        """
        bid = int(basin_id)
        agents = self._swarm.get_agents_for_basin(bid)
        return {
            "basin": bid,
            "agents": list(map(
                lambda a: {"name": a.name, "layer": a.layer, "state": ILogicState.name(a.i_logic_state)},
                agents,
            )),
        }

    def execute(self, task: str) -> Dict[str, Any]:
        """Dispatch AND execute — agent calls tools from its coordinate.

        The agent doesn't just get selected — it calls tools from its
        basin position. The coordinate IS the routing.

        Args:
            task: Natural-language task description.
        """
        return self._swarm.execute(task)

    def field(self, *contents: str) -> Dict[str, Any]:
        """Map a concept field — encode, route, physics, interact, light.

        ONE call. The system encodes, routes, computes physics,
        interactions, and Fernandez sphere positioning. Uses C backend
        for coordinates. Batch APIs for routing. No for-loops.

        Args:
            contents: Content strings (comma-separated in CLI).

        Returns:
            Full field: tokens, routing, world bodies, interactions,
            light mapping, behavioral groups, agent ownership.
        """
        content_list = list(contents)
        if len(content_list) == 1 and "," in content_list[0]:
            content_list = list(map(str.strip, content_list[0].split(",")))
        return self._swarm.map_field(content_list)

    def execute_batch(self, *tasks: str) -> Dict[str, Any]:
        """Batch execute — all tasks dispatched and executed.

        Args:
            tasks: Task descriptions (comma-separated or multiple args).
        """
        task_list = list(tasks)
        if len(task_list) == 1 and "," in task_list[0]:
            task_list = list(map(str.strip, task_list[0].split(",")))
        results = self._swarm.execute_batch(task_list)
        return {
            "executed": len(results),
            "results": results,
        }

    # ------------------------------------------------------------------
    # SHELL INTERFACE
    # ------------------------------------------------------------------

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Execute a swarm command.

        Commands:
            status   — Agent count, state distribution
            dispatch — Route a task to best agent
            batch    — Batch-dispatch multiple tasks
            compose  — Compose multi-agent workflow
            resolve  — Find resolver for an I-Logic condition
            agents   — List agents (optional layer filter)
            basin    — Find agents for a basin
        """
        handlers = {
            "status": lambda: self.status(),
            "dispatch": lambda: self.dispatch(*args) if args else self.dispatch("status check"),
            "batch": lambda: self.batch(*args) if args else self.batch("ping"),
            "compose": lambda: self.compose(*args) if args else self.compose("Navigator,Generator"),
            "resolve": lambda: self.resolve(args[0]) if args else self.resolve("INVERSE"),
            "agents": lambda: self.agents(args[0]) if args else self.agents(),
            "basin": lambda: self.basin(args[0]) if args else self.basin("0"),
            "execute": lambda: self.execute(*args) if args else self.execute("status check"),
            "execute_batch": lambda: self.execute_batch(*args) if args else self.execute_batch("ping"),
            "field": lambda: self.field(*args) if args else self.field("test"),
        }
        handler = handlers.get(command, handlers["status"])
        return handler()


def _dispatch_to_dict(result: DispatchResult) -> Dict[str, Any]:
    """Convert DispatchResult to dict without for-loops."""
    d = {
        "agent": result.agent.name,
        "agent_id": result.agent.agent_id,
        "layer": result.agent.layer,
        "task_state": ILogicState.name(result.task_state),
        "outcome": result.outcome_name,
        "resolved": result.resolved,
        "confidence": round(result.confidence, 3),
        "reasoning": result.reasoning,
    }
    if result.chain and result.chain.resolved:
        d["chain"] = result.chain.path_description
    return d
