from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum


@dataclass
class ProbeResult:
    host: str
    label: str
    sent: int
    lost: int
    rtts: list[float] = field(default_factory=list)

    @property
    def loss_pct(self) -> float:
        return (self.lost / self.sent) * 100 if self.sent else 0.0

    @property
    def avg(self) -> float | None:
        return sum(self.rtts) / len(self.rtts) if self.rtts else None

    @property
    def min(self) -> float | None:
        return min(self.rtts) if self.rtts else None

    @property
    def max(self) -> float | None:
        return max(self.rtts) if self.rtts else None

    @property
    def jitter(self) -> float | None:
        if len(self.rtts) < 2:
            return 0.0 if self.rtts else None
        mean = self.avg
        assert mean is not None
        variance = sum((r - mean) ** 2 for r in self.rtts) / (len(self.rtts) - 1)
        return variance**0.5

    @property
    def reachable(self) -> bool:
        return len(self.rtts) > 0


class Severity(Enum):
    WARNING = "warning"
    ERROR = "error"


@dataclass
class Issue:
    severity: Severity
    kind: str
    source: str  # where: "LAN", "ISP", "network", etc.
    name: str  # short problem name: "packet loss to gateway"
    cause: str = ""  # probable cause: "wifi/router issue"


@dataclass
class MetricAlert:
    """Flags a specific metric on a probe as out of range."""

    host: str
    metric: str  # "rtt", "loss", "jitter"
    severity: Severity


@dataclass
class DnsResult:
    """DNS resolution timing for one resolver."""

    server: str  # IP address
    label: str  # "Google DNS", "192.168.1.1", etc.
    cached_ms: float | None  # cached lookup time in ms
    uncached_ms: float | None  # uncached lookup time in ms (median of 3)
    is_configured: bool = False  # True if this is the system's configured resolver


@dataclass
class CheckResult:
    timestamp: datetime
    gateway: ProbeResult | None
    internet: list[ProbeResult]
    dns: list[DnsResult]
    issues: list[Issue]
    alerts: list[MetricAlert] = field(default_factory=list)

    @property
    def healthy(self) -> bool:
        return len(self.issues) == 0


@dataclass
class IssueRecord:
    kind: str
    source: str
    name: str
    cause: str
    count: int
    last_seen: datetime
    active: bool

    @staticmethod
    def from_results(results: list["CheckResult"]) -> list["IssueRecord"]:
        """Build a grouped history of all issues seen, sorted by most recent."""
        records: dict[str, IssueRecord] = {}
        active_kinds = set()
        if results:
            active_kinds = {i.kind for i in results[-1].issues}

        for result in results:
            for issue in result.issues:
                if issue.kind not in records:
                    records[issue.kind] = IssueRecord(
                        kind=issue.kind,
                        source=issue.source,
                        name=issue.name,
                        cause=issue.cause,
                        count=0,
                        last_seen=result.timestamp,
                        active=False,
                    )
                rec = records[issue.kind]
                rec.count += 1
                rec.last_seen = result.timestamp
                rec.source = issue.source
                rec.name = issue.name
                rec.cause = issue.cause

        for kind in active_kinds:
            if kind in records:
                records[kind].active = True

        return sorted(records.values(), key=lambda r: r.last_seen, reverse=True)
