from __future__ import annotations

import asyncio
from contextlib import suppress
from ipaddress import ip_address
import sys

from icmplib import NameLookupError, async_resolve

if sys.version_info >= (3, 11):
    from asyncio import timeout as async_timeout
else:
    from async_timeout import timeout as async_timeout

RESOLVE_TIMEOUT = 3.0


async def _async_resolve_wrapper(hostname: str) -> list[str] | Exception:
    """Wrap the icmplib async_resolve function."""
    with suppress(ValueError):
        return [str(ip_address(hostname))]
    try:
        async with async_timeout(RESOLVE_TIMEOUT):
            return await async_resolve(hostname)
    except (asyncio.TimeoutError, NameLookupError, UnicodeError) as ex:
        return ex


class DNSCache:
    """DNS cache for the dashboard."""

    def __init__(self, ttl: int | None = 120) -> None:
        """Initialize the DNSCache."""
        self._cache: dict[str, tuple[float, list[str] | Exception]] = {}
        self._ttl = ttl

    async def async_resolve(
        self, hostname: str, now_monotonic: float
    ) -> list[str] | Exception:
        """Resolve a hostname to a list of IP address."""
        if expire_time_addresses := self._cache.get(hostname):
            expire_time, addresses = expire_time_addresses
            if expire_time > now_monotonic:
                return addresses

        expires = now_monotonic + self._ttl
        addresses = await _async_resolve_wrapper(hostname)
        self._cache[hostname] = (expires, addresses)
        return addresses
