"""Slices AI CLI Entrypoint."""

import json
import logging
import os
import sys
from pathlib import Path
from typing import Annotated

import msgspec
import typer
from msgspec import ValidationError
from slices_clientlib_aiis.base.config import SlicesAIApiClientsConfig
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,
    load_current_project_id,
)

from slices_cli_ai.ai_common import version_callback
from slices_cli_ai.util.typer import AliasGroup

from . import VERBOSITY_DEBUG, VERBOSITY_TRACE, console, err_console
from .ai import ai_app
from .api import ApiClients
from .context import Context, ContextObj

# A hack to make help etc show "slices" instead of "slices-ai"
# Except when using shell completion or installing completions
if (
    sys.argv[0].endswith("-ai")
    and "_SLICES_AI_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("-ai")]


logging.basicConfig(level=logging.WARNING)

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

app = typer.Typer(
    help="Slices CLI ai",
    context_settings=CONTEXT_SETTINGS,
    no_args_is_help=True,
    cls=AliasGroup,
)
# AliasGroup.register_hidden_alias("foobar", "foobars")


def manifest_callback(manifest: bool):
    """Show slices CLI manifest."""
    if manifest:
        print(
            json.dumps(
                {
                    # The @type that identifies this as a slices cli subcommand manifest
                    "@type": "slices-cli:subcommand-metadata",
                    # The version of the slices cli 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",
                    "tab_completion_show_option": "--show-completion",
                    # The help text for this subcommand, to be shown in the main slices CLI help.
                    "short_help": "AI Infrastructure Service",
                }
            )
        )
        raise typer.Exit(0)


@app.callback(invoke_without_command=True)
def initialize_api_clients(
    ctx: Context,
    ai_api_config: Annotated[
        Path | None, typer.Option(exists=True, dir_okay=False, hidden=True, envvar="SLICES_AI_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,
    api_config: Annotated[
        Path | None, typer.Option(exists=True, dir_okay=False, hidden=True, envvar="SLICES_CUSTOM_CONFIG")
    ] = None,
):
    """Initialize API clients."""

    # 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)
        if verbosity >= VERBOSITY_TRACE:
            console.print(f"[bright_black]Loaded central config: {central_config!r}")
    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()
        if verbosity >= VERBOSITY_TRACE:
            console.print(f"[bright_black]Loaded auth token: {auth_token}")
        if verbosity >= VERBOSITY_DEBUG:
            console.print(f"[bright_black]Authenticated User ID: {user_info['sub'] if user_info else None}")
    except CredentialsBrokenError as e:
        err_console.print(f"[bold red]{e} :x:")
        err_console.print("[red]Please log in again")
        user_info = None
        raise typer.Exit(1) from None

    # We choose to not support the case where the user is not logged in.
    if user_info is None:
        err_console.print("[bold red]You must be logged in to use this command :x:")
        raise typer.Exit(1)

    # We get the current project ID.
    # This automatically loads the project ID from the setting, or from the env var.
    project_id = load_current_project_id()
    if verbosity >= VERBOSITY_DEBUG:
        console.print(f"[bright_black]Loaded project ID: {project_id!r}")

    ai_config = None
    if ai_api_config:
        try:
            with ai_api_config.open() as f:
                config_str = f.read()
            config_dict = json.loads(config_str)
            if config_dict.get("pubkey") or config_dict.get("bi"):
                err_console.print("[bold red]You are using an old version of the API config. Please upgrade. :x:")
                raise typer.Exit(1)

            if config_dict.get("bi_infrastructures"):
                ai_config = msgspec.json.decode(config_str, type=SlicesAIApiClientsConfig)

            if not ai_config:
                err_console.print(
                    f"[bold red]Your AI config file ('{ai_api_config}') does not contain a valid AI config:x:"
                )
                raise typer.Exit(1)
        except ValidationError as err:
            err_console.print("[bold red]Something is wrong with your AI api configuration :x:")
            err_console.print("\n", str(err))
            raise typer.Exit(1) from err

    api_clients = ApiClients(central_config=central_config, ai_config=ai_config, auth_token=auth_token)
    ctx.obj = ContextObj(api=api_clients, user_info=user_info, project_id=project_id, verbosity=verbosity)


app.add_typer(ai_app, name="ai", 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
