"""Slices CLI Entrypoint."""

import json
import logging
import os
import sys
from importlib.metadata import version as metadata_version
from pathlib import Path
from typing import Annotated

import typer
from msgspec import ValidationError
from slices_clientlib_core.base.auth import CredentialsBrokenError, load_auth_token_and_user
from slices_clientlib_core.base.config import (
    ConfigBrokenError,
    load_cli_central_api_clients_config,
)

from slices_cli_core.commands.auth import auth_app
from slices_cli_core.commands.experiment import experiment_app
from slices_cli_core.commands.project import project_app
from slices_cli_core.commands.pubkey import pubkey_app
from slices_cli_core.core.api import ApiClients
from slices_cli_core.core.context import Context, ContextObj
from slices_cli_core.core.users_info import OtherUsersInfo
from slices_cli_core.util.typer import AliasGroup

from . import console, err_console

# A hack to make help etc show "slices" instead of "slices-core"
# Except when using shell completion
if (
    sys.argv[0].endswith("-core")
    and "_SLICES_CORE_COMPLETE" not in os.environ
    and not ("--install-completion" in sys.argv or "--show-completion" in sys.argv)
):
    sys.argv[0] = sys.argv[0][: -len("-core")]

logging.basicConfig(level=logging.WARNING)

CONTEXT_SETTINGS = {
    "help_option_names": ["-h", "--help"],
}

app = typer.Typer(
    help="Slices CLI",
    context_settings=CONTEXT_SETTINGS,
    no_args_is_help=True,
    cls=AliasGroup,
)
AliasGroup.register_hidden_alias("experiment", "experiments")
AliasGroup.register_hidden_alias("pubkey", "pubkeys")
AliasGroup.register_hidden_alias("project", "projects")


def version_callback(version: bool):
    """Show CLI version."""

    if version:
        console.print(f"v{metadata_version('slices-cli-core')}", highlight=False)


def manifest_callback(manifest: bool):
    """Show slices CLI manifest."""
    if manifest:
        print(
            json.dumps(
                {
                    # The @type that identifies this as a slices cli MULTI subcommand manifest
                    "@type": "slices-cli:multi-subcommand-metadata",
                    # The version of the slices cli multi subcommand manifest (1.0 is the currently the only version)
                    "version": "1.0",
                    #
                    # Whether this subcommand supports command line tab completion.
                    "tab_completion_supported": True,
                    "tab_completion_install_option": "--install-completion",
                    # The subcommands this CLI supports
                    "subcommands": [
                        {"name": "auth", "short_help": "User authentication"},
                        {"name": "projects", "short_help": "Projects", "hidden_aliases": ["project"]},
                        {"name": "experiments", "short_help": "Experiments", "hidden_aliases": ["experiment"]},
                        {"name": "pubkey", "short_help": "SSH key management"},
                    ],
                }
            )
        )
        raise typer.Exit(0)


@app.callback(invoke_without_command=True)
def initialize_api_clients(
    ctx: Context,
    api_config: Annotated[
        Path | None, typer.Option(exists=True, dir_okay=False, hidden=True, envvar="SLICES_CUSTOM_CONFIG")
    ] = None,
    _version: Annotated[
        bool | None,
        typer.Option(
            "--version",
            callback=version_callback,
            help="Show CLI version.",
            is_eager=True,
        ),
    ] = None,
    _verbosity: Annotated[
        int,
        typer.Option(
            "-v",
            help="Verbosity level.",
            count=True,
        ),
    ] = 0,
    _manifest: Annotated[
        bool | None,
        typer.Option(
            "--cli-manifest",
            callback=manifest_callback,
            help="Show Slices CLI manifest.",
            is_eager=True,
            hidden=True,
        ),
    ] = None,
    _skip_upgrade_check: Annotated[
        bool | None,
        typer.Option(
            "--no-upgrade-check", help="Do not check for newer version.", envvar="SLICES_CLI_SKIP_UPGRADE_CHECK"
        ),
    ] = False,
):
    """Initialize API clients."""

    if ctx.resilient_parsing:
        # Don't initialize the API clients if we're auto-completing
        return

    # First we get the "central config". SlicesClient needs this.
    # This will get automatically get the config from the command line option, the env var, the cache, or online source.
    try:
        central_config = load_cli_central_api_clients_config(api_config)
    except ConfigBrokenError as e:
        err_console.print(f"[bold red]{e} :x:")
        raise typer.Exit(1) from None

    # Next, we'll get the auth token and user info.
    # This will automatically load the auth token from the cache, and refresh it if needed.
    # This also automatically supports external auth token providers that use SLICES_AUTH_TOKEN_PROVIDER.
    try:
        auth_token, user_info = load_auth_token_and_user()
    except CredentialsBrokenError as e:
        err_console.print(f"[bold red]{e} :x:")
        err_console.print("[red]Please log in again")
        auth_token = None
        user_info = None
        # We don't want to exit here because we want to allow the user to log in again.
        # raise typer.Exit(1) from None

    api_clients = ApiClients(central_config=central_config, auth_token=auth_token)
    ctx.obj = ContextObj(
        api=api_clients, auth_token=auth_token, user_info=user_info, other_users_info=OtherUsersInfo(api_clients)
    )


app.add_typer(auth_app, name="auth", rich_help_panel="Commands with subcommands")
app.add_typer(pubkey_app, name="pubkey", rich_help_panel="Commands with subcommands")
app.add_typer(experiment_app, name="experiment", rich_help_panel="Commands with subcommands")
app.add_typer(project_app, name="project", rich_help_panel="Commands with subcommands")

if __name__ == "__main__":
    try:
        app()
    except ValidationError as err:
        err_console.print("[bold red]Something is wrong with your input :x:")
        err_console.print(f"[red]{err}[/]")
        raise typer.Exit(1) from err
