"""alice auth — Authenticate and store credentials for future sessions."""

from __future__ import annotations

import os
import shutil

import click

from alice_cli import personality
from alice_cli.auth import detect_identity_from_key, detect_username
from alice_cli.aws.session import clear_all_sessions
from alice_cli.console import print_banner, print_status, print_success
from alice_cli.errors import AliceCLIError
from alice_cli.secrets import build_secret_path, fetch_credentials, fetch_inference_profiles
from alice_cli.store import (
    StoredCredentials,
    clear_stored_credentials,
    load_stored_credentials,
    save_stored_credentials,
)


@click.command("auth")
@click.option("--profile", default=None, help="AWS CLI profile to authenticate with")
@click.option("--api-key", default=None, help="Bedrock API key to store")
@click.option(
    "--use-stored-key", is_flag=True, help="Re-authenticate using the previously stored API key"
)
@click.option("--claude", is_flag=True, help="Launch Claude Code with credentials")
@click.option("--flush", is_flag=True, help="Clear stored credentials and exit")
@click.pass_context
def auth(
    ctx: click.Context,
    profile: str | None,
    api_key: str | None,
    use_stored_key: bool,
    claude: bool,
    flush: bool,
) -> None:
    """Authenticate and save credentials locally.

    Validates SSO session (--profile) or saves API key (--api-key). After authenticating
    once, subsequent commands work without flags. Use --claude to launch Claude Code.
    """
    config = ctx.obj["config"]

    if flush:
        clear_stored_credentials()
        clear_all_sessions()
        # Unset all credential env vars set by `alice auth --claude`.
        # Print `unset` lines to stdout so `eval $(alice auth --flush)` also
        # cleans up the calling shell.
        _flush_credential_env()
        print_success("Stored credentials cleared.", config.quiet)
        return

    # Resolve bare --use-stored-key to the previously stored key
    if use_stored_key:
        existing = load_stored_credentials()
        if not existing.bedrock_secret_key:
            raise AliceCLIError("No stored API key found. Run 'alice auth --api-key <KEY>' first.")
        api_key = existing.bedrock_secret_key
        if not config.quiet:
            print_status("Using previously stored API key.", config.quiet)
    elif not api_key and config.bedrock_secret_key:
        # Pick up the key from the global --api-key option
        api_key = config.bedrock_secret_key

    # Local --profile / --api-key override the global config for this command
    if profile:
        config = config.__class__(
            profile=profile,
            region=config.region,
            namespace=config.namespace,
            environment=config.environment,
            quiet=config.quiet,
            bedrock_access_key=config.bedrock_access_key,
            bedrock_secret_key=config.bedrock_secret_key,
        )
    if api_key:
        config = config.__class__(
            profile=profile,  # only keep profile if explicitly passed alongside --api-key
            region=config.region,
            namespace=config.namespace,
            environment=config.environment,
            quiet=config.quiet,
            bedrock_access_key=os.environ.get("BEDROCK_ACCESS_KEY"),
            bedrock_secret_key=api_key,
        )

    print_banner(personality.BANNER, config.quiet)

    if config.bedrock_secret_key and not config.profile:
        # API-key mode — store the key credentials
        identity = detect_identity_from_key(config.bedrock_access_key)
        print_status(personality.status_using_api_key(identity), config.quiet)

        # Try to fetch inference profile ARNs from the user's secret.
        # The Bedrock API key is service-specific and may not have Secrets
        # Manager permissions, so fall back to previously stored ARNs.
        secret_path = build_secret_path(config.namespace, config.environment, identity)
        arns = fetch_inference_profiles(
            config.bedrock_access_key or "",
            config.bedrock_secret_key or "",
            config.region,
            secret_path,
        )
        if not arns:
            # Preserve ARNs from a previous SSO auth run
            existing = load_stored_credentials()
            arns = existing.inference_profile_arns or {}

        path = save_stored_credentials(
            StoredCredentials(
                bedrock_access_key=config.bedrock_access_key,
                bedrock_secret_key=config.bedrock_secret_key,
                region=config.region,
                inference_profile_arns=arns or None,
            )
        )
        clear_all_sessions()  # ensure fresh sessions after new credentials
        print_success(f"Credentials saved to {path}", config.quiet)

        if claude:
            _set_claude_env(
                access_key=config.bedrock_access_key or "",
                secret_key=config.bedrock_secret_key or "",
                region=config.region,
                inference_profile_arns=arns or None,
            )
            _print_claude_models(config.quiet)
            _exec_claude(config.quiet)
        return

    # SSO profile mode
    result = detect_username(config.profile, config.region)

    print_status(personality.greeting(result.username), config.quiet)
    print_status(f"JHED: {result.username}", config.quiet)
    print_status(f"Session: {result.session_name}", config.quiet)
    print_status(f"ARN: {result.arn}", config.quiet)

    # Fetch and store the Bedrock credentials so future runs work without --profile
    secret_path = build_secret_path(config.namespace, config.environment, result.username)
    creds = fetch_credentials(config.profile, config.region, secret_path)

    path = save_stored_credentials(
        StoredCredentials(
            profile=config.profile,
            region=config.region,
            bedrock_access_key=creds.access_key,
            bedrock_secret_key=creds.secret_key,
            bedrock_bearer_token=creds.bearer_token,
            inference_profile_arns=creds.inference_profile_arns,
        )
    )
    clear_all_sessions()  # ensure fresh sessions after new credentials
    print_success(f"Credentials saved to {path}", config.quiet)

    if claude:
        # When using SSO profile, skip per-user AIP ARNs — they live in the
        # Bedrock service account and aren't accessible cross-account.
        # Claude Code will use system inference profile IDs instead.
        _set_claude_env(
            access_key=creds.access_key,
            secret_key=creds.secret_key,
            bearer_token=creds.bearer_token,
            region=creds.region,
            profile=config.profile,
            inference_profile_arns=creds.inference_profile_arns if not config.profile else None,
        )
        _print_claude_models(config.quiet)
        _exec_claude(config.quiet)


