"""Resolve public/private IPs for server alert Telegram messages."""

from __future__ import annotations

import ipaddress
import json
import logging
from typing import Any

logger = logging.getLogger("nexus.server_alert_ips")


def is_public_ip(value: str | None) -> bool:
    text = (value or "").strip()
    if not text:
        return False
    try:
        addr = ipaddress.ip_address(text)
    except ValueError:
        return False
    return not (
        addr.is_private
        or addr.is_loopback
        or addr.is_link_local
        or addr.is_reserved
    )


def is_private_ip(value: str | None) -> bool:
    text = (value or "").strip()
    if not text:
        return False
    try:
        addr = ipaddress.ip_address(text)
    except ValueError:
        return False
    return addr.is_private or addr.is_loopback or addr.is_link_local


def classify_domain_ip(domain: str | None) -> tuple[str | None, str | None]:
    """Return (public_ip, private_ip) when domain is a literal IP."""
    text = (domain or "").strip()
    if not text:
        return None, None
    try:
        ipaddress.ip_address(text)
    except ValueError:
        return None, None
    if is_public_ip(text):
        return text, None
    if is_private_ip(text):
        return None, text
    return None, None


def merge_alert_ips(
    *,
    domain: str | None = None,
    extra_attrs: dict[str, Any] | None = None,
    system_info: dict[str, Any] | None = None,
) -> tuple[str | None, str | None]:
    """Pick public/private IPs for Telegram display.

    外网 SSOT：servers.domain 为公网 IP 时优先（运维登记连接地址，如 120.79.11.13）。
    内网：Agent 上报 private_ip 优先，否则 domain 为私网 IP 时使用。
    Agent ipify 仅作 domain/extra 均无公网时的兜底。
    """
    public_ip: str | None = None
    private_ip: str | None = None

    info = system_info if isinstance(system_info, dict) else {}
    attrs = extra_attrs if isinstance(extra_attrs, dict) else {}
    dom_pub, dom_priv = classify_domain_ip(domain)

    if dom_pub:
        public_ip = dom_pub

    if not public_ip:
        for key in ("public_ip", "wan_ip", "external_ip"):
            raw = attrs.get(key)
            if raw and is_public_ip(str(raw)):
                public_ip = str(raw).strip()
                break

    if not public_ip:
        for key in ("public_ip", "wan_ip", "external_ip"):
            raw = info.get(key)
            if raw and is_public_ip(str(raw)):
                public_ip = str(raw).strip()
                break

    for key in ("private_ip", "lan_ip", "internal_ip"):
        raw = info.get(key)
        if raw and is_private_ip(str(raw)):
            private_ip = str(raw).strip()
            break

    if not private_ip and dom_priv:
        private_ip = dom_priv

    return public_ip, private_ip


def format_alert_ip_line(public_ip: str | None, private_ip: str | None) -> str:
    pub = (public_ip or "").strip() or "—"
    priv = (private_ip or "").strip() or "—"
    return f"IP地址：{pub}(外) {priv}(内)"


async def resolve_alert_ips_for_server(server_id: int) -> tuple[str | None, str | None]:
    """Load server row + Redis heartbeat system_info for Telegram IP lines."""
    from server.infrastructure.database.server_repo import ServerRepositoryImpl
    from server.infrastructure.database.session import AsyncSessionLocal
    from server.infrastructure.redis.client import get_redis

    domain: str | None = None
    extra_attrs: dict[str, Any] | None = None
    system_info: dict[str, Any] | None = None

    try:
        async with AsyncSessionLocal() as session:
            server = await ServerRepositoryImpl(session).get_by_id(server_id)
            if server:
                domain = server.domain
                extra_attrs = server.extra_attrs if isinstance(server.extra_attrs, dict) else None
    except Exception as e:
        logger.warning("resolve_alert_ips DB read failed server_id=%s: %s", server_id, e)

    try:
        redis = get_redis()
        raw = await redis.hget(f"heartbeat:{server_id}", "system_info")
        if raw:
            parsed = json.loads(raw)
            if isinstance(parsed, dict):
                system_info = parsed
    except Exception as e:
        logger.debug("resolve_alert_ips Redis read failed server_id=%s: %s", server_id, e)

    return merge_alert_ips(domain=domain, extra_attrs=extra_attrs, system_info=system_info)
