"""
Extractors information from text data
~~~~~~~~~~
:copyright: 2019
:license: MIT
"""


import re


def email(value: str):
    """
    Extract email from document
    Example Result: ['<EMAIL>', '<EMAIL>']
    """
    _email_pat = r'[a-z0-9\.\-+_]+@[a-z0-9\.\-+_]+\.[a-z]+'
    return re.findall(_email_pat, value)


def url(value: str):
    """
    Extract url from document
    Example Result: ['http://www.google.com.', 'https://www.crazyvn.com']
    """
    _url_pat = r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+/?'
    return re.findall(_url_pat, value)


def base64(value: str):
    """
    Extract base64 code from document
    Example Result:
        [QQ==,
        YmFzZTY0IQ,
        YmFzZTY0IQ==]
    """
    _base64_pat = r'(?:[a-zA-Z0-9+\/]{4})+(?:[a-zA-Z0-9+\/]{3}=|[a-zA-Z0-9+\/]{2}==|[a-zA-Z0-9+\/]{1}===)?'   # pylint: disable=C0301
    return re.findall(_base64_pat, value)


def credit_card(value: str, host: str = None):
    """
    Validator for credit card number
    Example Result: {
        'Visa': 4111111111111111
        'Master Card': 5500000000000004
        'American Express': 340000000000009
        'Diner's Club': 30000000000004
        'Discover': 6011000000000004
        'JCB': 3088000000000009
    }
    Detail: https://en.wikipedia.org/wiki/Payment_card_number
    """
    _pats = {
        'Visa': r'4[0-9]{12}(?:[0-9]{3}){0,2}',
        'Master Card': r'(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}',     # pylint: disable=C0301
        'American Express': r'^3[47][0-9]{13}$',
        'Diners Club': '3(?:0[0-5]|[68][0-9])[0-9]{11}',
        'Discover': r'6(?:011|5[0-9]{2})[0-9]{12}',
        'JCB': r'(?:2131|1800|35\d{3})\d{11}'
    }
    if host is None:
        ret = {}
        for pat_name, pat in _pats.items():
            exists = re.findall(pat, value)
            if exists:
                ret[pat_name] = exists
        if ret:
            return ret
        return None
    if host in _pats:
        return re.findall(_pats[host], value)
    raise ValueError('Host {} not support'.format(host))


def datetime(value: str, fmt='normal'):
    """
    Datetime validator with 2 format
    :param fmt: str, if it set normal, validate validate dd/mm/yy
                     if it set iso-8601. validator validate time follow ISO-8601
    Example Result:
        [1985-01-02T18:04:30,
        12-05-2017 14:05:30,
        12-05-2017]
    """
    _datetime_pats = {
        'iso-8601': r'(?:-?(?:[1-9][0-9]*)?[0-9]{4})-(?:1[0-2]|0[1-9])-(?:3[01]|0[1-9]|[12][0-9])T(?:2[0-3]|[01][0-9]):(?:[0-5][0-9]):(?:[0-5][0-9])(?:\.[0-9]+)?(?:Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?',    # pylint: disable=C0301
        'normal': r'(?:0?[1-9]|[12][0-9]|3[01])(?:[ \/\- ])(?:0?[1-9]|1[012])(?:[ \/\- ])(?:[0-9][0-9][0-9][0-9])(?:(?:[ - ])(?:[0-1]?[0-9]|2[0-3]):[0-5]?[0-9]:[0-5]?[0-9])?'  # pylint: disable=C0301
    }
    if fmt is None:
        ret = []
        for _, pat in _datetime_pats.items():
            exists = re.findall(pat, value)
            if exists:
                ret += exists
        if ret:
            return ret
        return None
    if fmt not in ('normal', 'iso-8601'):
        raise ValueError('Not support format {}'.format(fmt))
    return re.findall(_datetime_pats[fmt], value)


def domain_name(value: str):
    """
    Validator for domain name
    Example Result:
        [en.wikipedia.com]
    Detail: https://en.wikipedia.org/wiki/Domain_name#Domain_name_syntax
    """
    _domain_name_pat = r'(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]'    # pylint: disable=C0301
    return re.findall(_domain_name_pat, value)


# @TODO: unsupport file path for Windows system
def filename(value: str, system='linux'):
    """
    Filename validator
    :param system: linux or None
    Example Result:
        [/etc/nginx.conf]
    """
    _pats = {
        'linux': r'(?:/[a-zA-Z0-9_-]+)+(?:[.a-zA-Z0-9_-]+)|(?:/)'
    }
    if system is None:
        ret = []
        for _, pat in _pats.items():
            exists = re.findall(pat, value)
            if exists:
                ret += exists
        if ret:
            return ret
        return None
    if system not in _pats:
        raise ValueError('OS {} not support'.format(system))
    return re.findall(_pats[system], value)


