"""Argparse definition for the albus CLI.

Builds the parser and provides a dispatch function that converts
argparse.Namespace into the appropriate Pydantic model before calling
the command handler.
"""

from __future__ import annotations

import argparse

from server.transport.cli._models import (
    ChatArgs,
    ConfigArgs,
    InitArgs,
    ListArgs,
    PathwayArgs,
    RunArgs,
    ServerArgs,
    StudioArgs,
    ToolCallArgs,
    ValidateArgs,
)
from server.transport.cli.chat import cmd_chat
from server.transport.cli.config import cmd_config
from server.transport.cli.init import cmd_init
from server.transport.cli.list import cmd_list
from server.transport.cli.pathway import cmd_pathway
from server.transport.cli.run import cmd_run
from server.transport.cli.server import cmd_server, cmd_studio
from server.transport.cli.tool import cmd_tool
from server.transport.cli.validate import cmd_validate


def build_parser() -> argparse.ArgumentParser:
    """Build the full CLI argument parser."""
    parser = argparse.ArgumentParser(
        prog="albus",
        description="AlbusOS CLI - Pathway-first agent orchestration",
        epilog="Run 'albus' with no args to start interactive studio.",
    )
    subparsers = parser.add_subparsers(dest="command", required=True)

    # -------------------------------------------------------------------------
    # run - Execute a pathway
    # -------------------------------------------------------------------------
    p_run = subparsers.add_parser("run", help="Run a pathway (YAML file or registered ID)")
    p_run.add_argument("target", help="Pathway YAML file or registered pathway ID")
    p_run.add_argument("--input", "-i", help='Input JSON (e.g., \'{"query": "AI"}\')')
    p_run.add_argument(
        "--server", "-s", action="store_true", help="Run via server (for registered pathways)"
    )
    p_run.add_argument("--host", default="127.0.0.1")
    p_run.add_argument("--port", type=int, default=8080)
    p_run.set_defaults(_cmd="run", _handler=cmd_run, _model=RunArgs)

    # -------------------------------------------------------------------------
    # chat - Talk to an agent
    # -------------------------------------------------------------------------
    p_chat = subparsers.add_parser("chat", help="Send a message to an agent")
    p_chat.add_argument("message", help="Message to send")
    p_chat.add_argument("--agent", "-a", default="host", help="Agent ID (default: host)")
    p_chat.add_argument("--host", default="127.0.0.1")
    p_chat.add_argument("--port", type=int, default=8080)
    p_chat.set_defaults(_cmd="chat", _handler=cmd_chat, _model=ChatArgs)

    # -------------------------------------------------------------------------
    # tool - Direct tool execution
    # -------------------------------------------------------------------------
    p_tool = subparsers.add_parser("tool", help="Call a tool directly")
    p_tool.add_argument("tool_name", help="Tool name (e.g., web.search)")
    p_tool.add_argument("tool_args", nargs="*", help="Tool arguments (--key value)")
    p_tool.set_defaults(_cmd="tool", _handler=cmd_tool, _model=ToolCallArgs)

    # -------------------------------------------------------------------------
    # pathway - Lifecycle commands
    # -------------------------------------------------------------------------
    p_pathway = subparsers.add_parser("pathway", help="Pathway lifecycle commands")
    p_pathway.add_argument("--host", default="127.0.0.1")
    p_pathway.add_argument("--port", type=int, default=8080)
    pw_sub = p_pathway.add_subparsers(dest="action", required=True)

    pw_list = pw_sub.add_parser("list", help="List registered pathways")
    pw_list.set_defaults(_cmd="pathway", _handler=cmd_pathway, _model=PathwayArgs)

    pw_new = pw_sub.add_parser("new", help="Create a new pathway scaffold")
    pw_new.add_argument("id", help="Pathway ID")
    pw_new.add_argument("--mode", "-m", choices=["dag", "stateful"], default="dag")
    pw_new.add_argument("--output", "-o", help="Output file (default: stdout)")
    pw_new.set_defaults(_cmd="pathway", _handler=cmd_pathway, _model=PathwayArgs)

    pw_validate = pw_sub.add_parser("validate", help="Validate a pathway YAML")
    pw_validate.add_argument("target", help="Pathway YAML file")
    pw_validate.set_defaults(_cmd="pathway", _handler=cmd_pathway, _model=PathwayArgs)

    pw_export = pw_sub.add_parser("export", help="Export a registered pathway to YAML")
    pw_export.add_argument("id", help="Pathway ID")
    pw_export.add_argument("--output", "-o", help="Output file (default: stdout)")
    pw_export.set_defaults(_cmd="pathway", _handler=cmd_pathway, _model=PathwayArgs)

    pw_load = pw_sub.add_parser("load", help="Load a pathway YAML into the server")
    pw_load.add_argument("target", help="Pathway YAML file")
    pw_load.set_defaults(_cmd="pathway", _handler=cmd_pathway, _model=PathwayArgs)

    pw_hash = pw_sub.add_parser("hash", help="Get content hash of a pathway (for CI/CD)")
    pw_hash.add_argument("target", help="Pathway YAML file")
    pw_hash.set_defaults(_cmd="pathway", _handler=cmd_pathway, _model=PathwayArgs)

    # -------------------------------------------------------------------------
    # list - Discovery
    # -------------------------------------------------------------------------
    p_list = subparsers.add_parser("list", help="List resources")
    p_list.add_argument("--host", default="127.0.0.1")
    p_list.add_argument("--port", type=int, default=8080)
    list_sub = p_list.add_subparsers(dest="resource", required=True)

    list_tools = list_sub.add_parser("tools", help="List registered tools")
    list_tools.add_argument("--category", "-c", help="Filter by category (e.g., web, agent)")
    list_tools.set_defaults(_cmd="list", _handler=cmd_list, _model=ListArgs)

    list_pathways = list_sub.add_parser("pathways", help="List registered pathways")
    list_pathways.set_defaults(_cmd="list", _handler=cmd_list, _model=ListArgs)

    list_skills = list_sub.add_parser("skills", help="List available skills")
    list_skills.set_defaults(_cmd="list", _handler=cmd_list, _model=ListArgs)

    list_agents = list_sub.add_parser("agents", help="List registered agents")
    list_agents.set_defaults(_cmd="list", _handler=cmd_list, _model=ListArgs)

    list_node_types = list_sub.add_parser("node-types", help="List pathway node types")
    list_node_types.set_defaults(_cmd="list", _handler=cmd_list, _model=ListArgs)

    # -------------------------------------------------------------------------
    # config - Configuration
    # -------------------------------------------------------------------------
    p_config = subparsers.add_parser("config", help="Configuration commands")
    cfg_sub = p_config.add_subparsers(dest="action", required=True)

    cfg_validate = cfg_sub.add_parser("validate", help="Validate deployment config")
    cfg_validate.add_argument("--config", help="Config file path (default: auto-detect)")
    cfg_validate.set_defaults(_cmd="config", _handler=cmd_config, _model=ConfigArgs)

    cfg_models = cfg_sub.add_parser("models", help="Show model routing configuration")
    cfg_models.set_defaults(_cmd="config", _handler=cmd_config, _model=ConfigArgs)

    # -------------------------------------------------------------------------
    # init - Initialize workspace
    # -------------------------------------------------------------------------
    p_init = subparsers.add_parser(
        "init",
        help="Initialize a new AlbusOS workspace (optionally scan existing code)",
    )
    p_init.add_argument(
        "--minimal", action="store_true", help="Create minimal config (no comments/examples)"
    )
    p_init.add_argument(
        "--scan",
        metavar="SOURCE_DIR",
        help="Scan a Python codebase and generate native albusOS skills from it. "
        "Works with MCP servers, FastAPI apps, plain scripts — any Python source.",
    )
    p_init.add_argument(
        "--namespace",
        help="Skill namespace for scanned tools (default: inferred from source dir name)",
    )
    p_init.set_defaults(_cmd="init", _handler=cmd_init, _model=InitArgs)

    # -------------------------------------------------------------------------
    # server - Start the server
    # -------------------------------------------------------------------------
    p_server = subparsers.add_parser("server", help="Run Albus server")
    p_server.add_argument("--host", default="127.0.0.1")
    p_server.add_argument("--port", type=int, default=8080)
    p_server.add_argument("--debug", action="store_true")
    p_server.add_argument("--log-level", default="")
    p_server.set_defaults(_cmd="server", _handler=cmd_server, _model=ServerArgs)

    # -------------------------------------------------------------------------
    # validate - Run validation suites
    # -------------------------------------------------------------------------
    p_validate = subparsers.add_parser("validate", help="Run a validation suite against live agents")
    p_validate.add_argument("target", help="Validation suite YAML file")
    p_validate.add_argument("--verbose", "-v", action="store_true", help="Show detailed output")
    p_validate.add_argument("--stop-on-fail", action="store_true", help="Stop after first failure")
    p_validate.set_defaults(_cmd="validate", _handler=cmd_validate, _model=ValidateArgs)

    # -------------------------------------------------------------------------
    # studio - Interactive terminal UI
    # -------------------------------------------------------------------------
    p_studio = subparsers.add_parser("studio", help="Launch interactive terminal studio")
    p_studio.add_argument("--host", default="127.0.0.1")
    p_studio.add_argument("--port", type=int, default=8080)
    p_studio.add_argument("--thread-id", default="default")
    p_studio.set_defaults(_cmd="studio", _handler=cmd_studio, _model=StudioArgs)

    return parser


def dispatch(ns: argparse.Namespace) -> int:
    """Convert argparse Namespace to Pydantic model and dispatch to handler.

    Each subparser registers ``_handler`` and ``_model`` via ``set_defaults``,
    so dispatch is a single model construction + function call — no if/elif chain.
    """
    handler = getattr(ns, "_handler", None)
    model_cls = getattr(ns, "_model", None)

    if not handler or not model_cls:
        print(f"Unknown command: {getattr(ns, '_cmd', ns.command)}")
        return 2

    # Strip argparse internals; Pydantic ignores unknown fields via model_config.
    raw = {k: v for k, v in vars(ns).items() if not k.startswith("_")}
    return handler(model_cls.model_validate(raw))
