"""Command-line interface for Yera."""

from __future__ import annotations

import argparse
import logging
import signal
import sys
import time
import webbrowser
from pathlib import Path
from typing import Literal

from infra_mvp.stream.container import create_stream_server
from yera import llm
from yera.agents.decorator import AgentFunctionWrapper
from yera.agents.discovery import discover_agents, load_agent
from yera.config.logging import setup_logging


def _register_shutdown_handler(server) -> None:
    """Register signal handlers for graceful shutdown."""

    def shutdown(_sig, _frame):
        logging.info("Shutting down server...")
        server.stop()
        sys.exit(0)

    signal.signal(signal.SIGINT, shutdown)
    signal.signal(signal.SIGTERM, shutdown)


def _run_developement_environment(
    agents: dict[str, AgentFunctionWrapper],
    startup_agent_id: str | None = None,
    port: int = 8991,
    host: str = "127.0.0.1",
    log_events: bool = False,
    no_browser: bool = False,
    dev_ui: bool = False,
) -> None:
    """Run a Yera development environment."""
    server = create_stream_server(
        agents=agents,
        host=host,
        port=port,
        log_events=log_events,
        dev_ui=dev_ui,
    )

    _register_shutdown_handler(server)

    if dev_ui:
        ui_url = "http://localhost:3000"
        logging.info("Development mode: expecting UI dev server at %s", ui_url)
    else:
        ui_url = f"http://{host}:{port}"
        logging.info("Serving bundled UI from the stream server")

    if startup_agent_id:
        ui_url += f"/agents/{startup_agent_id}"

    logging.info(f"Starting Yera stream server on {server.url_base}")
    if startup_agent_id:
        logging.info(f"Agent ID: {startup_agent_id}")
    logging.info("Press Ctrl+C to stop the server")

    with server:
        time.sleep(0.5)  # Wait for server ready

        if no_browser:
            logging.info(f"Server ready. Access the UI at: {ui_url}")
        else:
            logging.info(f"Opening browser to: {ui_url}")
            webbrowser.open(ui_url)

        while True:
            time.sleep(1)


def run_agent(
    agent_path: str,
    port: int = 8991,
    host: str = "127.0.0.1",
    agent_id: str | None = None,
    log_events: bool = False,
    no_browser: bool = False,
    dev_ui: bool = False,
) -> None:
    """Run a Yera agent and view it in the UI.

    Args:
        agent_path: Path to the agent file containing an agent.
        port: Server port number (default: 8991).
        host: Server host address (default: 127.0.0.1).
        agent_id: Optional agent id when multiple agents exist in a file.
        log_events: Enable logging of events to file.
        no_browser: If True, don't automatically open browser window.
        dev_ui: If True, use Next.js dev server (localhost:3000) instead of bundled static UI.
    """
    try:
        agent = load_agent(agent_path, agent_id)
    except (FileNotFoundError, ValueError):
        logging.exception("Error loading Python agent")
        sys.exit(1)

    _run_developement_environment(
        agents=[agent],
        startup_agent_id=agent.metadata.identifier,
        port=port,
        host=host,
        log_events=log_events,
        no_browser=no_browser,
        dev_ui=dev_ui,
    )


def run_dev(
    working_directory: str | None = None,
    port: int = 8991,
    host: str = "127.0.0.1",
    log_events: bool = False,
    no_browser: bool = False,
    dev_ui: bool = False,
) -> None:
    """Run a local Yera development environment.

    Args:
        working_directory: Directory to run the development environment in.
        port: Server port number (default: 8991).
        host: Server host address (default: 127.0.0.1).
        log_events: Enable logging of events to file (default: False).
        no_browser: If True, don't automatically open browser window (default: False).
        dev_ui: Allow development UI to be served from the stream server.
    """
    working_directory = (
        Path(working_directory) if working_directory is not None else Path.cwd()
    )

    agents = discover_agents(working_directory)

    if not agents:
        logging.error(f"No agents found in {working_directory}")
        sys.exit(1)

    _run_developement_environment(
        agents=agents,
        port=port,
        host=host,
        log_events=log_events,
        no_browser=no_browser,
        dev_ui=dev_ui,
    )


loc_map = {
    "user": "global",
    "project": "local",
}
loc_paths = {
    "user": Path.home() / ".config" / "yera" / "yera.toml",
    "project": Path.cwd() / "yera.toml",
}


def run_setup(location: Literal["user", "project"], auto_import: bool):
    from yera.config2.setup import set_default_llm, setup

    if location not in loc_map:
        valid = ", ".join(loc_map.keys())
        raise ValueError(f"{location} is not a valid location. Choose from {valid}.")

    print(f"""
Welcome to the Yera setup helper!

This script will do the following:
  * find your existing provider credentials
  * store them safely on your OS's secure keyring
  * use them to find your available models.
  * create or extend your yera.toml file with your available models.

Yera currently supports Anthropic, OpenAI, Azure, LlamaCpp, Ollama.

Note: Azure users may need to run `az login`

We're going to set up for your {location}-level configuration at
  * {loc_paths[location]!s}

If you're happy to continue enter \"y\" below:""")
    res = input(" > ")
    if res != "y":
        print("Response was not y. Exiting...")
        return

    setup(loc_map[location], auto_import)

    set_default_llm(loc_map[location])


