"""
Formats:
    -   schemed str: scheme://ip:port or scheme://user:pass@user:pswd. Example: socks5://localhost:5556.
    -   dict: {'ip', 'port', 'scheme'} or {'ip', 'port', 'user', 'pswd', 'scheme'}. Scheme is 'http' by default.
    -   None
    Default format: dict.
"""

import re
from utils_ak import re as re_tools

# from here: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
IP_PAT = "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"
HOST_PAT = "(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])"
PORT_PAT = '(\d+)'
SCHEME_PAT = 'http|https|socks5'
USER_PAT = '[a-zA-Z0-9]+'
PSWD_PAT = '[^\s@]+'

PROXY_PAT = f'%SCHEME://(%USER:%PSWD@)?%HOST:%PORT'
PROXY_PAT = re_tools.replace_with_pattern(PROXY_PAT, '%SCHEME', 'scheme', SCHEME_PAT)
PROXY_PAT = re_tools.replace_with_pattern(PROXY_PAT, '%USER', 'user', USER_PAT)
PROXY_PAT = re_tools.replace_with_pattern(PROXY_PAT, '%PSWD', 'pswd', PSWD_PAT)
PROXY_PAT = re_tools.replace_with_pattern(PROXY_PAT, '%HOST', 'host', HOST_PAT)
PROXY_PAT = re_tools.replace_with_pattern(PROXY_PAT, '%PORT', 'port', PORT_PAT)


def cast_dict(proxy_obj):
    if proxy_obj is None:
        return None
    elif isinstance(proxy_obj, dict):
        if any(x not in proxy_obj for x in ['scheme', 'host', 'port']):
            raise ValueError('Missing some proxy keys')
        if ('user' in proxy_obj or 'pswd' in proxy_obj) and any(x not in proxy_obj for x in ['user', 'pswd']):
            raise ValueError('Missing some proxy keys')
        return proxy_obj
    elif isinstance(proxy_obj, str):
        search = re.search(PROXY_PAT, proxy_obj)
        if not search:
            raise ValueError(f'Non-valid proxy {proxy_obj}')
        return search.groupdict()
    else:
        raise ValueError('Unknown proxy type')


def cast_str(proxy_obj):
    proxy = cast_dict(proxy_obj)
    if not proxy:
        raise Exception('Cannot cast proxy string from null proxy')
    elif proxy.get('user'):
        scheme, user, pswd, host, port = proxy['scheme'], proxy['user'], proxy['pswd'], proxy['host'], proxy['port']
        return f'{scheme}://{user}:{pswd}@{host}:{port}'
    else:
        scheme, host, port = proxy['scheme'], proxy['host'], proxy['port']
        return f'{scheme}://{host}:{port}'


def cast_requests_proxies(proxy_obj):
    """
    :return:
       proxies = {
        'http': 'http://10.10.1.10:3128',
        'https': 'http://10.10.1.10:1080',
    }
    """
    if not proxy_obj:
        return None
    proxy_str = cast_str(proxy_obj)
    return {'http': proxy_str, 'https': proxy_str}


if __name__ == '__main__':
    print(cast_dict('http://ip:1234'))
    print(cast_dict({'scheme': 'http', 'host': 'host', 'port': 9999}))
    print(cast_str({'scheme': 'http', 'host': 'host', 'port': 9999, 'user': 'user', 'pswd': 'pswd'}))
    print(cast_requests_proxies('http://user:pswd@host:9999'))
