from typing import Any, Dict, List, Literal, Optional

import httpx
from radicli import Arg
from wasabi import msg

from .. import about
from ..auth import AuthState
from ..cli import cli
from ..config import SavedSettings, global_config_dir
from ..errors import CLIError, ProdigyTeamsError
from ..messages import Messages
from ..ui import print_as_json, print_mutation_result
from ._state import get_auth_state, get_root_cfg


@cli.command(
    "login",
    no_cluster=Arg("--no-cluster", help=Messages.no_cluster),
    no_browser=Arg("--no-browser", help=Messages.no_browser),
    as_json=Arg("--json", help=Messages.as_json),
)
def login(
    no_cluster: bool = False, no_browser: bool = False, as_json: bool = False
) -> None:
    """
    Log in to your Prodigy Teams account. You normally don't need to call this
    manually. It will automatically authenticate when needed.
    """
    auth = get_auth_state()
    auth.no_browser = no_browser
    auth._ensure_readable_secrets()
    auth.get_api_token(force_refresh=True)
    if not no_cluster:
        try:
            auth.get_broker_token(force_refresh=True)
        except ProdigyTeamsError as e:
            err = Messages.E116.format(command=f"{cli.prog} login --no-cluster")
            raise CLIError(err, e)
    print_mutation_result(
        {"status": "ok"},
        Messages.T012,
        as_json=as_json,
    )


@cli.command("info", field=Arg(help=Messages.select_field))
def info(field: Optional[Literal["config-dir", "code", "defaults"]] = None) -> Any:
    """Print information about the CLI"""
    settings = SavedSettings.from_file(get_root_cfg().saved_settings_path)
    info = {
        "config-dir": str(global_config_dir().absolute()),
        "code": __file__,
        "defaults": settings.to_json(),
    }
    if field:
        print(info[field])
        return info[field]
    else:
        print_as_json(info)
        return info


@cli.command(
    "get-auth-token",
    token_type=Arg(help="The token type"),
)
def get_auth_token(
    token_type: Optional[Literal["api", "cluster", "id", "ci"]] = None,
) -> AuthState:
    """
    Return an auth token for the current user
    """
    auth = get_auth_state()
    if token_type == "api":
        print(auth.get_api_token().access_token)
    elif token_type == "cluster":
        print(auth.get_broker_token().access_token)
    elif token_type == "id":
        print(auth.get_id_token())
    elif token_type == "ci":
        # this uses a dev endpoint because we don't want to give CI
        # a full access token. Once we can create and revoke limited
        # tokens, remove this.
        response = auth.client._sync_client.post(
            "/api/v1/dev/create-ci-token",
            json={},
        )
        response.raise_for_status()
        print(response.json()["access_token"])
    else:
        raise CLIError(Messages.E117.format(token_type=token_type))
    return auth


def _fetch_version(client: httpx.Client, path: str) -> Optional[Dict[str, str]]:
    """Fetch version info from a service endpoint, returning None on failure."""
    try:
        response = client.get(path)
        response.raise_for_status()
        return response.json()
    except Exception:
        return None


def _print_version_table(rows: List[Dict[str, str]]) -> None:
    """Print version info for all services in a readable format."""
    for row in rows:
        msg.divider(row["service"])
        for key in (
            "package_version",
            "repo_sha",
            "service_sha",
            "container_sha",
            "build_date",
        ):
            value = row.get(key, "")
            if value:
                print(f"  {key}: {value}")
            else:
                print(f"  {key}: -")


@cli.command(
    "version",
    as_json=Arg("--json", help=Messages.as_json),
)
def version(as_json: bool = False) -> None:
    """Show version info for the CLI and running services."""
    auth = get_auth_state()
    rows: List[Dict[str, str]] = []

    pam_version = _fetch_version(auth.client._sync_client, "/api/v1/version")
    if pam_version:
        rows.append(pam_version)
    elif not as_json:
        msg.warn("Could not reach PAM service")

    try:
        broker_version = _fetch_version(
            auth.broker_client._sync_client, "/api/v1/version"
        )
        if broker_version:
            rows.append(broker_version)
        elif not as_json:
            msg.warn("Could not reach Broker service")
    except ProdigyTeamsError:
        if not as_json:
            msg.warn("Could not reach Broker service (no broker configured)")

    if as_json:
        import json

        result = {"cli": {"package_version": about.__version__}, "services": rows}
        print(json.dumps(result, indent=2, default=str))
    else:
        msg.divider("cli")
        print(f"  package_version: {about.__version__}")
        print()
        _print_version_table(rows)
