"""Utilities for working with IP addresses"""

from parallels.core.utils.common.ip import is_ipv4, is_ipv6


class IPAddressType(object):
    """Adapter to work with IPv4 and IPv6 addresses in a single way"""

    @property
    def title(self):
        """Title of IP address type ("IPv4" or "IPv6")

        :rtype: str
        """
        raise NotImplementedError()

    @staticmethod
    def is_valid_ip(ip):
        """Check if specified string is correct address of that type

        :type ip: str | unicode
        :rtype: bool
        """
        raise NotImplementedError()

    @staticmethod
    def get_for_backup_subscription(subscription):
        """Get IP address of that type for subscription

        :type subscription: parallels.core.dump.data_model.Subscription
        :rtype: str | unicode
        """
        raise NotImplementedError()

    @staticmethod
    def get_type_for_backup_subscription(subscription):
        """Get IP address type (shared or exclusive) of that type for subscription

        :type subscription: parallels.core.dump.data_model.Subscription
        :rtype: str | unicode
        """
        raise NotImplementedError()

    @staticmethod
    def get_for_ip_mapping(mapping):
        """Get IP address of that type from migration list IP mapping

        :type mapping: parallels.core.migration_list.entities.ip_mapping.SubscriptionIPMapping
        :rtype: str | unicode
        """
        raise NotImplementedError()

    def get_default_ip(self, backup):
        """Get default IP address on the source node according to Plesk backup

        :type backup: parallels.core.dump.dump.PleskBackupSourceBase
        :rtype: str | unicode
        """
        # First, try explicitly specified IP address
        if backup.get_default_ip() is not None:
            if self.is_valid_ip(backup.get_default_ip()):
                return backup.get_default_ip()

        # If default IP is not explicitly specified in backup, fallback
        # to the first shared IP of that type from backup
        system_ips = [
            ip.address for ip in backup.get_system_ips()
            if ip.type == 'shared'
        ]
        system_ips_of_type = [
            ip for ip in system_ips if self.is_valid_ip(ip)
        ]
        if len(system_ips_of_type) > 0:
            return system_ips_of_type[0]

        # Return None if no IP of corresponding type was found
        return None


class IPv4AddressType(IPAddressType):
    """Adapter over IPv4 address to manage IPv4 and IPv6 addresses in a single way"""

    @property
    def title(self):
        return 'IPv4'

    @staticmethod
    def is_valid_ip(ip):
        """Check if specified string is correct address of that type

        :type ip: str | unicode
        :rtype: bool
        """
        return is_ipv4(ip)

    @staticmethod
    def get_for_backup_subscription(subscription):
        """Get IPv4 address of subscription

        :type subscription: parallels.core.dump.data_model.Subscription
        :rtype: str | unicode
        """
        return subscription.ip

    @staticmethod
    def get_type_for_backup_subscription(subscription):
        """Get IP address type (shared or exclusive) of that type for subscription

        :type subscription: parallels.core.dump.data_model.Subscription
        :rtype: str | unicode
        """
        return subscription.ip_type

    @staticmethod
    def get_for_ip_mapping(mapping):
        """Get IPv4 address from migration list IP mapping

        :type mapping: parallels.core.migration_list.entities.ip_mapping.SubscriptionIPMapping
        :rtype: str | unicode
        """
        return mapping.v4


class IPv6AddressType(IPAddressType):
    """Adapter over IPv6 address to manage IPv4 and IPv6 addresses in a single way"""

    @property
    def title(self):
        return 'IPv6'

    @staticmethod
    def is_valid_ip(ip):
        """Check if specified string is correct address of that type

        :type ip: str | unicode
        :rtype: bool
        """
        return is_ipv6(ip)

    @staticmethod
    def get_for_backup_subscription(subscription):
        """Get IPv6 address of subscription

        :type subscription: parallels.core.dump.model.Subscription
        :rtype: str | unicode
        """
        return subscription.ipv6

    @staticmethod
    def get_type_for_backup_subscription(subscription):
        """Get IP address type (shared or exclusive) of that type for subscription

        :type subscription: parallels.core.dump.data_model.Subscription
        :rtype: str | unicode
        """
        return subscription.ipv6_type

    @staticmethod
    def get_for_ip_mapping(mapping):
        """Get IPv6 address from migration list IP mapping

        :type mapping: parallels.core.migration_list.entities.ip_mapping.SubscriptionIPMapping
        :rtype: str | unicode
        """
        return mapping.v6


def get_ip_address_types():
    """Get IPv4 and IPv6 address types as list

    :rtype: list[parallels.core.utils.ip_utils.IPAddressType]
    """
    return [IPv4AddressType(), IPv6AddressType()]
