from __future__ import annotations
from dataclasses import dataclass, fields
from typing import ClassVar
from ipaddress import IPv4Address

from socket import inet_ntoa, AF_INET6, AF_INET


@dataclass
class ResolvedHostname:
    family_map: ClassVar[dict[int, str]] = {AF_INET: "IPv4", AF_INET6: "IPv6"}
    name: str
    address: IPv4Address | None = None
    interface_name: str | None = None
    family: str | None = None
    canonical_name: str | None = None
    elapsed: str | None = None
    error: str | None = None

    @classmethod
    def from_dbus(
        cls,
        name: str,
        data: tuple[int, int, bytes],
        canonical: str,
        links: dict[int, str],
        elapsed: float,
    ) -> ResolvedHostname:
        ifindex, family, address_bytes = data

        address = IPv4Address(inet_ntoa(address_bytes))
        formatted_elapsed = str.rjust(f"{elapsed:.2f} ms", 10)

        return cls(
            name,
            address,
            links[ifindex],
            ResolvedHostname.family_map[family],
            canonical,
            formatted_elapsed,
            "    -",
        )

    @classmethod
    def from_error(
        cls, name: str, elapsed: float, error: str
    ) -> ResolvedHostname:
        formatted_elapsed = str.rjust(f"{elapsed:.2f} ms", 10)

        return cls(name, elapsed=formatted_elapsed, error=error)

    @property
    def data_column(self) -> list[str]:
        return [field.name for field in fields(self)]

    @property
    def data_row(self) -> list[str | int]:
        return [getattr(self, field.name) for field in fields(self)]

    @property
    def resolved(self) -> bool:
        return bool(self.address)


@dataclass
class ResolvedDnsServer:
    family_map: ClassVar[dict[int, str]] = {AF_INET: "IPv4", AF_INET6: "IPv6"}
    address: IPv4Address
    interface_name: str
    family: str

    @classmethod
    def from_dbus(
        cls, data: tuple[int, int, bytes], links: dict[int, str]
    ) -> ResolvedDnsServer:
        ifindex, family, address_bytes = data

        address = IPv4Address(inet_ntoa(address_bytes))

        return cls(
            address, links[ifindex], ResolvedDnsServer.family_map[family]
        )

    def __hash__(self) -> int:
        return (
            hash(self.address) + hash(self.interface_name) + hash(self.family)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, ResolvedDnsServer):
            return False

        return (
            self.address == other.address
            and self.interface_name == other.interface_name
            and self.family == other.family
        )

    def __str__(self) -> str:
        return str(self.address)

    @property
    def data_column(self) -> list[str]:
        return [field.name for field in fields(self)]

    @property
    def data_row(self) -> list[str | int]:
        return [getattr(self, field.name) for field in fields(self)]
