"""Slices AI CLI authentication."""

import os
from typing import Annotated

import typer
from requests.exceptions import HTTPError
from slices_clientlib_core.base.auth import auth_file_location, delete_auth_token, parse_auth_token, save_auth_token
from slices_clientlib_core.base.config import (
    set_config_string,
)
from slices_clientlib_core.oauth2.flow import AuthenticationFlow, DeviceFlow

from . import console, err_console
from .context import Context

auth_app = typer.Typer(help="User authentication", no_args_is_help=True)


@auth_app.command(help="Authenticate and retrieve credentials", rich_help_panel="Commands")
def login(
    ctx: Context,
    open_browser: Annotated[bool, typer.Option(help="Automatically open browser to perform login.")] = False,
):
    """Authenticate and retrieve credentials."""

    if os.environ.get("SLICES_AUTH_TOKEN_PROVIDER"):
        err_console.print("[bold red]Authentication is handled by external token provider.")

    if not open_browser:
        flow = DeviceFlow()

        console.print(f"Please go to {flow._device_code_info['verification_uri_complete']} and login to continue.")
        try:
            with console.status("[mangenta]Waiting for authorization"):
                token = flow.wait_for_authorization_token()
        except TimeoutError:
            err_console.print("The user code has expired. Please start the login flow again.")
            raise typer.Exit(1) from None
        except ValueError:
            err_console.print("Could not retrieve authentication details from device login flow.")
            raise typer.Exit(1) from None
    else:
        flow = AuthenticationFlow()

        try:
            with console.status("[mangenta]Waiting for authentication response"):
                token = flow.wait_for_authorization_token()
        except TimeoutError:
            err_console.print("Timeout while waiting for authorization token.")
            raise typer.Exit(1) from None
        except ValueError:
            err_console.print("Could not exchange authorization code for a token.")
            raise typer.Exit(1) from None

    if token:
        save_auth_token(token)

        auth_info = parse_auth_token(token)
        console.print(f"[bold green]Successfully logged in as '{auth_info['preferred_username']}' ")
        console.print(f":floppy_disk: Saved to {auth_file_location}")

        if ctx.obj.get_current_project_id() is None:
            console.print(
                ":backhand_index_pointing_right: "
                "Now select the project that you want to use with [blue]slices project use <project name>[/]"
            )

    else:
        err_console.print("[red]Logging in failed. :x:")
        raise typer.Exit(1)


@auth_app.command(help="Show user credentials.", rich_help_panel="Commands")
def show(  # noqa: PLR0912
    ctx: Context,
    show_id: Annotated[bool, typer.Option("--show-id", "-I", help="Show internal ID.")] = False,
    show_access_token: Annotated[bool, typer.Option(help="Show JWT Access Token.")] = False,
    show_id_token: Annotated[bool, typer.Option(help="Show JWT ID Token.")] = False,
    show_refresh_token: Annotated[bool, typer.Option(help="Show JWT Refresh Token.")] = False,
):
    """Show user credentials."""
    user_info = ctx.obj.user_info

    if user_info:
        if not show_id:
            console.print(f"Logged in as '{user_info['preferred_username']}'")
        else:
            console.print(f"Logged in as '{user_info['sub']}'")

        if show_access_token:
            if ctx.obj.api.auth_token:
                console.print(f"Access Token: '{ctx.obj.api.auth_token.access_token}'")
            else:
                console.print("Access Token: none")
        if show_id_token:
            if ctx.obj.api.auth_token:
                console.print(f"ID Token: '{ctx.obj.api.auth_token.id_token}'")
            else:
                console.print("ID Token: none")
        if show_refresh_token:
            if ctx.obj.api.auth_token:
                console.print(f"Refresh Token: '{ctx.obj.api.auth_token.refresh_token}'")
            else:
                console.print("Refresh Token: none")
    else:
        err_console.print("[red]Not logged in. :x:")
        raise typer.Exit(1)


@auth_app.command(help="Remove user credentials", rich_help_panel="Commands")
def logout():
    """Remove user credentials."""
    if os.environ.get("SLICES_AUTH_TOKEN_PROVIDER"):
        err_console.print("[red]Cannot logout when using an external token provider. :x:")
        raise typer.Exit(1)

    delete_auth_token()
    set_config_string("default", None, "project")
    console.print("[bold green]Successfully logged out.[/] :white_check_mark:")
    console.print(f":floppy_disk: Deleted {auth_file_location}")


@auth_app.command(help="Get credentials for a specific audience.", rich_help_panel="Advanced Commands")
def get_for_audience(
    ctx: Context,
    audience: str,
    with_project: Annotated[bool, typer.Option(help="Include project ID in the token.")] = True,
):
    """Print user credentials for a specific audience."""
    user_info = ctx.obj.user_info

    if not user_info:
        err_console.print("[red]Not logged in. :x:")
        raise typer.Exit(1)
    try:
        print(
            ctx.obj.api.get_access_token_for_audience(audience, ctx.obj.require_project_id() if with_project else None)
        )
    except HTTPError as e:
        try:
            if e.response:
                error_response = e.response.json()
                err_console.print(
                    f"[bold red]Error: Could not get token. :x:[/]\n[red]{error_response['error_description']}"
                )
            else:
                err_console.print("[bold red]Error: Could not get token. :x:")
        except:  # noqa: E722
            err_console.print("[bold red]Error: Could not get token. :x:")
            if e.response:
                err_console.print(f"[red]{str(e.response.text)}")
        raise typer.Exit(1) from None


@auth_app.command(help="Get id_token for a specific audience.", rich_help_panel="Advanced Commands")
def id_token(
    ctx: Context,
    audience: str,
):
    """Print id_token for a specific audience."""

    print(ctx.obj.api.get_id_token_for_audience(audience, ctx.obj.require_project_id()))
