"""CLI entry point — argparse + mode dispatch.

Usage:
    icarus                             → Textual TUI (interactive)
    icarus run "fix the bug"           → Headless mode
    icarus --model azure_ai:Kimi-K2.5  → Override model
    icarus --auto-approve              → Skip HITL
"""

from __future__ import annotations

import argparse
import asyncio
import logging
import os
import sys
from collections.abc import Callable


def _make_help_action(
    print_help: Callable[[], None],
) -> type[argparse.Action]:
    """Create an argparse Action that prints custom help and exits."""

    class _HelpAction(argparse.Action):
        def __init__(
            self,
            option_strings,
            dest=argparse.SUPPRESS,
            default=argparse.SUPPRESS,
            **kwargs,
        ):
            super().__init__(
                option_strings=option_strings,
                dest=dest,
                default=default,
                nargs=0,
                **kwargs,
            )

        def __call__(self, parser, namespace, values, option_string=None):
            print_help()
            parser.exit()

    return _HelpAction


def build_parser() -> argparse.ArgumentParser:
    """Build the argument parser."""
    parser = argparse.ArgumentParser(
        prog="icarus",
        description="ICARUS — AI Coding Agent",
    )

    parser.add_argument(
        "--model", "-M",
        type=str,
        default=None,
        help="Model override (e.g. 'azure_ai:Kimi-K2.5')",
    )
    parser.add_argument(
        "--auto-approve",
        action="store_true",
        default=False,
        help="Skip HITL approval for all tool calls",
    )
    parser.add_argument(
        "--debug",
        action="store_true",
        default=False,
        help="Enable debug logging",
    )
    parser.add_argument(
        "--quiet", "-q",
        action="store_true",
        default=False,
        help="Quiet mode (stderr for status, stdout for response only)",
    )

    subparsers = parser.add_subparsers(dest="command")

    run_parser = subparsers.add_parser("run", help="Run a task non-interactively")
    run_parser.add_argument("message", type=str, help="The task message to execute")

    subparsers.add_parser("models", help="List available models")

    from icarus_cli.skills_commands import setup_skills_parser

    setup_skills_parser(subparsers, make_help_action=_make_help_action)

    # Server commands
    setup_p = subparsers.add_parser("setup", help="Configure the Icarus server")
    setup_p.add_argument("--non-interactive", action="store_true")
    setup_p.add_argument("--port", type=int, default=8642)
    setup_p.add_argument("--bind", default="0.0.0.0")
    setup_p.add_argument("--admin-username", default=None)
    setup_p.add_argument("--admin-password", default=None)
    setup_p.add_argument("--daemon", choices=["user", "system", "none"], default="none")
    setup_p.add_argument("--skip-health", action="store_true")

    serve_p = subparsers.add_parser("serve", help="Start the Icarus HTTP server")
    serve_p.add_argument("--port", type=int, default=None)
    serve_p.add_argument("--bind", default=None)

    subparsers.add_parser("start", help="Start Icarus daemon (Linux systemd)")
    subparsers.add_parser("stop", help="Stop Icarus daemon (Linux systemd)")

    # User management
    user_p = subparsers.add_parser("user", help="Manage server users")
    user_sub = user_p.add_subparsers(dest="user_command")

    user_add = user_sub.add_parser("add", help="Create a new user")
    user_add.add_argument("username")
    user_add.add_argument("--email", required=True)
    user_add.add_argument("--role", choices=["user", "admin", "read-only"], default="user")

    user_list_p = user_sub.add_parser("list", help="List all users")
    user_list_p.add_argument("--pending", action="store_true")

    user_reset = user_sub.add_parser("reset-password", help="Reset a user's password")
    user_reset.add_argument("username")

    user_role = user_sub.add_parser("set-role", help="Change a user's role")
    user_role.add_argument("username")
    user_role.add_argument("role", choices=["admin", "user", "read-only", "pending"])

    user_revoke = user_sub.add_parser("revoke", help="Revoke all sessions")
    user_revoke.add_argument("username")

    user_unlock_p = user_sub.add_parser("unlock", help="Unlock a locked account")
    user_unlock_p.add_argument("username")

    user_remove = user_sub.add_parser("remove", help="Remove a user")
    user_remove.add_argument("username")

    return parser


