"""Provide a model for the Z-Wave JS node's health checks and power tests."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import TypedDict

from ...const import PowerLevel


class LifelineHealthCheckResultDataType(TypedDict, total=False):
    """Represent a lifeline health check result data dict type."""

    # https://github.com/zwave-js/node-zwave-js/blob/master/packages/zwave-js/src/lib/node/Types.ts#L171
    latency: int  # required
    numNeighbors: int  # required
    failedPingsNode: int  # required
    rating: int  # required
    routeChanges: int
    minPowerlevel: int
    failedPingsController: int
    snrMargin: int


class LifelineHealthCheckSummaryDataType(TypedDict):
    """Represent a lifeline health check summary data dict type."""

    # https://github.com/zwave-js/node-zwave-js/blob/master/packages/zwave-js/src/lib/node/_Types.ts#L254
    results: list[LifelineHealthCheckResultDataType]
    rating: int


@dataclass(frozen=True)
class LifelineHealthCheckResult:
    """Represent a lifeline health check result."""

    data: LifelineHealthCheckResultDataType = field(repr=False)
    latency: int = field(init=False)
    num_neighbors: int = field(init=False)
    failed_pings_node: int = field(init=False)
    rating: int = field(init=False)
    route_changes: int | None = field(init=False)
    min_power_level: PowerLevel | None = field(init=False, default=None)
    failed_pings_controller: int | None = field(init=False)
    snr_margin: int | None = field(init=False)

    def __post_init__(self) -> None:
        """Post initialize."""
        object.__setattr__(self, "latency", self.data["latency"])
        object.__setattr__(self, "num_neighbors", self.data["numNeighbors"])
        object.__setattr__(self, "failed_pings_node", self.data["failedPingsNode"])
        object.__setattr__(self, "rating", self.data["rating"])
        object.__setattr__(self, "route_changes", self.data.get("routeChanges"))
        if (min_power_level := self.data.get("minPowerlevel")) is not None:
            object.__setattr__(self, "min_power_level", PowerLevel(min_power_level))
        object.__setattr__(
            self, "failed_pings_controller", self.data.get("failedPingsController")
        )
        object.__setattr__(self, "snr_margin", self.data.get("snrMargin"))


@dataclass(frozen=True)
class LifelineHealthCheckSummary:
    """Represent a lifeline health check summary update."""

    data: LifelineHealthCheckSummaryDataType = field(repr=False)
    rating: int = field(init=False)
    results: list[LifelineHealthCheckResult] = field(init=False)

    def __post_init__(self) -> None:
        """Post initialize."""
        object.__setattr__(self, "rating", self.data["rating"])
        object.__setattr__(
            self,
            "results",
            [LifelineHealthCheckResult(r) for r in self.data.get("results", [])],
        )


class RouteHealthCheckResultDataType(TypedDict, total=False):
    """Represent a route health check result data dict type."""

    # https://github.com/zwave-js/node-zwave-js/blob/master/packages/zwave-js/src/lib/node/_Types.ts#L285
    numNeighbors: int  # required
    rating: int  # required
    failedPingsToTarget: int
    failedPingsToSource: int
    minPowerlevelSource: int
    minPowerlevelTarget: int


class RouteHealthCheckSummaryDataType(TypedDict):
    """Represent a route health check summary data dict type."""

    # https://github.com/zwave-js/node-zwave-js/blob/master/packages/zwave-js/src/lib/node/_Types.ts#L317
    results: list[RouteHealthCheckResultDataType]
    rating: int


@dataclass(frozen=True)
class RouteHealthCheckResult:
    """Represent a route health check result."""

    data: RouteHealthCheckResultDataType = field(repr=False)
    num_neighbors: int = field(init=False)
    rating: int = field(init=False)
    failed_pings_to_target: int | None = field(init=False)
    failed_pings_to_source: int | None = field(init=False)
    min_power_level_source: PowerLevel | None = field(init=False, default=None)
    min_power_level_target: PowerLevel | None = field(init=False, default=None)

    def __post_init__(self) -> None:
        """Post initialize."""
        object.__setattr__(self, "num_neighbors", self.data["numNeighbors"])
        object.__setattr__(self, "rating", self.data["rating"])
        object.__setattr__(
            self, "failed_pings_to_target", self.data.get("failedPingsToTarget")
        )
        object.__setattr__(
            self, "failed_pings_to_source", self.data.get("failedPingsToSource")
        )
        if (min_power_level_source := self.data.get("minPowerlevelSource")) is not None:
            object.__setattr__(
                self, "min_power_level_source", PowerLevel(min_power_level_source)
            )
        if (min_power_level_target := self.data.get("minPowerlevelTarget")) is not None:
            object.__setattr__(
                self, "min_power_level_target", PowerLevel(min_power_level_target)
            )


@dataclass(frozen=True)
class RouteHealthCheckSummary:
    """Represent a route health check summary update."""

    data: RouteHealthCheckSummaryDataType = field(repr=False)
    rating: int = field(init=False)
    results: list[RouteHealthCheckResult] = field(init=False)

    def __post_init__(self) -> None:
        """Post initialize."""
        object.__setattr__(self, "rating", self.data["rating"])
        object.__setattr__(
            self,
            "results",
            [RouteHealthCheckResult(r) for r in self.data.get("results", [])],
        )


@dataclass(frozen=True)
class TestPowerLevelProgress:
    """Class to represent a test power level progress update."""

    acknowledged: int
    total: int


@dataclass(frozen=True)
class CheckHealthProgress:
    """Represent a check lifeline/route health progress update."""

    rounds: int
    total_rounds: int
    last_rating: int
    last_result: LifelineHealthCheckResult | RouteHealthCheckResult
