import re
from urllib.parse import urlparse
from .exceptions import NdffException  # importing ndff.exceptions will raise a module error in the plugin


def is_uri(uri_string: str):
    # good enough test for uri? https://stackoverflow.com/questions/6718633/python-regular-expression-again-match-url
    uri_regex = re.compile(r"((https?):((//)|(\\\\))+[\w:#@%/;$()~_?+-=\\.&]*)", re.MULTILINE | re.UNICODE)
    # this one, is not good enough!!
    # uri_regex = re.compile(r"((http|https)\:\/\/)?[a-zA-Z0-9\.\/\?\:@\-_=#]+\.([a-zA-Z]){2,6}([a-zA-Z0-9\.\&\/\?\:@\-_=#])*", re.MULTILINE | re.UNICODE)

    # # extra check for valid top level domain needed for ndff (Toplevel domain is mandatory!)
    # # see: https://ndff.zendesk.com/hc/nl/requests/39562
    domain_and_path_ok = False
    try:
        parse_result = urlparse(f'{uri_string}')
        domain_part = parse_result.netloc
        path_part = parse_result.path
        # smallest domain I can imagine WITH a top-level domain part is a.nl (4 chars) https://en.wikipedia.org/wiki/Single-letter_second-level_domain
        # as we use this for .nl checks, we are not testing for .company etc. etc.
        domain_and_path_ok = len(domain_part.split('.')) >= 2 and domain_part[-1] not in ['.', ' '] and domain_part[0] not in ['.', ' '] and ' ' not in path_part

    except NdffException:
        # there can be parse exceptions etc etc
        pass

    return domain_and_path_ok and uri_regex.match(f'{uri_string}') is not None and '\n' not in uri_string and uri_string[-1] not in ['.', ' ']


def is_hex(potential_hex_string):
    """
    Test if the argument value could be a hexadecimal string

    In Postgis for example geometries are saved as hexadecimal strings (WKB)

    :param potential_hex_string:
    :return:
    """
    try:
        int(potential_hex_string, 16)
        return True
    except ValueError:
        return False


def is_numeric(potential_numeric_value):
    """
    Test if the argument value (either float, int or string) is numeric

    Created here because str.isnumeric() returns False for values like 1.0 or 1e14 so cannot not be used

    :param potential_numeric_value:
    :return: True incase the value is a number (or can be created from the string), False else
    """
    # first putting it in string here because using this method in the Plugin/Qt environment, the input
    # is potentially a QVariant.None or something like that, which returns 'Null' when casting to str
    if potential_numeric_value is None or str(potential_numeric_value).strip().upper() in ('NONE', 'FALSE', 'TRUE', 'NULL' ''):
        return False

    try:
        float(potential_numeric_value)
        return True
    except ValueError:
        return False
