"""DNS name resolution monitoring widget.

This widget subscribes to the 'dns_resolution' topic to receive
real-time DNS resolution results from the backend.
"""

from __future__ import annotations

from typing import Any, Literal, Protocol

from textual.app import ComposeResult
from textual.dom import NoMatches
from textual.message import Message
from textual.reactive import var
from textual.widget import Widget
from textual.widgets import DataTable

from flux_networking_shared.tui.models.network import ResolvedHostname

StateTypes = Literal["Unknown", "Pass", "Degraded", "Fail"]

COLUMNS: list[dict[str, str | int]] = [
    {"label": "Name", "key": "name"},
    {"label": "Address", "key": "address"},
    {"label": "Interface", "key": "interface"},
    {"label": "Family", "key": "family"},
    {"label": "Canonical Name", "key": "canonical"},
    {"label": "   Elapsed", "key": "elapsed"},
    {"label": "Error", "key": "error"},
]

COLUMN_KEYS = ["name", "address", "interface", "family", "canonical", "elapsed", "error"]


class NetworkClient(Protocol):
    """Protocol for network RPC client."""

    async def subscribe(self, topics: list[str]) -> dict[str, Any]: ...

    async def unsubscribe(self, topics: list[str]) -> dict[str, Any]: ...


class NameResolution(Widget):
    """Displays DNS name resolution status.

    Subscribes to the 'dns_resolution' topic to receive real-time
    resolution results from the backend.
    """

    class ResolutionStateChanged(Message):
        """Posted when resolution state changes."""

        def __init__(self, state: StateTypes) -> None:
            super().__init__()
            self.state = state

    BORDER_TITLE = "Name Resolution"

    state: var[StateTypes] = var("Unknown")

    def __init__(self, client: NetworkClient | None = None) -> None:
        super().__init__()
        self.client = client
        self.resolutions: list[ResolvedHostname] = []
        self._monitoring = False

    def compose(self) -> ComposeResult:
        dt: DataTable = DataTable(show_cursor=False)
        for column in COLUMNS:
            dt.add_column(**column)
        yield dt

    async def on_mount(self) -> None:
        """Subscribe to dns_resolution topic on mount."""
        if self.client:
            try:
                await self.client.subscribe(["dns_resolution"])
                self._monitoring = True
            except Exception:
                pass

    async def on_unmount(self) -> None:
        """Unsubscribe from dns_resolution topic on unmount."""
        if self.client and self._monitoring:
            try:
                await self.client.unsubscribe(["dns_resolution"])
            except Exception:
                pass

    def handle_resolution_event(self, data: dict[str, Any]) -> None:
        """Handle a DNS resolution update event from the backend."""
        hostname = ResolvedHostname.from_dict(data)

        existing_idx = next(
            (i for i, r in enumerate(self.resolutions) if r.name == hostname.name),
            None,
        )

        if existing_idx is not None:
            self.resolutions[existing_idx] = hostname
            self._update_row(hostname)
        else:
            self.resolutions.append(hostname)
            self._add_row(hostname)

        self._evaluate_state()

    def update_from_results(self, results: list[dict[str, Any]]) -> None:
        """Update from a batch of resolution results."""
        self.resolutions = [ResolvedHostname.from_dict(r) for r in results]

        try:
            table = self.query_one(DataTable)
        except NoMatches:
            return

        table.clear()

        for r in self.resolutions:
            table.add_row(*r.data_row, key=r.name)

        self._evaluate_state()

    def _add_row(self, hostname: ResolvedHostname) -> None:
        """Add a new row to the table."""
        try:
            table = self.query_one(DataTable)
        except NoMatches:
            return

        table.add_row(*hostname.data_row, key=hostname.name)

    def _update_row(self, hostname: ResolvedHostname) -> None:
        """Update an existing row's cells."""
        try:
            table = self.query_one(DataTable)
        except NoMatches:
            return

        values = hostname.data_row
        for key, value in zip(COLUMN_KEYS, values, strict=True):
            table.update_cell(row_key=hostname.name, column_key=key, value=value or "")

    def _evaluate_state(self) -> None:
        """Compute overall resolution state."""
        if not self.resolutions:
            self.state = "Unknown"
            return

        resolved = [r.resolved for r in self.resolutions]

        if all(resolved):
            self.state = "Pass"
        elif any(resolved):
            self.state = "Degraded"
        else:
            self.state = "Fail"

    def watch_state(self, old: StateTypes, new: StateTypes) -> None:
        """Post message when state changes."""
        if old == new:
            return

        self.post_message(self.ResolutionStateChanged(new))
