from importlib.metadata import PackageNotFoundError, version

from pydantic import BaseModel

from cli.infrastructure.command import async_command, authenticated_client
from cli.infrastructure.output import OutputService
from cli.infrastructure.renderers.entity_renderer import FieldConfig


class PingOutput(BaseModel):
    email: str
    name: str
    user_id: str
    organization: str
    organization_id: str | None = None
    system_admin: str
    api_host: str
    api_key: str
    scopes: str
    http_status: str
    http_version: str
    tls_version: str
    sdk_version: str
    cli_version: str
    types_version: str
    release: str
    build_id: str
    build_age: str
    instance: str
    dns: str
    connect: str
    tls: str
    ttfb: str
    wait: str
    transfer: str
    total: str


def _mask_api_key_prefix(api_key: str) -> str:
    normalized = api_key.strip()
    visible_chars = min(20, max(4, len(normalized) - 4))
    return f"{normalized[:visible_chars]}..."


def _format_build_age(build_age: str | None, build_age_seconds: int | None) -> str:
    if build_age:
        return build_age
    if build_age_seconds is None:
        return "n/a"
    if build_age_seconds < 60:
        return "fresh"
    minutes = build_age_seconds // 60
    if minutes < 60:
        unit = "minute" if minutes == 1 else "minutes"
        return f"{minutes} {unit}"
    hours = build_age_seconds // 3600
    if hours < 48:
        unit = "hour" if hours == 1 else "hours"
        return f"{hours} {unit}"
    days = build_age_seconds // 86400
    unit = "day" if days == 1 else "days"
    return f"{days} {unit}"


def _format_ms(value: float | None) -> str:
    if value is None:
        return "n/a"
    return f"{value:.1f} ms"


def _installed_package_version(package_name: str) -> str:
    try:
        installed_version = version(package_name)
    except PackageNotFoundError:
        return "n/a"
    return installed_version or "n/a"


def ping_command() -> None:
    _execute_ping()


@async_command
async def _execute_ping() -> None:
    output = OutputService.get()

    async with authenticated_client() as (config, client):
        ping = await client.get_ping()

    api_key_prefix = ping.api_key_prefix or _mask_api_key_prefix(config.api_key)

    rendered = PingOutput(
        email=ping.email,
        name=ping.name,
        user_id=str(ping.user_id),
        organization=str(ping.organization_name or ping.organization_id or "personal"),
        organization_id=(str(ping.organization_id) if ping.organization_id else None),
        system_admin="yes" if ping.is_system_admin else "no",
        api_host=ping.api_host or "n/a",
        api_key=api_key_prefix,
        scopes=", ".join(scope.value for scope in ping.scopes),
        http_status=str(ping.http_status) if ping.http_status is not None else "n/a",
        http_version=ping.http_version or "n/a",
        tls_version=ping.tls_version or "n/a",
        sdk_version=ping.sdk_version or "n/a",
        cli_version=_installed_package_version("alloy-runtime-cli"),
        types_version=ping.types_version or "n/a",
        release=ping.release_version or "n/a",
        build_id=ping.build_id or "n/a",
        build_age=_format_build_age(ping.build_age, ping.build_age_seconds),
        instance=ping.instance_label or "n/a",
        dns=_format_ms(ping.dns_ms),
        connect=_format_ms(ping.connect_ms),
        tls=_format_ms(ping.tls_ms),
        ttfb=_format_ms(ping.ttfb_ms),
        wait=_format_ms(ping.wait_ms),
        transfer=_format_ms(ping.transfer_ms),
        total=_format_ms(ping.total_ms),
    )

    output.entity(
        rendered,
        "Ping",
        [
            FieldConfig("email", "Email"),
            FieldConfig("name", "Name"),
            FieldConfig("user_id", "User ID"),
            FieldConfig("organization", "Organization"),
            FieldConfig("organization_id", "Organization ID"),
            FieldConfig("system_admin", "System Admin"),
            FieldConfig("api_host", "API Host"),
            FieldConfig("api_key", "API Key"),
            FieldConfig("scopes", "Scopes"),
            FieldConfig("http_status", "HTTP Status"),
            FieldConfig("http_version", "HTTP Version"),
            FieldConfig("tls_version", "TLS Version"),
            FieldConfig("sdk_version", "SDK Version"),
            FieldConfig("cli_version", "CLI Version"),
            FieldConfig("types_version", "Types Version"),
            FieldConfig("release", "Release"),
            FieldConfig("build_id", "Build ID"),
            FieldConfig("build_age", "Build Age"),
            FieldConfig("instance", "Instance"),
            FieldConfig("dns", "DNS"),
            FieldConfig("connect", "Connect"),
            FieldConfig("tls", "TLS"),
            FieldConfig("ttfb", "TTFB"),
            FieldConfig("wait", "Wait"),
            FieldConfig("transfer", "Transfer"),
            FieldConfig("total", "Total"),
        ],
        raw_payload=ping,
    )
