# Copyright 2016 Splunk, Inc.
# SPDX-FileCopyrightText: 2020 2020
#
# SPDX-License-Identifier: Apache-2.0

"""
Net utilities.
"""
import inspect
import itertools
import re
import socket
from functools import wraps

from . import ip_math

__all__ = ["resolve_hostname"]


def resolve_hostname(addr):
    """Try to resolve an IP to a host name and returns None
    on common failures.

    :param addr: IP address to resolve.
    :type addr: ``string``
    :returns: Host name if success else None.
    :rtype: ``string``

    :raises ValueError: If `addr` is not a valid address
    """

    if ip_math.is_valid_ip(addr):
        try:
            name, _, _ = socket.gethostbyaddr(addr)
            return name
        except socket.gaierror:
            # [Errno 8] nodename nor servname provided, or not known
            pass
        except socket.herror:
            # [Errno 1] Unknown host
            pass
        except socket.timeout:
            # Timeout.
            pass

        return None
    else:
        raise ValueError("Invalid ip address.")


def is_valid_hostname(hostname):
    """Validate a host name.

    :param hostname: host name to validate.
    :type hostname: ``string``
    :returns: True if is valid else False
    :rtype: ``bool``
    """

    if len(hostname) > 255:
        return False
    if hostname[-1:] == ".":
        hostname = hostname[:-1]
    allowed = re.compile("(?!-)(::)?[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
    return all(allowed.match(x) for x in hostname.split("."))


def is_valid_port(port):
    """Validate a port.

    :param port: port to validate.
    :type port: ``(string, int)``
    :returns: True if is valid else False
    :rtype: ``bool``
    """

    try:
        return 0 < int(port) <= 65535
    except ValueError:
        return False


def is_valid_scheme(scheme):
    """Validate a scheme.

    :param scheme: scheme to validate.
    :type scheme: ``string``
    :returns: True if is valid else False
    :rtype: ``bool``
    """

    return scheme.lower() in ("http", "https")


def check_css_params(**validators):
    """A decorator for validating arguments for function with specified
     validating function which returns True or False.

    :param validators: argument and it's validation function
    :raises ValueError: If validation fails.
    """

    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            arg_spec = inspect.getargspec(f)
            actual_args = dict(list(zip(arg_spec.args, args)) + list(kwargs.items()))
            dfs = arg_spec.defaults
            optional = dict(list(zip(arg_spec.args[-len(dfs) :], dfs))) if dfs else {}

            for arg, func in list(validators.items()):
                if arg not in actual_args:
                    continue
                value = actual_args[arg]
                if arg in optional and optional[arg] == value:
                    continue
                if not func(value):
                    raise ValueError("Illegal argument: {}={}".format(arg, value))
            return f(*args, **kwargs)

        return wrapper

    return decorator
