import ipaddress
import json
from collections import OrderedDict
from socket import AF_INET, AF_INET6

from pyroute2.common import AF_MPLS, dqn2int, get_address_family
from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg


class MPLSTarget(OrderedDict):
    def __init__(self, prime=None):
        super(MPLSTarget, self).__init__()
        if prime is None:
            prime = {}
        elif isinstance(prime, str):
            prime = {'label': int(prime)}
        elif isinstance(prime, int):
            prime = {'label': prime}
        elif isinstance(prime, dict):
            pass
        else:
            raise TypeError()
        self['label'] = prime.get('label', 16)
        self['tc'] = prime.get('tc', 0)
        self['bos'] = prime.get('bos', 1)
        self['ttl'] = prime.get('ttl', 0)

    def __eq__(self, right):
        return (
            isinstance(right, (dict, MPLSTarget))
            and self['label'] == right.get('label', 16)
            and self['tc'] == right.get('tc', 0)
            and self['bos'] == right.get('bos', 1)
            and self['ttl'] == right.get('ttl', 0)
        )

    def __repr__(self):
        return repr(dict(self))


class IPTargets:
    add_defaults = True

    def parse_target(self, key, context, value):
        ret = {key: value}
        set_full_mask = False
        if isinstance(value, int):
            return {'family': AF_MPLS, key: MPLSTarget(value)}
        if isinstance(value, (list, tuple)):
            targets = []
            target = None
            for spec in value:
                target = MPLSTarget(spec)
                target['bos'] = 0
                targets.append(target)
            if target:
                target['bos'] = 1
                return {'family': AF_MPLS, key: targets}
            else:
                return {}
        if isinstance(value, dict):
            if value.get('label'):
                return {'family': AF_MPLS, key: MPLSTarget(value)}
            # do not overwrite message family for IP VIA
            return {key: value}
        try:
            return self.parse_target(key, context, json.loads(value))
        except (json.JSONDecodeError, TypeError):
            pass
        if isinstance(value, str):
            if value == '':
                return {}
            labels = value.split('/')
            if len(labels) > 1:
                # MPLS label stack simple syntax? e.g.: 16/24, 200/300 etc.
                try:
                    return self.parse_target(key, context, labels)
                except ValueError:
                    pass
                # only simple IP targets are left
                value, prefixlen = labels
                ret[key] = value
                if self.add_defaults:
                    if prefixlen.find('.') > 0:
                        ret[f'{key}_len'] = dqn2int(prefixlen, AF_INET)
                    elif prefixlen.find(':') >= 0:
                        ret[f'{key}_len'] = dqn2int(prefixlen, AF_INET6)
                    else:
                        ret[f'{key}_len'] = int(prefixlen)
            else:
                if (
                    self.add_defaults
                    and key in ('dst', 'src')
                    and f'{key}_len' not in context
                ):
                    set_full_mask = True

            if self.add_defaults:
                ret['family'] = get_address_family(value)
                if ret['family'] == AF_INET6:
                    ret[key] = ipaddress.ip_address(value).compressed
                if set_full_mask:
                    if ret['family'] == AF_INET6:
                        ret[f'{key}_len'] = 128
                    elif ret['family'] == AF_INET:
                        ret[f'{key}_len'] = 32
        return ret

    def set_dst(self, context, value):
        if value in ('', 'default'):
            return {}
        elif value in ('0', '0.0.0.0', '0.0.0.0/0'):
            return {'family': AF_INET}
        elif value in ('::', '::/0'):
            return {'family': AF_INET6}
        return self.parse_target('dst', context, value)

    def set_src(self, context, value):
        return self.parse_target('src', context, value)

    def set_via(self, context, value):
        return self.parse_target('via', context, value)

    def set_newdst(self, context, value):
        return self.parse_target('newdst', context, value)

    def set_gateway(self, context, value):
        return self.parse_target('gateway', context, value)


class Index:
    def set_index(self, context, value):
        if isinstance(value, (list, tuple)):
            value = value[0]
        if isinstance(value, ifinfmsg):
            value = value['index']
        return {'index': value}


class IPRouteFilter:
    def __init__(self, command):
        self.command = command

    def policy(self, key):
        if self.command == 'add' and key in ('tso_max_segs', 'tso_max_size'):
            return False
        return True


class NLAKeyTransform:
    _nla_prefix = ''

    def key_transform(self, key):
        if isinstance(key, str) and key.startswith(self._nla_prefix):
            key = key[len(self._nla_prefix) :].lower()
        return key
