"""Bridge network management."""

from __future__ import annotations

import ipaddress
import shutil
import subprocess
from pathlib import Path
from typing import NamedTuple

from gaol.networking.models import BridgeConfig


class BridgeError(Exception):
    """Bridge network error."""

    pass


class BridgeInfo(NamedTuple):
    """Information about a network bridge."""

    name: str
    exists: bool
    ip_address: str | None
    interfaces: list[str]
    state: str  # up, down, unknown


def bridge_exists(name: str) -> bool:
    """Check if a bridge interface exists.

    Args:
        name: Bridge interface name

    Returns:
        True if the bridge exists
    """
    bridge_path = Path(f"/sys/class/net/{name}/bridge")
    return bridge_path.exists()


def get_bridge_info(name: str) -> BridgeInfo | None:
    """Get information about a bridge.

    Args:
        name: Bridge interface name

    Returns:
        BridgeInfo or None if bridge doesn't exist
    """
    if not bridge_exists(name):
        return None

    # Get IP address
    ip_address = None
    try:
        result = subprocess.run(
            ["ip", "-4", "addr", "show", name],
            capture_output=True,
            text=True,
        )
        if result.returncode == 0:
            for line in result.stdout.splitlines():
                if "inet " in line:
                    # Extract IP/prefix
                    parts = line.strip().split()
                    ip_idx = parts.index("inet") + 1
                    if ip_idx < len(parts):
                        ip_address = parts[ip_idx]
                    break
    except Exception:
        pass

    # Get attached interfaces
    interfaces: list[str] = []
    try:
        brif_path = Path(f"/sys/class/net/{name}/brif")
        if brif_path.exists():
            interfaces = [f.name for f in brif_path.iterdir()]
    except Exception:
        pass

    # Get state
    state = "unknown"
    try:
        state_path = Path(f"/sys/class/net/{name}/operstate")
        if state_path.exists():
            state = state_path.read_text().strip()
    except Exception:
        pass

    return BridgeInfo(
        name=name,
        exists=True,
        ip_address=ip_address,
        interfaces=interfaces,
        state=state,
    )


def list_bridges() -> list[BridgeInfo]:
    """List all bridge interfaces on the system.

    Returns:
        List of BridgeInfo for each bridge found
    """
    bridges: list[BridgeInfo] = []
    net_path = Path("/sys/class/net")

    if not net_path.exists():
        return bridges

    for iface_path in net_path.iterdir():
        bridge_path = iface_path / "bridge"
        if bridge_path.exists():
            info = get_bridge_info(iface_path.name)
            if info:
                bridges.append(info)

    return bridges


def create_bridge(
    config: BridgeConfig,
    use_sudo: bool = True,
) -> BridgeInfo:
    """Create a bridge interface.

    Args:
        config: Bridge configuration
        use_sudo: Use sudo for commands

    Returns:
        BridgeInfo for the created bridge

    Raises:
        BridgeError: If creation fails
    """
    if bridge_exists(config.name):
        raise BridgeError(f"Bridge already exists: {config.name}")

    cmd_prefix = ["sudo"] if use_sudo else []

    # Create the bridge
    result = subprocess.run(
        cmd_prefix + ["ip", "link", "add", config.name, "type", "bridge"],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        raise BridgeError(f"Failed to create bridge: {result.stderr}")

    try:
        # Set IP address if specified
        if config.ip_address:
            result = subprocess.run(
                cmd_prefix
                + ["ip", "addr", "add", config.ip_address, "dev", config.name],
                capture_output=True,
                text=True,
            )
            if result.returncode != 0:
                raise BridgeError(f"Failed to set IP address: {result.stderr}")

        # Set IPv6 address if specified
        if config.ipv6_enabled and config.ipv6_address:
            result = subprocess.run(
                cmd_prefix
                + ["ip", "-6", "addr", "add", config.ipv6_address, "dev", config.name],
                capture_output=True,
                text=True,
            )
            if result.returncode != 0:
                raise BridgeError(f"Failed to set IPv6 address: {result.stderr}")

        # Bring up the interface
        result = subprocess.run(
            cmd_prefix + ["ip", "link", "set", config.name, "up"],
            capture_output=True,
            text=True,
        )
        if result.returncode != 0:
            raise BridgeError(f"Failed to bring up bridge: {result.stderr}")

        # Enable NAT if requested (for shared/NAT networking)
        if config.nat_enabled and config.ip_address:
            _setup_bridge_nat(config.name, config.ip_address, use_sudo)

    except Exception as e:
        # Clean up on failure
        subprocess.run(
            cmd_prefix + ["ip", "link", "delete", config.name],
            capture_output=True,
        )
        raise BridgeError(f"Bridge setup failed: {e}") from e

    info = get_bridge_info(config.name)
    if info is None:
        raise BridgeError("Bridge created but not found")
    return info


def delete_bridge(name: str, use_sudo: bool = True) -> None:
    """Delete a bridge interface.

    Args:
        name: Bridge interface name
        use_sudo: Use sudo for commands

    Raises:
        BridgeError: If deletion fails
    """
    if not bridge_exists(name):
        raise BridgeError(f"Bridge does not exist: {name}")

    cmd_prefix = ["sudo"] if use_sudo else []

    # Bring down the interface
    subprocess.run(
        cmd_prefix + ["ip", "link", "set", name, "down"],
        capture_output=True,
    )

    # Delete the bridge
    result = subprocess.run(
        cmd_prefix + ["ip", "link", "delete", name, "type", "bridge"],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        raise BridgeError(f"Failed to delete bridge: {result.stderr}")


def _setup_bridge_nat(
    _bridge_name: str,
    bridge_ip: str,
    use_sudo: bool = True,
) -> None:
    """Set up NAT for a bridge network.

    Args:
        _bridge_name: Bridge interface name (reserved for future use)
        bridge_ip: Bridge IP with prefix (e.g., "192.168.100.1/24")
        use_sudo: Use sudo for commands
    """
    # Extract network from IP/prefix
    network = ipaddress.ip_network(bridge_ip, strict=False)

    cmd_prefix = ["sudo"] if use_sudo else []

    # Enable IP forwarding
    subprocess.run(
        cmd_prefix + ["sysctl", "-w", "net.ipv4.ip_forward=1"],
        capture_output=True,
    )

    # Add NAT rule using iptables or nftables
    if shutil.which("nft"):
        # Use nftables
        subprocess.run(
            cmd_prefix + ["nft", "add", "table", "nat"],
            capture_output=True,
        )
        subprocess.run(
            cmd_prefix
            + [
                "nft",
                "add",
                "chain",
                "nat",
                "postrouting",
                "{ type nat hook postrouting priority 100 ; }",
            ],
            capture_output=True,
        )
        subprocess.run(
            cmd_prefix
            + [
                "nft",
                "add",
                "rule",
                "nat",
                "postrouting",
                f"ip saddr {network}",
                "masquerade",
            ],
            capture_output=True,
        )
    elif shutil.which("iptables"):
        # Use iptables
        subprocess.run(
            cmd_prefix
            + [
                "iptables",
                "-t",
                "nat",
                "-A",
                "POSTROUTING",
                "-s",
                str(network),
                "-j",
                "MASQUERADE",
            ],
            capture_output=True,
        )


def ensure_bridge(config: BridgeConfig, use_sudo: bool = True) -> BridgeInfo:
    """Ensure a bridge exists, creating it if necessary.

    Args:
        config: Bridge configuration
        use_sudo: Use sudo for commands

    Returns:
        BridgeInfo for the bridge
    """
    info = get_bridge_info(config.name)
    if info:
        return info
    return create_bridge(config, use_sudo)
