"""Interface configuration modal screen."""

from __future__ import annotations

from ipaddress import IPv4Interface, IPv4Network

from textual import on
from textual.app import ComposeResult
from textual.binding import Binding
from textual.containers import Horizontal, Vertical
from textual.reactive import var
from textual.screen import ModalScreen
from textual.validation import Function, Number
from textual.widgets import Button, Input, Label, Select, Switch

from flux_networking_shared.tui.models.interface_config_result import (
    InterfaceConfigResult,
    InterfaceShapingPolicy,
)
from flux_networking_shared.tui.screens.confirm_exit import ConfirmExitScreen
from flux_networking_shared.tui.widgets.address_builder import Layer3Builder
from flux_networking_shared.tui.widgets.interface import Interface
from flux_networking_shared.tui.widgets.labels import InvalidLabel


class InterfaceModalScreen(ModalScreen[InterfaceConfigResult]):
    """Modal screen for configuring a network interface.

    Allows users to:
    - Select IP allocation mode (DHCP, Static, Disabled)
    - Configure static IP settings (subnet, address, gateway, DNS)
    - Add/delete VLAN subinterfaces
    - Configure bandwidth limiting
    """

    DEFAULT_CSS = """
        InterfaceModalScreen {
            align: center middle;
        }

        InterfaceModalScreen > .modal-container {
            padding: 1;
            width: 81;
            height: auto;
            border: solid $primary;
            align: center middle;
        }

        InterfaceModalScreen > .modal-container > #vlan-input-container {
            height: auto;
            align: center middle;
        }

        InterfaceModalScreen > .modal-container > #vlan-input-container > Input {
            width: 20;
        }

        InterfaceModalScreen > .modal-container > #vlan-input-container > Input.-valid {
            border: tall $success 60%;
        }

        InterfaceModalScreen > .modal-container > #vlan-input-container > Input.-valid:focus {
            border: tall $success;
        }

        InterfaceModalScreen > .modal-container > #vlan-input-invalid {
            margin-bottom: 1;
        }

        InterfaceModalScreen > .modal-container > Layer3Builder {
            margin-top: 1;
            padding: 1 0 0 0;
            height: auto;
            grid-size: 4;
            grid-gutter: 0;
            width: 81;
            grid-columns: 9 31;
            grid-rows: 3 2;
        }

        InterfaceModalScreen > .modal-container > Layer3Builder > .text-label {
            text-align: right;
            width: 100%;
        }

        InterfaceModalScreen > .modal-container > Layer3Builder > ValidateBlurInput {
            margin-right: 3;
        }

        InterfaceModalScreen > .modal-container > Layer3Builder > ValidateBlurInput.-valid {
            border: tall $success 60%;
        }

        InterfaceModalScreen > .modal-container > Layer3Builder > ValidateBlurInput.-valid:focus {
            border: tall $success;
        }

        InterfaceModalScreen > .modal-container > Layer3Builder > InvalidLabel {
            width: 1fr;
            height: auto;
        }

        InterfaceModalScreen > .modal-container > Layer3Builder > WarningLabel {
            width: 100%;
            height: auto;
            column-span: 4;
            margin: 0 3 0 0;
            text-align: center;
        }

        InterfaceModalScreen > .modal-container > #options-container {
            height: auto;
            width: 100%;
            align: center middle;
        }

        InterfaceModalScreen > .modal-container > #options-container > Button {
            dock: right;
        }

        InterfaceModalScreen > .modal-container > #options-container Select {
            width: 18;
        }

        InterfaceModalScreen > .modal-container > #bandwidth-container {
            align: center middle;
            height: auto;
            margin: 1;
            width: 100%;
        }

        InterfaceModalScreen > .modal-container > #bandwidth-container > Select {
            width: 18;
        }

        InterfaceModalScreen > .modal-container > .button-container {
            align: center bottom;
            width: 100%;
            height: auto;
        }

        InterfaceModalScreen > .modal-container > .button-container > Button {
            margin: 0 1;
        }
    """

    BINDINGS = [Binding("escape", "return", "Dismiss", show=False)]

    display_layer3_builder = var(False)

    def __init__(
        self,
        interface: Interface,
        default_gateway: str,
        resolved_dns: list[str],
        shaping_policy: InterfaceShapingPolicy,
    ) -> None:
        """Initialize the interface modal.

        Args:
            interface: The interface to configure
            default_gateway: Current default gateway address
            resolved_dns: List of currently configured DNS servers
            shaping_policy: Current traffic shaping policy
        """
        super().__init__()
        self.interface = interface
        self.default_gateway = default_gateway
        self.resolved_dns = resolved_dns
        self.shaping_policy = shaping_policy

        subnet = ""
        address = ""

        if interface.address:
            subnet = str(interface.address.network)
            address = str(interface.address.ip)

        static_allocation = interface.ip_allocation == "static"

        self.layer3_builder = Layer3Builder(
            subnet, address, default_gateway, ", ".join(resolved_dns)
        )
        self.layer3_builder.display = static_allocation

        self.addressing_container = Vertical(classes="modal-container")
        self.vlan_container = Vertical(classes="modal-container")
        self.vlan_container.display = False

        self.add_vlan_button = Button("Add VLAN", id="interface_add_vlan")
        self.delete_vlan_int = Button("Delete", id="interface_delete_vlan")

        if interface.interface_type == "vlan":
            self.add_vlan_button.visible = False
        else:
            self.delete_vlan_int.visible = False

    def compose(self) -> ComposeResult:
        with self.addressing_container:
            with Horizontal(id="options-container"):
                yield Label("IP Allocation:", classes="text-label")
                yield Select(
                    [
                        ("DHCP", "dhcp"),
                        ("Static", "static"),
                        ("Disabled", "disabled"),
                    ],
                    value=self.interface.ip_allocation,
                    id="allocation_selector",
                )
                yield self.add_vlan_button
                yield self.delete_vlan_int

            yield self.layer3_builder

            with Horizontal(id="bandwidth-container"):
                options: list[tuple[str, int | None]] = [
                    ("35Mbps", 35),
                    ("75Mbps", 75),
                    ("135Mbps", 135),
                    ("250Mbps", 250),
                    ("Full", None),
                ]

                yield Label("Limit Bandwidth:", classes="text-label")
                yield Switch(value=self.shaping_policy.is_shaped)
                yield Select(
                    options,
                    value=self.shaping_policy.rate,
                    disabled=not self.shaping_policy.is_shaped,
                    id="bandwidth_selector",
                )

            with Horizontal(classes="button-container"):
                yield Button("Back", id="interface_back")
                yield Button("Save", id="interface_save", disabled=True)

        with self.vlan_container:
            with Horizontal(id="vlan-input-container"):
                yield Label("Vlan Tag:", classes="text-label")
                yield Input(
                    placeholder="Enter Tag between 2-4094",
                    validate_on=["changed"],
                    validators=[
                        Number(
                            minimum=2,
                            maximum=4094,
                            failure_description="Vlan must be between 2-4094",
                        ),
                        Function(
                            lambda x: int(x) not in self.interface.child_vlans
                            if x.isdigit()
                            else True,
                            "Vlan in use already",
                        ),
                    ],
                    id="vlan-input",
                )

            yield InvalidLabel(id="vlan-input-invalid")

            with Horizontal(classes="button-container"):
                yield Button("Back", id="interface_back_vlan")
                yield Button("Save", id="interface_save_vlan")

    def action_return(self) -> None:
        """Dismiss without changes."""
        self.dismiss(InterfaceConfigResult(self.interface))

    def on_button_pressed(self, event: Button.Pressed) -> None:
        """Handle button presses."""
        button_id = event.button.id

        if button_id == "interface_add_vlan":
            self.addressing_container.display = False
            self.vlan_container.display = True
            self.query_one("#vlan-input", Input).focus()

        elif button_id == "interface_back_vlan":
            self.query_one("#vlan-input", Input).value = ""
            self.addressing_container.display = True
            self.vlan_container.display = False
            self.query_one(Select).focus()

        elif button_id == "interface_delete_vlan":

            def handle_exit_action(delete: bool | None) -> None:
                if delete:
                    self.dismiss(
                        InterfaceConfigResult(self.interface, change_type="remove")
                    )

            msg = "Are you sure? This action will delete the VLAN interface."
            self.app.push_screen(
                ConfirmExitScreen(msg, action_label="Delete"),
                handle_exit_action,
            )

        elif button_id == "interface_back":
            self.action_return()

        elif button_id == "interface_save_vlan":
            vlan = self.query_one("#vlan-input", Input).value

            interface = self.interface

            interface.blur()

            # Only set default gateway/DNS if not already set
            if not interface.gateway:
                interface.gateway = self.default_gateway if self.default_gateway else ""

            if not interface.dns:
                interface.dns = self.resolved_dns if self.resolved_dns else []

            self.dismiss(
                InterfaceConfigResult(self.interface, change_type="add", vlan=vlan)
            )

        elif button_id == "interface_save":
            allocation = self.query_one("#allocation_selector", Select).value

            if allocation == "static":
                if not self.layer3_builder.complete:
                    return

            interface = self.interface
            result = InterfaceConfigResult(interface)

            subnet, address, gateway, dns = self.layer3_builder.get_selected_values()
            traffic_shaping = self.query_one(Switch).value
            bandwidth_value = self.query_one("#bandwidth_selector", Select).value

            result.bandwidth_limit = bandwidth_value if traffic_shaping else None
            result.bandwidth_changed = (
                result.bandwidth_limit != self.shaping_policy.rate
            )

            if allocation == "static" and interface.ip_allocation == "static":
                changes = []
                current_dns = set(self.resolved_dns) if self.resolved_dns else set()
                new_dns = set(dns) if dns else set()
                changes.append(current_dns != new_dns)
                changes.append(str(interface.address.network) != subnet)
                changes.append(str(interface.address.ip) != address)
                current_gateway = (
                    self.default_gateway if self.default_gateway else "none"
                )
                new_gateway = gateway if gateway else "none"
                changes.append(current_gateway != new_gateway)

                if any(changes):
                    result.change_type = "change"

            if allocation != interface.ip_allocation:
                result.change_type = "change"
                interface.ip_allocation = allocation

            if result.change_type == "change" and allocation == "static":
                network = IPv4Network(subnet)

                prefix = network.prefixlen
                ip_int = IPv4Interface(f"{address}/{prefix}")

                interface.address = ip_int
                interface.gateway = gateway if gateway else "none"
                interface.dns = dns if dns else []

            self.dismiss(result)

    def on_switch_changed(self, event: Switch.Changed) -> None:
        """Handle bandwidth switch toggle."""
        selector = self.query_one("#bandwidth_selector", Select)
        selector.disabled = not event.value

        selector.value = 250 if event.value else None

    def on_select_changed(self, event: Select.Changed) -> None:
        """Handle select changes."""
        if event.select.id == "allocation_selector":
            is_static = event.value == "static"
            self.layer3_builder.display = is_static

    def on_input_changed(self, event: Input.Changed) -> None:
        """Handle VLAN input changes."""
        if event.input.id != "vlan-input":
            return

        label = self.query_one("#vlan-input-invalid", InvalidLabel)
        validation_result = event.validation_result

        if not event.input.value:
            label.set_class(False, "-show-error")
            event.input.set_class(False, "-invalid")
            return

        if not validation_result:
            return

        is_valid = validation_result.is_valid

        button = self.query_one("#interface_save_vlan", Button)
        button.disabled = not is_valid

        label = self.query_one(f"#{event.input.id}-invalid", InvalidLabel)
        label.visible = not is_valid

        label.set_class(not is_valid, "-show-error")

        msg = "" if is_valid else "\n".join(validation_result.failure_descriptions)
        label.update(msg)

    @on(Layer3Builder.Layer3StateChanged)
    def on_layer3_state_change(self, event: Layer3Builder.Layer3StateChanged) -> None:
        """Enable/disable save button based on form validity."""
        save = self.query_one("#interface_save", Button)
        save.disabled = not event.valid