def _require_serve() -> None:
    """Exit with a helpful message if the [serve] extra is not installed."""
    try:
        import icarus_server  # noqa: F401
    except ImportError:
        print("This command requires the [serve] extra. Install with: pip install icarus[serve]")
        sys.exit(1)


def _systemctl_cmd(action: str) -> None:
    """Run systemctl start/stop for the icarus daemon."""
    import platform
    import subprocess

    if platform.system() != "Linux":
        print("Daemon mode requires Linux with systemd. Use 'icarus serve' for foreground.")
        sys.exit(1)
    _require_serve()
    from icarus_server.config import read_config

    config = read_config()
    if not config or config.daemon_mode == "none":
        print("No daemon configured. Run 'icarus setup' and choose user/system service.")
        sys.exit(1)
    cmd = ["systemctl"]
    if config.daemon_mode == "user":
        cmd.append("--user")
    cmd.extend([action, "icarus"])
    subprocess.run(cmd, check=True)


def _handle_user_command(args: "argparse.Namespace") -> None:
    """Dispatch icarus user sub-commands."""
    from icarus_server.auth.db import get_user_by_email, get_user_by_username, list_users, delete_user
    from icarus_server.auth.local import LocalAuthProvider
    from icarus_server.config import SERVER_DIR

    db_path = SERVER_DIR / "server.db"
    if not db_path.exists():
        print("No server.db found. Run 'icarus setup' first.")
        sys.exit(1)

    provider = LocalAuthProvider(db_path)

    if args.user_command == "add":
        from getpass import getpass
        password = getpass(f"Password for {args.username}: ")
        user = provider.register(
            username=args.username, email=args.email,
            password=password, role=args.role,
        )
        print(f"User '{args.username}' created (id: {user.id})")

    elif args.user_command == "list":
        role_filter = "pending" if args.pending else None
        users = list_users(db_path, role=role_filter)
        if not users:
            print("No users found.")
            return
        print(f"{'USERNAME':<20} {'ROLE':<12} {'EMAIL':<30} {'PROVIDER':<10}")
        for u in users:
            print(f"{u.username:<20} {u.role:<12} {u.email:<30} {u.auth_provider:<10}")

    elif args.user_command == "reset-password":
        from getpass import getpass
        user = get_user_by_username(db_path, args.username) or get_user_by_email(db_path, args.username)
        if not user:
            print(f"User '{args.username}' not found.")
            sys.exit(1)
        password = getpass("New password: ")
        provider.update_password(user.id, password)
        print(f"Password updated for '{args.username}'")

    elif args.user_command == "set-role":
        user = get_user_by_username(db_path, args.username)
        if not user:
            print(f"User '{args.username}' not found.")
            sys.exit(1)
        provider.set_role(user.id, args.role)
        print(f"Role updated to '{args.role}' for '{args.username}'")

    elif args.user_command == "revoke":
        user = get_user_by_username(db_path, args.username)
        if not user:
            print(f"User '{args.username}' not found.")
            sys.exit(1)
        provider.revoke_all_sessions(user.id)
        print(f"All sessions revoked for '{args.username}'")

    elif args.user_command == "unlock":
        print("Account lockouts are in-memory. Restart 'icarus serve' to clear all lockouts.")
        print("For persistent lockout clearing, the admin API will be added in a future version.")

    elif args.user_command == "remove":
        user = get_user_by_username(db_path, args.username)
        if not user:
            print(f"User '{args.username}' not found.")
            sys.exit(1)
        delete_user(db_path, user.id)
        print(f"User '{args.username}' removed")

    else:
        print("Usage: icarus user {add|list|reset-password|set-role|revoke|unlock|remove}")


