"""Network interface display widget."""

from __future__ import annotations

from contextlib import contextmanager
from ipaddress import IPv4Interface
from typing import TYPE_CHECKING, Generator, Literal

from textual.app import ComposeResult
from textual.containers import Vertical
from textual.dom import NoMatches
from textual.events import Click
from textual.message import Message
from textual.reactive import var
from textual.widget import Widget
from textual.widgets import Label

from flux_networking_shared.tui.widgets.field_set import FieldSet
from flux_networking_shared.tui.widgets.loading import Loading

if TYPE_CHECKING:
    pass

IpAllocationType = Literal["disabled", "dhcp", "static"]
InterfaceStateTypes = Literal["UP", "DOWN"]


class Interface(Vertical, can_focus=True):
    """Widget displaying a network interface's configuration."""

    class InterfaceClicked(Message):
        """Message sent when interface is clicked."""

        def __init__(self, interface: Interface):
            super().__init__()
            self.interface = interface

    ip_allocation: var[IpAllocationType] = var("disabled")
    address: var[IPv4Interface | None] = var(None)
    interface_state: var[InterfaceStateTypes] = var("DOWN")

    DEFAULT_CSS = """
    Interface {
        height: auto;
        border: solid $primary;
        padding: 1;
        margin: 1;
    }
    Interface:focus {
        border: solid $accent;
    }
    Interface.--highlight {
        border: solid $accent;
    }
    Interface .select-label {
        text-style: italic;
        color: $text-muted;
    }
    """

    @classmethod
    def from_dict(cls, data: dict, **kwargs) -> Interface:
        """Create an Interface from a dictionary (e.g., from RPC response).

        Args:
            data: Dictionary with interface data
            kwargs: Additional keyword arguments

        Returns:
            Interface instance
        """
        address = None
        if data.get("address"):
            try:
                address = IPv4Interface(data["address"])
            except ValueError:
                pass

        return cls(
            type=data.get("type", "ethernet"),
            name=data.get("name", "unknown"),
            state=data.get("state", "DOWN"),
            ifindex=data.get("ifindex", -1),
            mac_addr=data.get("mac_addr", ""),
            ip_allocation=data.get("ip_allocation", "disabled"),
            network_vendor=data.get("network_vendor", ""),
            network_type=data.get("network_type", ""),
            address=address,
            vlan=data.get("vlan"),
            parent_ifindex=data.get("parent_ifindex"),
            **kwargs,
        )

    def __init__(
        self,
        type: str,
        name: str,
        state: str,
        ifindex: int,
        mac_addr: str,
        ip_allocation: IpAllocationType,
        network_vendor: str,
        network_type: str,
        address: IPv4Interface | None = None,
        vlan: int | None = None,
        parent_ifindex: int | None = None,
        bridge_id: str | None = None,
        gateway: str | None = None,
        dns: list[str] | None = None,
        **kwargs,
    ) -> None:
        """Initialize the interface widget.

        Args:
            type: Interface type (ethernet, vlan, etc.)
            name: Interface name (eth0, etc.)
            state: Interface state (UP, DOWN)
            ifindex: Interface index
            mac_addr: MAC address
            ip_allocation: IP allocation method
            network_vendor: Network card vendor
            network_type: Network type
            address: IPv4 address with prefix
            vlan: VLAN ID if applicable
            parent_ifindex: Parent interface index for VLANs
            bridge_id: Bridge ID if applicable
            gateway: Gateway address
            dns: DNS servers
            kwargs: Additional widget arguments
        """
        super().__init__(**kwargs)

        self._interface_state = state
        self.interface_type = type
        self.interface_name = name
        self.ifindex = ifindex
        self.mac_addr = mac_addr
        self._ip_allocation = ip_allocation
        self.network_vendor = network_vendor
        self.network_type = network_type
        self._address = address
        self.vlan = vlan
        self.parent_ifindex = parent_ifindex
        self.bridge_id = bridge_id
        self.gateway = gateway
        self.dns = dns or []

        self.border_title = f"Interface: {self.interface_name}"

        self.select_label = Label("Select To Configure", classes="select-label")
        self.select_label.visible = False

    @contextmanager
    def loading_indicator(self) -> Generator:
        """Show loading indicator during operations."""
        self.loading = True
        yield
        self.loading = False

    def get_loading_widget(self) -> Widget:
        """Get the loading widget."""
        return Loading("Updating Interface")

    def watch_has_focus(self, value: bool) -> None:
        """Update styling when focus changes."""
        super().watch_has_focus(value)
        self.set_class(value, "--highlight")
        self.select_label.visible = value

    def update_from(self, other: Interface) -> None:
        """Update this interface's state from another.

        Args:
            other: Interface to copy state from
        """
        assert isinstance(other, Interface)
        self.ip_allocation = other.ip_allocation
        self.address = other.address
        self.interface_state = other.interface_state

    def update_from_dict(self, data: dict) -> None:
        """Update this interface's state from a dictionary.

        Args:
            data: Dictionary with updated interface data
        """
        if "ip_allocation" in data:
            self.ip_allocation = data["ip_allocation"]
        if "address" in data:
            if data["address"]:
                try:
                    self.address = IPv4Interface(data["address"])
                except ValueError:
                    pass
            else:
                self.address = None
        if "state" in data:
            self.interface_state = data["state"]

    def compose(self) -> ComposeResult:
        """Compose the interface display."""
        yield FieldSet("Type", Label(self.interface_type))
        yield FieldSet("State", Label(self._interface_state, id="interface_state"))
        if self.vlan:
            yield FieldSet("VlanId", Label(str(self.vlan)))
        yield FieldSet("Allocation", Label(self._ip_allocation, id="ip_allocation"))
        yield FieldSet("Address", Label(str(self._address), id="ip_address"))
        if self.bridge_id:
            yield FieldSet("BridgeId", Label(self.bridge_id))

        if self.network_vendor:
            yield FieldSet("Vendor", Label(self.network_vendor))

        yield FieldSet("MAC Address", Label(self.mac_addr))
        yield self.select_label

    def on_mount(self) -> None:
        """Initialize reactive properties after mount."""
        self.ip_allocation = self._ip_allocation
        self.address = self._address
        self.interface_state = self._interface_state

    def on_click(self, event: Click) -> None:
        """Handle click events."""
        event.stop()
        self.post_message(Interface.InterfaceClicked(self))

    def watch_ip_allocation(self, old: IpAllocationType, new: IpAllocationType) -> None:
        """Update display when IP allocation changes."""
        if old == new:
            return

        self._ip_allocation = new

        try:
            allocation = self.query_one("#ip_allocation", Label)
        except NoMatches:
            return

        allocation.update(new)

    def watch_address(self, old: str, new: IPv4Interface) -> None:
        """Update display when address changes."""
        if old == new:
            return

        self._address = new

        try:
            ip = self.query_one("#ip_address", Label)
        except NoMatches:
            return

        ip.update(str(new))

    def watch_interface_state(
        self, old: InterfaceStateTypes, new: InterfaceStateTypes
    ) -> None:
        """Update display when interface state changes."""
        if old == new:
            return

        self._interface_state = new

        try:
            state = self.query_one("#interface_state", Label)
        except NoMatches:
            return

        state.update(str(new))
