"""Code to handle a Motion Gateway."""
import contextlib
import logging
import socket

from motionblinds import DEVICE_TYPES_WIFI, AsyncMotionMulticast, MotionGateway

from homeassistant.components import network

from .const import DEFAULT_INTERFACE

_LOGGER = logging.getLogger(__name__)


def device_name(blind):
    """Construct common name part of a device."""
    if blind.device_type in DEVICE_TYPES_WIFI:
        return blind.blind_type
    return f"{blind.blind_type} {blind.mac[12:]}"


class ConnectMotionGateway:
    """Class to async connect to a Motion Gateway."""

    def __init__(self, hass, multicast=None, interface=None):
        """Initialize the entity."""
        self._hass = hass
        self._multicast = multicast
        self._gateway_device = None
        self._interface = interface

    @property
    def gateway_device(self):
        """Return the class containing all connections to the gateway."""
        return self._gateway_device

    def update_gateway(self):
        """Update all information of the gateway."""
        self.gateway_device.GetDeviceList()
        self.gateway_device.Update()
        for blind in self.gateway_device.device_list.values():
            blind.Update_from_cache()

    async def async_connect_gateway(self, host, key):
        """Connect to the Motion Gateway."""
        _LOGGER.debug("Initializing with host %s (key %s)", host, key[:3])
        self._gateway_device = MotionGateway(
            ip=host, key=key, multicast=self._multicast
        )
        try:
            # update device info and get the connected sub devices
            await self._hass.async_add_executor_job(self.update_gateway)
        except socket.timeout:
            _LOGGER.error(
                "Timeout trying to connect to Motion Gateway with host %s", host
            )
            return False
        _LOGGER.debug(
            "Motion gateway mac: %s, protocol: %s detected",
            self.gateway_device.mac,
            self.gateway_device.protocol,
        )
        return True

    def check_interface(self):
        """Check if the current interface supports multicast."""
        with contextlib.suppress(socket.timeout):
            return self.gateway_device.Check_gateway_multicast()
        return False

    async def async_get_interfaces(self):
        """Get list of interface to use."""
        interfaces = [DEFAULT_INTERFACE, "0.0.0.0"]
        enabled_interfaces = []
        default_interface = DEFAULT_INTERFACE

        adapters = await network.async_get_adapters(self._hass)
        for adapter in adapters:
            if ipv4s := adapter["ipv4"]:
                ip4 = ipv4s[0]["address"]
                interfaces.append(ip4)
                if adapter["enabled"]:
                    enabled_interfaces.append(ip4)
                    if adapter["default"]:
                        default_interface = ip4

        if len(enabled_interfaces) == 1:
            default_interface = enabled_interfaces[0]
            interfaces.remove(default_interface)
            interfaces.insert(0, default_interface)

        if self._interface is not None:
            interfaces.remove(self._interface)
            interfaces.insert(0, self._interface)

        return interfaces

    async def async_check_interface(self, host, key):
        """Connect to the Motion Gateway."""
        interfaces = await self.async_get_interfaces()
        for interface in interfaces:
            _LOGGER.debug(
                "Checking Motion Blinds interface '%s' with host %s", interface, host
            )
            # initialize multicast listener
            check_multicast = AsyncMotionMulticast(interface=interface)
            try:
                await check_multicast.Start_listen()
            except socket.gaierror:
                continue
            except OSError:
                continue

            # trigger test multicast
            self._gateway_device = MotionGateway(
                ip=host, key=key, multicast=check_multicast
            )
            result = await self._hass.async_add_executor_job(self.check_interface)

            # close multicast listener again
            try:
                check_multicast.Stop_listen()
            except socket.gaierror:
                continue

            if result:
                # successfully received multicast
                _LOGGER.debug(
                    "Success using Motion Blinds interface '%s' with host %s",
                    interface,
                    host,
                )
                return interface

        _LOGGER.error(
            "Could not find working interface for Motion Blinds host %s, using interface '%s'",
            host,
            self._interface,
        )
        return self._interface
