"""BI CLI."""

from datetime import datetime, timezone
from typing import TYPE_CHECKING, Annotated

import typer

from slices_cli_bi import console, err_console
from slices_cli_bi.util.ssh_known_hosts import SSHKnownHostsManager

from .bi_context import BIContext
from .bi_helpers import bi_get_best_ssh_login, bi_get_experiment, bi_get_requested_bi_resources, handle_some_exceptions

if TYPE_CHECKING:
    from collections.abc import Collection

    from slices_clientlib_bi.bi_resource import ComputeResource

known_hosts_app = typer.Typer(no_args_is_help=True)


@known_hosts_app.command("add", rich_help_panel="Commands")
@handle_some_exceptions
def add_known_hosts(
    ctx: BIContext,
    experiment: Annotated[
        str,
        typer.Option(..., help="Experiment name or ID.", prompt=True, show_default=False, envvar="SLICES_EXPERIMENT"),
    ],
    resources: Annotated[list[str] | None, typer.Argument(help="Resource names or IDs.", show_default=False)] = None,
    proxy: Annotated[
        bool,
        typer.Option(
            "--proxy/--no-proxy",
            # "-p/-P",
            help="Also add SSH proxy host keys (if any). (Default: --proxy)",
            show_default=False,
        ),
    ] = True,
    verbose: Annotated[
        bool,
        typer.Option(
            "--verbose",
            "-v",
            help="Output what is done",
            show_default=False,
        ),
    ] = False,
):
    """Add host keys to the ~/.ssh/known_hosts file, for the specified resource(s)."""
    exp = bi_get_experiment(ctx, experiment)

    if exp.expires_at < datetime.now(timezone.utc):
        err_console.print(f"[red]Experiment {exp.friendly_name} has expired :x:")
        raise typer.Exit(1)

    resources_to_add: Collection[ComputeResource] = bi_get_requested_bi_resources(
        ctx, exp, resources, include_expired=False, include_terminated=False
    )

    if verbose:
        console.print(f"[grey]{len(resources_to_add)} active resource(s) in experiment {exp.friendly_name}.")

    changed = False
    no_ssh_login_count = 0
    SSHKnownHostsManager().cleanup_expired()
    for compute_resource in resources_to_add:
        ssh_login, can_connect = bi_get_best_ssh_login(compute_resource, with_proxy=False)
        if ssh_login:
            cur_changed = SSHKnownHostsManager().add_login(compute_resource, ssh_login)
            if verbose:
                if cur_changed:
                    console.print(
                        f"[grey]Added hosts keys ({len(ssh_login.host_keys or [])}) "
                        f"for {compute_resource.friendly_name!r}."
                    )
                else:
                    console.print(
                        f"[grey]Hosts keys ({len(ssh_login.host_keys or [])}) "
                        f"for {compute_resource.friendly_name!r} already up to date."
                    )
            changed = cur_changed or changed
            if proxy and ssh_login.jump_proxy:
                cur_changed = SSHKnownHostsManager().add_login(compute_resource, ssh_login.jump_proxy)
                changed = cur_changed or changed
                if verbose:
                    if cur_changed:
                        console.print(
                            f"[grey]Added proxy hosts keys ({len(ssh_login.jump_proxy.host_keys or [])}) for "
                            f"{compute_resource.friendly_name!r}."
                        )
                    else:
                        console.print(
                            f"[grey]Proxy hosts keys ({len(ssh_login.jump_proxy.host_keys or [])}) for "
                            f"{compute_resource.friendly_name!r} already up to date."
                        )
        else:
            no_ssh_login_count += 1
            if verbose:
                console.print(f"[grey]No SSH Login for {compute_resource.friendly_name!r}.")
    if no_ssh_login_count:
        err_console.print(f"[yellow]Ignored {no_ssh_login_count} resources without SSH info :warning:")

    if changed:
        console.print("~/.ssh/known_hosts was updated.")
    else:
        console.print("~/.ssh/known_hosts was already up to date.")


@known_hosts_app.command("remove", rich_help_panel="Commands")
@handle_some_exceptions
def remove_known_hosts(
    ctx: BIContext,
    experiment: Annotated[
        str,
        typer.Option(..., help="Experiment name or ID.", prompt=True, show_default=False, envvar="SLICES_EXPERIMENT"),
    ],
    resources: Annotated[list[str] | None, typer.Argument(help="Resource names or IDs.", show_default=False)] = None,
):
    """Remove host keys to the ~/.ssh/known_hosts file, for the specified resources.

    Proxy host keys are automatically removed, once each resource that uses the proxy is removed.
    """
    SSHKnownHostsManager().cleanup_expired()

    exp = bi_get_experiment(ctx, experiment)

    if exp.expires_at < datetime.now(timezone.utc):
        err_console.print(f"Experiment {exp.friendly_name} has expired. Resources have been cleaned up.")
        raise typer.Exit(0)

    resources_to_remove: Collection[ComputeResource] = bi_get_requested_bi_resources(
        ctx, exp, resources, include_expired=False, include_terminated=True
    )

    changed = False
    for compute_resource in resources_to_remove:
        changed = SSHKnownHostsManager().cleanup(compute_resource.id) or changed
    if changed:
        console.print("~/.ssh/known_hosts was updated.")
    else:
        console.print("~/.ssh/known_hosts was already up to date.")


@known_hosts_app.command("cleanup", rich_help_panel="Commands")
@handle_some_exceptions
def cleanup_known_hosts():
    """Cleanup host keys of expired resources in ~/.ssh/known_hosts.

    This command is always safe and sane to do.
    All other functionality that manipulates ~/.ssh/known_hosts (add, remove, ssh) does this cleanup as well.
    """
    if SSHKnownHostsManager().cleanup_expired():
        console.print("~/.ssh/known_hosts was updated.")
    else:
        console.print("~/.ssh/known_hosts was already up to date.")
