import datetime
import ipaddress
from ipaddress import IPv4Network, IPv6Network
from typing import TYPE_CHECKING, Generic, TypeVar

import msgspec


class AccountingIPAddress(msgspec.Struct):
    exploded: str


class AccountingRecord(msgspec.Struct):
    username: str
    start_time: datetime.datetime = msgspec.field(name="startTime")
    framed_ip_address: AccountingIPAddress | None = msgspec.field(name="framedIpAddress", default=None)
    framed_ip_v6_address: AccountingIPAddress | None = msgspec.field(name="framedIpV6Address", default=None)


class GetAccountingHistoryResult(msgspec.Struct):
    data: list[AccountingRecord]


if TYPE_CHECKING:
    IPNetwork = IPv4Network | IPv6Network
else:
    from ipaddress import _BaseNetwork as IPNetwork  # type: ignore


class CIDR(msgspec.Struct):
    _raw_exploded: str = msgspec.field(name="raw_exploded")
    exploded: IPNetwork = ipaddress.ip_network("0.0.0.0/0")

    def __post_init__(self) -> None:
        self.exploded = ipaddress.ip_interface(self._raw_exploded).network


class Tenant(msgspec.Struct):
    name: str


class NetboxAsset(msgspec.Struct):
    tenant: list[Tenant]
    cidr: CIDR
    description: str


class QueryResult(msgspec.Struct):
    prefixes: list[NetboxAsset]
    ip_addresses: list[NetboxAsset]


T = TypeVar("T")


class Data(msgspec.Struct, Generic[T]):
    data: list[T]


class RawDescriptionDetail(msgspec.Struct):
    description: str


class RawTenantDetail(msgspec.Struct):
    detail: Data[Tenant]


class RawNetboxAsset(msgspec.Struct):
    cidr: CIDR
    detail: Data[RawDescriptionDetail]
    tenant: RawTenantDetail | None = None


class RawQueryResult(msgspec.Struct):
    prefixes: Data[RawNetboxAsset]
    ip_addresses: Data[RawNetboxAsset]

    def to_query_result(self) -> QueryResult:
        return QueryResult(
            prefixes=[
                NetboxAsset(
                    tenant=prefix.tenant.detail.data if (prefix.tenant is not None) else [],
                    cidr=prefix.cidr,
                    description=", ".join(
                        detail.description for detail in prefix.detail.data if detail.description.strip() != ""
                    ),
                )
                for prefix in self.prefixes.data
            ],
            ip_addresses=[
                NetboxAsset(
                    tenant=ip.tenant.detail.data if (ip.tenant is not None) else [],
                    cidr=ip.cidr,
                    description=", ".join(
                        [detail.description for detail in ip.detail.data if detail.description.strip() != ""]
                    ),
                )
                for ip in self.ip_addresses.data
            ],
        )