def run_show(to_show: Literal["llms"]):
    valid = ["llms"]

    if to_show == "llms":
        print(llm._ensure_atlas())
    else:
        valid_str = ", ".join(valid)
        raise ValueError(
            f"Cannot show objects for {to_show}. Valid options are {valid_str}"
        )


def parse_cli_args(
    args: list[str] | None = None,
) -> tuple[argparse.Namespace, argparse.ArgumentParser]:
    """Parse CLI arguments.

    Args:
        args: Optional list of arguments to parse. If None, uses sys.argv[1:].

    Returns:
        Tuple of (parsed arguments namespace, parser instance).
    """
    parser = argparse.ArgumentParser(
        description="Yera CLI - Run agentic apps and view them in the UI"
    )
    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # Run subcommand
    run_parser = subparsers.add_parser(
        "run",
        help="Run a Yera app and view it in the UI",
    )
    run_parser.add_argument(
        "agent_path",
        type=str,
        help="Path to the Python agent file to run",
    )
    run_parser.add_argument(
        "--agent-id",
        type=str,
        default=None,
        help=(
            "ID of the Python agent to run when multiple agents exist in a single file."
        ),
    )
    run_parser.add_argument(
        "--port",
        type=int,
        default=8991,
        help="Server port number (default: 8991)",
    )
    run_parser.add_argument(
        "--host",
        type=str,
        default="127.0.0.1",
        help="Server host address (default: 127.0.0.1)",
    )
    run_parser.add_argument(
        "--log-events",
        action="store_true",
        help="Enable logging of events to file (logs to yera-event-log-<timestamp>.jsonl)",
    )
    run_parser.add_argument(
        "--no-browser",
        action="store_true",
        help="Don't automatically open browser window",
    )
    run_parser.add_argument(
        "--dev-ui",
        action="store_true",
        help="Use Next.js dev server (localhost:3000) instead of bundled static UI",
    )

    # Dev subcommand
    dev_parser = subparsers.add_parser(
        "dev",
        help="Run a local Yera development environment",
    )
    dev_parser.add_argument(
        "--dir",
        type=str,
        default=None,
        help="Directory to run the development environment in",
    )
    dev_parser.add_argument(
        "--port",
        type=int,
        default=8991,
        help="Server port number (default: 8991)",
    )
    dev_parser.add_argument(
        "--host",
        type=str,
        default="127.0.0.1",
        help="Server host address (default: 127.0.0.1)",
    )
    dev_parser.add_argument(
        "--log-events",
        action="store_true",
        help="Enable logging of events to file (logs to yera-event-log-<timestamp>.jsonl)",
    )
    dev_parser.add_argument(
        "--no-browser",
        action="store_true",
        help="Don't automatically open browser window",
    )
    dev_parser.add_argument(
        "--dev-ui",
        action="store_true",
        help="Use Next.js dev server (localhost:3000) instead of bundled static UI",
    )

    setup_parser = subparsers.add_parser("setup", help="Run Yera setup helper")
    setup_parser.add_argument(
        "--location",
        type=str,
        default="user",
        help="""Where to build or update your Yera config at. Can be user or project:
 * user: ~/.config/yera/yera.toml
 * project: ./yera.toml
""",
    )

    show_parser = subparsers.add_parser("show", help="Print useful info to the shell")
    show_parser.add_argument(
        "object_type",
        type=str,
        help="""What type of object to show. LLMs only for now""",
    )

    if args is None:
        parsed_args = parser.parse_args()
    else:
        parsed_args = parser.parse_args(args)

    return parsed_args, parser


def main() -> None:
    """Main entry point for yera CLI."""
    setup_logging()

    args, parser = parse_cli_args()

    match args.command:
        case "run":
            run_agent(
                agent_path=args.agent_path,
                port=args.port,
                host=args.host,
                agent_id=args.agent_id,
                log_events=args.log_events,
                no_browser=args.no_browser,
                dev_ui=args.dev_ui,
            )
        case "dev":
            run_dev(
                working_directory=args.dir,
                port=args.port,
                host=args.host,
                log_events=args.log_events,
                no_browser=args.no_browser,
                dev_ui=args.dev_ui,
            )
        case "setup":
            run_setup(args.location, True)
        case "show":
            run_show(args.object_type.lower())
        case _:
            parser.print_help()

    #    logging.info("Exiting Yera CLI")
    sys.exit(0)


if __name__ == "__main__":
    main()
