"""Network management for container groups."""

from __future__ import annotations

import subprocess
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from gaol.groups.models import GroupSpec


class GroupNetworkError(Exception):
    """Error during group network operations."""

    pass


class GroupNetworkManager:
    """Manage shared networking for container groups.

    This manager creates and destroys bridge networks for groups,
    enabling service discovery between containers in the same group.

    For Docker runtime, it uses Docker networks.
    For other runtimes, it uses Linux bridge interfaces.
    """

    NETWORK_PREFIX = "gaol"

    def __init__(self, runtime: str = "docker") -> None:
        """Initialize the network manager.

        Args:
            runtime: Default container runtime to use
        """
        self._runtime = runtime

    def get_network_name(self, group_name: str) -> str:
        """Get the network name for a group.

        Args:
            group_name: Name of the container group

        Returns:
            Network name in the format "gaol-{group_name}"
        """
        return f"{self.NETWORK_PREFIX}-{group_name}"

    def create_network(self, group_name: str, runtime: str | None = None) -> str:
        """Create a shared network for a container group.

        Args:
            group_name: Name of the container group
            runtime: Runtime to create network for (default: self._runtime)

        Returns:
            Name of the created network

        Raises:
            GroupNetworkError: If network creation fails
        """
        network_name = self.get_network_name(group_name)
        runtime = runtime or self._runtime

        if runtime == "docker":
            return self._create_docker_network(network_name)
        else:
            return self._create_bridge_network(network_name)

    def destroy_network(self, group_name: str, runtime: str | None = None) -> None:
        """Destroy a group's shared network.

        Args:
            group_name: Name of the container group
            runtime: Runtime to destroy network for (default: self._runtime)

        Raises:
            GroupNetworkError: If network destruction fails
        """
        network_name = self.get_network_name(group_name)
        runtime = runtime or self._runtime

        if runtime == "docker":
            self._destroy_docker_network(network_name)
        else:
            self._destroy_bridge_network(network_name)

    def network_exists(self, group_name: str, runtime: str | None = None) -> bool:
        """Check if a group's network exists.

        Args:
            group_name: Name of the container group
            runtime: Runtime to check (default: self._runtime)

        Returns:
            True if the network exists
        """
        network_name = self.get_network_name(group_name)
        runtime = runtime or self._runtime

        if runtime == "docker":
            return self._docker_network_exists(network_name)
        else:
            return self._bridge_exists(network_name)

    def _create_docker_network(self, network_name: str) -> str:
        """Create a Docker network.

        Args:
            network_name: Name for the network

        Returns:
            Network name

        Raises:
            GroupNetworkError: If creation fails
        """
        # Check if network already exists
        if self._docker_network_exists(network_name):
            return network_name

        result = subprocess.run(
            [
                "docker",
                "network",
                "create",
                "--driver",
                "bridge",
                "--label",
                f"gaol.group={network_name}",
                network_name,
            ],
            capture_output=True,
            text=True,
        )

        if result.returncode != 0:
            raise GroupNetworkError(f"Failed to create Docker network: {result.stderr}")

        return network_name

    def _destroy_docker_network(self, network_name: str) -> None:
        """Destroy a Docker network.

        Args:
            network_name: Name of the network

        Raises:
            GroupNetworkError: If destruction fails
        """
        if not self._docker_network_exists(network_name):
            return

        result = subprocess.run(
            ["docker", "network", "rm", network_name],
            capture_output=True,
            text=True,
        )

        if result.returncode != 0:
            # Ignore "network not found" errors
            if "not found" not in result.stderr.lower():
                raise GroupNetworkError(f"Failed to remove Docker network: {result.stderr}")

    def _docker_network_exists(self, network_name: str) -> bool:
        """Check if a Docker network exists.

        Args:
            network_name: Name of the network

        Returns:
            True if the network exists
        """
        result = subprocess.run(
            ["docker", "network", "inspect", network_name],
            capture_output=True,
            text=True,
        )
        return result.returncode == 0

    def _create_bridge_network(self, network_name: str) -> str:
        """Create a Linux bridge network for non-Docker runtimes.

        Args:
            network_name: Name for the bridge

        Returns:
            Bridge name

        Raises:
            GroupNetworkError: If creation fails
        """
        from gaol.networking.bridge import bridge_exists, create_bridge
        from gaol.networking.models import BridgeConfig

        if bridge_exists(network_name):
            return network_name

        try:
            config = BridgeConfig(
                name=network_name,
                ip_address="172.30.0.1/16",  # Use private range for groups
                nat_enabled=True,
            )
            create_bridge(config)
            return network_name
        except Exception as e:
            raise GroupNetworkError(f"Failed to create bridge network: {e}") from e

    def _destroy_bridge_network(self, network_name: str) -> None:
        """Destroy a Linux bridge network.

        Args:
            network_name: Name of the bridge

        Raises:
            GroupNetworkError: If destruction fails
        """
        from gaol.networking.bridge import bridge_exists, delete_bridge

        if not bridge_exists(network_name):
            return

        try:
            delete_bridge(network_name)
        except Exception as e:
            raise GroupNetworkError(f"Failed to delete bridge network: {e}") from e

    def _bridge_exists(self, network_name: str) -> bool:
        """Check if a Linux bridge exists.

        Args:
            network_name: Name of the bridge

        Returns:
            True if the bridge exists
        """
        from gaol.networking.bridge import bridge_exists

        return bridge_exists(network_name)

    def get_container_dns_alias(self, group_name: str, container_name: str) -> str:
        """Get the DNS alias for a container within a group.

        In Docker networks, containers can reach each other by name.
        This returns the simple container name that other containers
        can use to connect.

        Args:
            group_name: Name of the container group
            container_name: Name of the container within the group

        Returns:
            DNS alias for the container
        """
        # In Docker, containers on the same network can reach each other
        # by their container name directly
        return container_name

    def get_full_container_name(self, group_name: str, container_name: str) -> str:
        """Get the full container name for a group member.

        Args:
            group_name: Name of the container group
            container_name: Name of the container within the group

        Returns:
            Full container name (group-container)
        """
        return f"{group_name}-{container_name}"


def get_network_manager(runtime: str = "docker") -> GroupNetworkManager:
    """Get a network manager for the given runtime.

    Args:
        runtime: Container runtime

    Returns:
        GroupNetworkManager instance
    """
    return GroupNetworkManager(runtime=runtime)
