"""Network manager coordinating all networking operations."""

from __future__ import annotations

from typing import TYPE_CHECKING

from gaol.networking.bridge import BridgeConfig, ensure_bridge, get_bridge_info
from gaol.networking.dns import generate_resolv_conf, get_default_dns_config, get_system_dns
from gaol.networking.firewall import FirewallManager, ForwardingRule
from gaol.networking.models import DNSConfig, NetworkConfigV2
from gaol.networking.ports import PortError, validate_port_mappings

if TYPE_CHECKING:
    from gaol.runtimes.base import ContainerRuntime


class NetworkManager:
    """Manages networking for containers and VMs."""

    def __init__(self, use_sudo: bool = True) -> None:
        self.use_sudo = use_sudo
        self._firewall: FirewallManager | None = None

    @property
    def firewall(self) -> FirewallManager:
        """Get or create the firewall manager."""
        if self._firewall is None:
            self._firewall = FirewallManager(use_sudo=self.use_sudo)
            self._firewall.setup()
        return self._firewall

    def validate_network_config(
        self,
        config: NetworkConfigV2,
        runtime: ContainerRuntime | None = None,
        container_name: str | None = None,
    ) -> list[PortError]:
        """Validate a network configuration.

        Args:
            config: Network configuration to validate
            runtime: Runtime for conflict checking
            container_name: Container name (for excluding from conflict check)

        Returns:
            List of errors found (empty if valid)
        """
        errors: list[PortError] = []

        # Validate port mappings
        errors.extend(
            validate_port_mappings(
                list(config.ports.mappings),
                check_availability=True,
                check_conflicts=True,
                runtime=runtime,
                exclude_container=container_name,
            )
        )

        return errors

    def setup_container_network(
        self,
        container_id: str,
        config: NetworkConfigV2,
        guest_ip: str | None = None,
    ) -> None:
        """Set up networking for a container.

        Args:
            container_id: Container ID/name
            config: Network configuration
            guest_ip: Guest IP (required for libvirt port forwarding)
        """
        # For libvirt VMs with port mappings, set up firewall rules
        if guest_ip and config.ports.mappings:
            for mapping in config.ports.mappings:
                self.firewall.add_forwarding_rule(
                    ForwardingRule(
                        host_port=mapping.host_port,
                        guest_ip=guest_ip,
                        guest_port=mapping.container_port,
                        protocol=mapping.protocol,
                        host_ip=mapping.host_ip,
                        comment=f"gaol:{container_id}",
                    )
                )

    def teardown_container_network(
        self,
        _container_id: str,
        _config: NetworkConfigV2,
        guest_ip: str | None = None,
    ) -> None:
        """Tear down networking for a container.

        Args:
            _container_id: Container ID/name (reserved for future use)
            _config: Network configuration (reserved for future use)
            guest_ip: Guest IP (for firewall rule cleanup)
        """
        if guest_ip:
            self.firewall.clear_rules_for_vm(guest_ip)

    def get_dns_for_container(
        self,
        config: NetworkConfigV2,
        use_system_dns: bool = True,
    ) -> DNSConfig:
        """Get DNS configuration for a container.

        Args:
            config: Network configuration
            use_system_dns: Fall back to system DNS if not configured

        Returns:
            DNSConfig to use
        """
        if config.dns.nameservers:
            return config.dns

        if use_system_dns:
            return get_system_dns()

        # Return default public DNS
        return get_default_dns_config()

    def generate_resolv_conf_for_container(
        self,
        config: NetworkConfigV2,
        use_system_dns: bool = True,
    ) -> str:
        """Generate resolv.conf content for a container.

        Args:
            config: Network configuration
            use_system_dns: Fall back to system DNS if not configured

        Returns:
            resolv.conf content
        """
        dns_config = self.get_dns_for_container(config, use_system_dns)
        return generate_resolv_conf(dns_config)

    def ensure_bridge_for_mode(
        self,
        network_mode: str,
        bridge_config: BridgeConfig | None = None,
    ) -> str | None:
        """Ensure a bridge exists for bridged networking mode.

        Args:
            network_mode: Network mode (BRIDGED, etc.)
            bridge_config: Custom bridge configuration

        Returns:
            Bridge name if one was ensured, None otherwise
        """
        if network_mode.lower() != "bridged":
            return None

        config = bridge_config or BridgeConfig(name="br0")

        info = get_bridge_info(config.name)
        if not info:
            ensure_bridge(config, self.use_sudo)

        return config.name


# Singleton instance
_network_manager: NetworkManager | None = None


def get_network_manager() -> NetworkManager:
    """Get the global network manager instance."""
    global _network_manager
    if _network_manager is None:
        _network_manager = NetworkManager()
    return _network_manager