_CREDENTIAL_ENV_VARS: tuple[str, ...] = (
    "AWS_ACCESS_KEY_ID",
    "AWS_SECRET_ACCESS_KEY",
    "AWS_SESSION_TOKEN",
    "AWS_BEARER_TOKEN_BEDROCK",
    "BEDROCK_ACCESS_KEY",
    "BEDROCK_SECRET_KEY",
    "CLAUDE_CODE_USE_BEDROCK",
    "ANTHROPIC_MODEL",
    "ANTHROPIC_DEFAULT_OPUS_MODEL",
    "ANTHROPIC_DEFAULT_SONNET_MODEL",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL",
)


def _flush_credential_env() -> None:
    """Remove Alice/Bedrock credential env vars from the current process and
    print ``unset`` commands to stdout for use with ``eval $(alice auth --flush)``."""
    for var in _CREDENTIAL_ENV_VARS:
        os.environ.pop(var, None)
        click.echo(f"unset {var}")


def _set_claude_env(
    *,
    access_key: str,
    secret_key: str,
    region: str,
    bearer_token: str = "",
    profile: str | None = None,
    inference_profile_arns: dict[str, str] | None = None,
) -> None:
    """Set all environment variables Claude Code needs for Bedrock.

    Claude Code recognises three auth mechanisms (in preference order):
    - SSO profile: ``AWS_PROFILE`` (same account as inference profiles)
    - Bearer token: ``AWS_BEARER_TOKEN_BEDROCK`` (Bedrock API key)
    - IAM credentials: ``AWS_ACCESS_KEY_ID`` + ``AWS_SECRET_ACCESS_KEY``

    When *profile* is provided, SSO auth is preferred because the SSO role
    lives in the same account as the application inference profiles.

    When *inference_profile_arns* is provided (from the user's secret),
    the ``ANTHROPIC_DEFAULT_*_MODEL`` vars are resolved to the user's
    per-user application inference profile ARNs instead of generic model IDs.
    """
    from alice_cli.models import CLAUDE_DEFAULT_MODELS, DEFAULT_MODEL_ALIAS, resolve_model_id

    # Core Bedrock flag + region
    os.environ["CLAUDE_CODE_USE_BEDROCK"] = "1"
    os.environ["AWS_REGION"] = region

    # Auth — prefer SSO profile, then bearer token, then IAM credentials
    if profile:
        os.environ["AWS_PROFILE"] = profile
        # Clear ALL credential vars so Claude Code uses only the SSO profile
        for var in (
            "AWS_ACCESS_KEY_ID",
            "AWS_SECRET_ACCESS_KEY",
            "AWS_SESSION_TOKEN",
            "AWS_BEARER_TOKEN_BEDROCK",
            "BEDROCK_ACCESS_KEY",
            "BEDROCK_SECRET_KEY",
        ):
            os.environ.pop(var, None)
    else:
        # API key / bearer token mode — clear SSO profile so keys take priority
        os.environ.pop("AWS_PROFILE", None)
        if bearer_token:
            os.environ["AWS_BEARER_TOKEN_BEDROCK"] = bearer_token
        if access_key and secret_key:
            os.environ["AWS_ACCESS_KEY_ID"] = access_key
            os.environ["AWS_SECRET_ACCESS_KEY"] = secret_key
        os.environ["BEDROCK_ACCESS_KEY"] = access_key
        os.environ["BEDROCK_SECRET_KEY"] = secret_key

    # Model defaults — resolve through user's inference profile ARNs when available
    os.environ["ANTHROPIC_MODEL"] = resolve_model_id(DEFAULT_MODEL_ALIAS, inference_profile_arns)
    for env_var, (alias, _fallback) in CLAUDE_DEFAULT_MODELS.items():
        os.environ[env_var] = resolve_model_id(alias, inference_profile_arns)


def _print_claude_models(quiet: bool) -> None:
    """Display the resolved inference profile models before launching Claude."""
    from alice_cli.models import CLAUDE_DEFAULT_MODELS

    for env_var, (alias, _) in CLAUDE_DEFAULT_MODELS.items():
        value = os.environ.get(env_var, "")
        label = alias.replace("-", " ").title()
        print_status(f"{label}: {value}", quiet)


def _exec_claude(quiet: bool) -> None:
    """Verify claude is on PATH and exec into it."""
    if shutil.which("claude") is None:
        raise AliceCLIError(personality.error_claude_not_found())

    print_success(personality.SUCCESS_CLAUDE_LAUNCH, quiet)
    os.execvp("claude", ["claude"])