def main() -> None:
    """Main entry point for the ICARUS CLI."""
    parser = build_parser()
    args = parser.parse_args()

    log_level = logging.DEBUG if args.debug else logging.WARNING
    logging.basicConfig(
        level=log_level,
        format="%(asctime)s %(name)s %(levelname)s %(message)s",
    )

    if args.command == "run":
        from icarus_cli.headless import run_headless

        exit_code = asyncio.run(
            run_headless(
                args.message,
                model=args.model,
                auto_approve=args.auto_approve,
                quiet=args.quiet,
            )
        )
        sys.exit(exit_code)

    elif args.command == "models":
        from icarus.config.model_config import ModelConfig, get_available_models

        config = ModelConfig.load()
        available = get_available_models()
        default = config.default_model or config.recent_model
        models = [
            f"{provider}:{model}"
            for provider, model_list in sorted(available.items())
            for model in model_list
        ]
        if models:
            if default:
                print(f"Default: {default}")
            print("\nAvailable models:")
            for m in models:
                marker = " (default)" if m == default else ""
                print(f"  - {m}{marker}")
        else:
            print("No models configured. Add providers to ~/.icarus/config.toml")

    elif args.command == "skills":
        from icarus_cli.skills_commands import execute_skills_command

        execute_skills_command(args)

    elif args.command in ("setup", "user", "serve"):
        try:
            import icarus_server  # noqa: F401
        except ImportError:
            print("Server features require: pip install icarus[serve]", file=sys.stderr)
            sys.exit(1)

        if args.command == "setup":
            if args.non_interactive:
                from icarus_server.setup_wizard import run_setup_non_interactive

                admin_password = args.admin_password or os.environ.get("ICARUS_ADMIN_PASSWORD")
                run_setup_non_interactive(
                    port=args.port, bind=args.bind,
                    admin_username=args.admin_username,
                    admin_password=admin_password,
                    daemon=args.daemon, skip_health=args.skip_health,
                )
            else:
                from icarus_server.setup_wizard import run_setup_interactive

                run_setup_interactive()

        elif args.command == "user":
            _handle_user_command(args)

        elif args.command == "serve":
            from icarus_server.config import read_config

            config = read_config()
            if not config:
                print("No server.toml found. Run 'icarus setup' first.")
                sys.exit(1)
            if args.port:
                config.port = args.port
            if args.bind:
                config.bind = args.bind
            from icarus_server.app import create_app

            import uvicorn

            app = create_app(config)
            uvicorn.run(app, host=config.bind, port=config.port, ws_per_message_deflate=False)

    elif args.command in ("start", "stop"):
        _systemctl_cmd(args.command)

    else:
        # Interactive TUI mode
        asyncio.run(_run_tui(args))


async def _run_tui(args: argparse.Namespace) -> None:
    """Launch the ICARUS Textual TUI."""
    try:
        from icarus_tui.app import run_textual_app
    except ImportError:
        print("TUI requires: pip install icarus[tui]", file=sys.stderr)
        sys.exit(1)

    from icarus.mcp import MCPManager
    from icarus.runtime.builder import create_icarus_agent
    from icarus.runtime.context import UserContext
    from icarus.session.threads import get_checkpointer

    ctx = UserContext.default()

    # Load MCP tools if ~/.icarus/mcp.json exists
    mcp_manager = MCPManager.from_config_file(ctx.config_dir / "mcp.json")
    mcp_tools, _ = await mcp_manager.start() if mcp_manager else ([], [])

    # Use SQLite checkpointer for session persistence across restarts
    async with get_checkpointer() as checkpointer:
        result = create_icarus_agent(
            ctx,
            model=args.model,
            tools=mcp_tools or None,
            auto_approve=args.auto_approve,
            checkpointer=checkpointer,
        )

        try:
            app_result = await run_textual_app(
                agent=result.graph,
                backend=result.backend,
                auto_approve=args.auto_approve,
                cwd=str(ctx.working_dir),
                checkpointer=checkpointer,
                ctx=ctx,
                mcp_tools=mcp_tools,
            )
            if app_result.return_code != 0:
                sys.exit(app_result.return_code)
        finally:
            if mcp_manager:
                await mcp_manager.stop()
            if hasattr(result.model, "aclose"):
                await result.model.aclose()


if __name__ == "__main__":
    main()