def hex_color_code(value: str):
    """
    Hex color validator
    Example Result:
        [#00ff00,
        #fff]
    """
    _hex_color_pat = r'#(?:[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})'
    return re.findall(_hex_color_pat, value)


def ipv4(value: str):
    """
    Validator IPv4 address
    Example Result:
        [0.0.0.0,
        255.255.255.255,
        192.168.3.11]
    """
    _ipv4_pat = r'(?:[\d]{1,3}\.){3}[\d]{1,3}'
    return re.findall(_ipv4_pat, value)


def ipv6(value: str):
    """
    Validator IPv6 address
    Example Result:
        [2001:0db8:0000:0000:0000:8a2e:0370:7334,
        2001:db8::8a2e:370:7334]
    Detail: https://en.wikipedia.org/wiki/IPv6
    """
    _ipv6_pat = r'(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,4}:[^\s:](?:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])))|(?:::(?:ffff(?::0{1,4}){0,1}:){0,1}[^\s:](?:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])))|(?:fe80:(?::(?:(?:[0-9a-fA-F]){1,4})){0,4}%[0-9a-zA-Z]{1,})|(?::(?:(?::(?:(?:[0-9a-fA-F]){1,4})){1,7}|:))|(?:(?:(?:[0-9a-fA-F]){1,4}):(?:(?::(?:(?:[0-9a-fA-F]){1,4})){1,6}))|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,2}(?::(?:(?:[0-9a-fA-F]){1,4})){1,5})|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,3}(?::(?:(?:[0-9a-fA-F]){1,4})){1,4})|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,4}(?::(?:(?:[0-9a-fA-F]){1,4})){1,3})|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,5}(?::(?:(?:[0-9a-fA-F]){1,4})){1,2})|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,6}:(?:(?:[0-9a-fA-F]){1,4}))|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,7}:)|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){7,7}(?:(?:[0-9a-fA-F]){1,4}))'    # pylint: disable=C0301
    return re.findall(_ipv6_pat, value)


def phone_number(value: str):
    """
    Validator phone number
    Example Result:
        [0123456789,
        +84123456789]
    """
    _phone_number = r'[\d]{10}|[\+]{1}[\d]{11}'
    return re.findall(_phone_number, value)


def sha256(value: str):
    """
    SHA-256 Validator
    Example Result:
        [b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9]
    """
    _sha256_pat = r'[A-Fa-f0-9]{64}'
    return re.findall(_sha256_pat, value)


def time(value: str):
    """
    Time validator
    Example Result:
        [13:05,
        13:05:30]
    """
    _time_pat = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?'
    return re.findall(_time_pat, value)


def uuid(value: str):
    """
    Validator for Universally unique identifier
    Example Result:
        [123e4567-e89b-12d3-a456-426655440000,
        6a2f41a3-c54c-fce8-32d2-0324e1c32e22]
    Detail: https://en.wikipedia.org/wiki/Universally_unique_identifier#Format
    """
    _uuid_pat = r'[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}'  # pylint: disable=C0301
    return re.findall(_uuid_pat, value)

"""
Extractors information from text data
~~~~~~~~~~
:copyright: 2019
:license: MIT
"""


import re


def email(value: str):
    """
    Extract email from document
    Example Result: ['<EMAIL>', '<EMAIL>']
    """
    _email_pat = r'[a-z0-9\.\-+_]+@[a-z0-9\.\-+_]+\.[a-z]+'
    return re.findall(_email_pat, value)


def url(value: str):
    """
    Extract url from document
    Example Result: ['http://www.google.com.', 'https://www.crazyvn.com']
    """
    _url_pat = r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+/?'
    return re.findall(_url_pat, value)


def base64(value: str):
    """
    Extract base64 code from document
    Example Result:
        [QQ==,
        YmFzZTY0IQ,
        YmFzZTY0IQ==]
    """
    _base64_pat = r'(?:[a-zA-Z0-9+\/]{4})+(?:[a-zA-Z0-9+\/]{3}=|[a-zA-Z0-9+\/]{2}==|[a-zA-Z0-9+\/]{1}===)?'   # pylint: disable=C0301
    return re.findall(_base64_pat, value)


