from __future__ import annotations

import os
import sys
from typing import Any, Callable, Dict, Mapping, Optional

from autotouch_cli.exceptions import AutotouchInputError

__all__ = [
    "mask_api_key",
    "mask_secret",
    "looks_like_api_key",
    "looks_like_jwt",
    "env_user_token",
    "env_api_key",
    "resolve_token",
    "resolve_user_session_token",
    "auth_headers",
    "print_missing_developer_auth_help",
    "print_missing_user_session_help",
]


def mask_api_key(value: Optional[str]) -> str:
    token = str(value or "").strip()
    if not token:
        return ""
    if len(token) <= 8:
        return "*" * len(token)
    return f"{token[:6]}...{token[-4:]}"


def mask_secret(value: Optional[str]) -> str:
    token = str(value or "").strip()
    if not token:
        return ""
    if len(token) <= 10:
        return "*" * len(token)
    return f"{token[:4]}...{token[-4:]}"


def looks_like_api_key(value: Optional[str]) -> bool:
    token = str(value or "").strip()
    return token.startswith("stk_") and "." in token


def looks_like_jwt(value: Optional[str]) -> bool:
    token = str(value or "").strip()
    return bool(token) and not looks_like_api_key(token) and token.count(".") == 2


def env_user_token(env: Optional[Mapping[str, str]] = None) -> Optional[str]:
    environment = env or os.environ
    for key in ("SMARTTABLE_TOKEN", "AUTOTOUCH_TOKEN"):
        value = str(environment.get(key) or "").strip()
        if value and not looks_like_api_key(value):
            return value
    return None


def env_api_key(env: Optional[Mapping[str, str]] = None) -> Optional[str]:
    environment = env or os.environ
    direct = str(environment.get("AUTOTOUCH_API_KEY") or "").strip()
    if direct:
        return direct
    for key in ("SMARTTABLE_TOKEN", "AUTOTOUCH_TOKEN"):
        value = str(environment.get(key) or "").strip()
        if looks_like_api_key(value):
            return value
    return None


def print_missing_developer_auth_help() -> None:
    print(
        "ERROR: missing token. Pass --token, run `autotouch auth set-key --api-key stk_...`, "
        "or bootstrap a fresh account with `autotouch auth bootstrap ... --save-key`.",
        file=sys.stderr,
    )
    print(
        "Collaborative workflow rule: if you are working with a user, ask whether they want to use an existing "
        "API key/account before you bootstrap a fresh one.",
        file=sys.stderr,
    )
    print(
        "Fresh-account / isolated audit pattern: "
        "`export AUTOTOUCH_CONFIG_PATH=/tmp/autotouch-audit.json && "
        "read -s AUTOTOUCH_BOOTSTRAP_PASSWORD && export AUTOTOUCH_BOOTSTRAP_PASSWORD && "
        "autotouch auth bootstrap --first-name Ada --last-name Lovelace "
        "--email ada+audit@example.com --password \"$AUTOTOUCH_BOOTSTRAP_PASSWORD\" "
        "--organization-name \"Audit Org\" --save-key`",
        file=sys.stderr,
    )


def print_missing_user_session_help() -> None:
    print(
        "ERROR: this command requires a saved user session. Run `autotouch auth login --email you@example.com` "
        "or bootstrap a fresh account with `autotouch auth bootstrap ... --save-key`.",
        file=sys.stderr,
    )
    print(
        "Collaborative workflow rule: if you are working with a user, ask whether they want to use an existing "
        "account/session before you bootstrap a fresh one.",
        file=sys.stderr,
    )
    print(
        "Fresh-account / isolated audit pattern: "
        "`export AUTOTOUCH_CONFIG_PATH=/tmp/autotouch-audit.json && "
        "read -s AUTOTOUCH_BOOTSTRAP_PASSWORD && export AUTOTOUCH_BOOTSTRAP_PASSWORD && "
        "autotouch auth bootstrap --first-name Ada --last-name Lovelace "
        "--email ada+audit@example.com --password \"$AUTOTOUCH_BOOTSTRAP_PASSWORD\" "
        "--organization-name \"Audit Org\" --save-key`",
        file=sys.stderr,
    )


def resolve_token(
    explicit_token: Optional[str],
    *,
    required: bool = True,
    load_config_fn: Callable[[], Dict[str, Any]],
    env_user_token_fn: Callable[[], Optional[str]] = env_user_token,
    env_api_key_fn: Callable[[], Optional[str]] = env_api_key,
    print_missing_help_fn: Callable[[], None] = print_missing_developer_auth_help,
) -> Optional[str]:
    token = str(explicit_token or "").strip()
    if not token:
        cfg = load_config_fn()
        token = (
            str(cfg.get("auth_token") or "").strip()
            or str(cfg.get("api_key") or "").strip()
            or str(env_user_token_fn() or "").strip()
            or str(env_api_key_fn() or "").strip()
        )
    if not token:
        if required:
            print_missing_help_fn()
            raise AutotouchInputError("missing token")
        return None
    return token


def resolve_user_session_token(
    explicit_token: Optional[str],
    *,
    required: bool = True,
    load_config_fn: Callable[[], Dict[str, Any]],
    looks_like_api_key_fn: Callable[[Optional[str]], bool] = looks_like_api_key,
    env_user_token_fn: Callable[[], Optional[str]] = env_user_token,
    print_missing_help_fn: Callable[[], None] = print_missing_user_session_help,
) -> Optional[str]:
    token = str(explicit_token or "").strip()
    if token and looks_like_api_key_fn(token):
        raise AutotouchInputError(
            "this command requires a user session token, not a developer API key. Run `autotouch auth login`."
        )

    if not token:
        cfg = load_config_fn()
        token = str(cfg.get("auth_token") or "").strip() or str(env_user_token_fn() or "").strip()

    if not token:
        if required:
            print_missing_help_fn()
            raise AutotouchInputError("missing user session token")
        return None
    return token


def auth_headers(token: Optional[str], *, use_x_api_key: bool = False) -> Dict[str, str]:
    if not token:
        return {}
    if use_x_api_key:
        return {"X-API-Key": token}
    return {"Authorization": f"Bearer {token}"}