def credit_card(value: str, host: str = None):
    """
    Validator for credit card number
    Example Result: {
        'Visa': 4111111111111111
        'Master Card': 5500000000000004
        'American Express': 340000000000009
        'Diner's Club': 30000000000004
        'Discover': 6011000000000004
        'JCB': 3088000000000009
    }
    Detail: https://en.wikipedia.org/wiki/Payment_card_number
    """
    _pats = {
        'Visa': r'4[0-9]{12}(?:[0-9]{3}){0,2}',
        'Master Card': r'(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}',     # pylint: disable=C0301
        'American Express': r'^3[47][0-9]{13}$',
        'Diners Club': '3(?:0[0-5]|[68][0-9])[0-9]{11}',
        'Discover': r'6(?:011|5[0-9]{2})[0-9]{12}',
        'JCB': r'(?:2131|1800|35\d{3})\d{11}'
    }
    if host is None:
        ret = {}
        for pat_name, pat in _pats.items():
            exists = re.findall(pat, value)
            if exists:
                ret[pat_name] = exists
        if ret:
            return ret
        return None
    if host in _pats:
        return re.findall(_pats[host], value)
    raise ValueError('Host {} not support'.format(host))


def datetime(value: str, fmt='normal'):
    """
    Datetime validator with 2 format
    :param fmt: str, if it set normal, validate validate dd/mm/yy
                     if it set iso-8601. validator validate time follow ISO-8601
    Example Result:
        [1985-01-02T18:04:30,
        12-05-2017 14:05:30,
        12-05-2017]
    """
    _datetime_pats = {
        'iso-8601': r'(?:-?(?:[1-9][0-9]*)?[0-9]{4})-(?:1[0-2]|0[1-9])-(?:3[01]|0[1-9]|[12][0-9])T(?:2[0-3]|[01][0-9]):(?:[0-5][0-9]):(?:[0-5][0-9])(?:\.[0-9]+)?(?:Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?',    # pylint: disable=C0301
        'normal': r'(?:0?[1-9]|[12][0-9]|3[01])(?:[ \/\- ])(?:0?[1-9]|1[012])(?:[ \/\- ])(?:[0-9][0-9][0-9][0-9])(?:(?:[ - ])(?:[0-1]?[0-9]|2[0-3]):[0-5]?[0-9]:[0-5]?[0-9])?'  # pylint: disable=C0301
    }
    if fmt is None:
        ret = []
        for _, pat in _datetime_pats.items():
            exists = re.findall(pat, value)
            if exists:
                ret += exists
        if ret:
            return ret
        return None
    if fmt not in ('normal', 'iso-8601'):
        raise ValueError('Not support format {}'.format(fmt))
    return re.findall(_datetime_pats[fmt], value)


def domain_name(value: str):
    """
    Validator for domain name
    Example Result:
        [en.wikipedia.com]
    Detail: https://en.wikipedia.org/wiki/Domain_name#Domain_name_syntax
    """
    _domain_name_pat = r'(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]'    # pylint: disable=C0301
    return re.findall(_domain_name_pat, value)


# @TODO: unsupport file path for Windows system
def filename(value: str, system='linux'):
    """
    Filename validator
    :param system: linux or None
    Example Result:
        [/etc/nginx.conf]
    """
    _pats = {
        'linux': r'(?:/[a-zA-Z0-9_-]+)+(?:[.a-zA-Z0-9_-]+)|(?:/)'
    }
    if system is None:
        ret = []
        for _, pat in _pats.items():
            exists = re.findall(pat, value)
            if exists:
                ret += exists
        if ret:
            return ret
        return None
    if system not in _pats:
        raise ValueError('OS {} not support'.format(system))
    return re.findall(_pats[system], value)


def hex_color_code(value: str):
    """
    Hex color validator
    Example Result:
        [#00ff00,
        #fff]
    """
    _hex_color_pat = r'#(?:[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})'
    return re.findall(_hex_color_pat, value)


def ipv4(value: str):
    """
    Validator IPv4 address
    Example Result:
        [0.0.0.0,
        255.255.255.255,
        192.168.3.11]
    """
    _ipv4_pat = r'(?:[\d]{1,3}\.){3}[\d]{1,3}'
    return re.findall(_ipv4_pat, value)


def ipv6(value: str):
    """
    Validator IPv6 address
    Example Result:
        [2001:0db8:0000:0000:0000:8a2e:0370:7334,
        2001:db8::8a2e:370:7334]
    Detail: https://en.wikipedia.org/wiki/IPv6
    """
    _ipv6_pat = r'(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,4}:[^\s:](?:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])))|(?:::(?:ffff(?::0{1,4}){0,1}:){0,1}[^\s:](?:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])))|(?:fe80:(?::(?:(?:[0-9a-fA-F]){1,4})){0,4}%[0-9a-zA-Z]{1,})|(?::(?:(?::(?:(?:[0-9a-fA-F]){1,4})){1,7}|:))|(?:(?:(?:[0-9a-fA-F]){1,4}):(?:(?::(?:(?:[0-9a-fA-F]){1,4})){1,6}))|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,2}(?::(?:(?:[0-9a-fA-F]){1,4})){1,5})|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,3}(?::(?:(?:[0-9a-fA-F]){1,4})){1,4})|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,4}(?::(?:(?:[0-9a-fA-F]){1,4})){1,3})|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,5}(?::(?:(?:[0-9a-fA-F]){1,4})){1,2})|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,6}:(?:(?:[0-9a-fA-F]){1,4}))|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){1,7}:)|(?:(?:(?:(?:[0-9a-fA-F]){1,4}):){7,7}(?:(?:[0-9a-fA-F]){1,4}))'    # pylint: disable=C0301
    return re.findall(_ipv6_pat, value)


def phone_number(value: str):
    """
    Validator phone number
    Example Result:
        [0123456789,
        +84123456789]
    """
    _phone_number = r'[\d]{10}|[\+]{1}[\d]{11}'
    return re.findall(_phone_number, value)


def sha256(value: str):
    """
    SHA-256 Validator
    Example Result:
        [b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9]
    """
    _sha256_pat = r'[A-Fa-f0-9]{64}'
    return re.findall(_sha256_pat, value)


def time(value: str):
    """
    Time validator
    Example Result:
        [13:05,
        13:05:30]
    """
    _time_pat = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?'
    return re.findall(_time_pat, value)


def uuid(value: str):
    """
    Validator for Universally unique identifier
    Example Result:
        [123e4567-e89b-12d3-a456-426655440000,
        6a2f41a3-c54c-fce8-32d2-0324e1c32e22]
    Detail: https://en.wikipedia.org/wiki/Universally_unique_identifier#Format
    """
    _uuid_pat = r'[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}'  # pylint: disable=C0301
    return re.findall(_uuid_pat, value)

Extractors information from text data ~~~~~~~~~~ :copyright: 2019 :license: MIT Extract email from document     Example Result: ['<EMAIL>', '<EMAIL>'] Extract url from document     Example Result: ['http://www.google.com.', 'https://www.crazyvn.com'] Extract base64 code from document     Example Result:         [QQ==,         YmFzZTY0IQ,         YmFzZTY0IQ==] # pylint: disable=C0301 Validator for credit card number     Example Result: {         'Visa': 4111111111111111         'Master Card': 5500000000000004         'American Express': 340000000000009         'Diner's Club': 30000000000004         'Discover': 6011000000000004         'JCB': 3088000000000009     }     Detail: https://en.wikipedia.org/wiki/Payment_card_number # pylint: disable=C0301 Datetime validator with 2 format     :param fmt: str, if it set normal, validate validate dd/mm/yy                      if it set iso-8601. validator validate time follow ISO-8601     Example Result:         [1985-01-02T18:04:30,         12-05-2017 14:05:30,         12-05-2017] # pylint: disable=C0301 # pylint: disable=C0301 Validator for domain name     Example Result:         [en.wikipedia.com]     Detail: https://en.wikipedia.org/wiki/Domain_name#Domain_name_syntax # pylint: disable=C0301 # @TODO: unsupport file path for Windows system Filename validator     :param system: linux or None     Example Result:         [/etc/nginx.conf] Hex color validator     Example Result:         [#00ff00,         #fff] Validator IPv4 address     Example Result:         [0.0.0.0,         255.255.255.255,         192.168.3.11] Validator IPv6 address     Example Result:         [2001:0db8:0000:0000:0000:8a2e:0370:7334,         2001:db8::8a2e:370:7334]     Detail: https://en.wikipedia.org/wiki/IPv6 # pylint: disable=C0301 Validator phone number     Example Result:         [0123456789,         +84123456789] SHA-256 Validator     Example Result:         [b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9] Time validator     Example Result:         [13:05,         13:05:30] Validator for Universally unique identifier     Example Result:         [123e4567-e89b-12d3-a456-426655440000,         6a2f41a3-c54c-fce8-32d2-0324e1c32e22]     Detail: https://en.wikipedia.org/wiki/Universally_unique_identifier#Format # pylint: disable=C0301