
import logging
import sys
import time

from publicaddr import google as PROVIDER_GOOGLE
from publicaddr import opendns as PROVIDER_OPENDNS
from publicaddr import cloudflare as PROVIDER_CLOUDFLARE
from publicaddr import akamai as PROVIDER_AKAMAI
from publicaddr import ipify as PROVIDER_IPIFY
from publicaddr import icanhazip as PROVIDER_ICANHAZIP
from publicaddr import matrix as PROVIDER_MATRIX

from publicaddr import randprov
from publicaddr import constants

from publicaddr.constants import *

loglevel = logging.CRITICAL
logging.basicConfig(format='%(levelname)s %(message)s', stream=sys.stdout, level=loglevel)
logging.getLogger("requests").setLevel(loglevel)
logging.getLogger("urllib3").setLevel(loglevel)

# register providers
randprov.set_providers([PROVIDER_GOOGLE, PROVIDER_OPENDNS, PROVIDER_IPIFY,
                        PROVIDER_CLOUDFLARE, PROVIDER_AKAMAI, PROVIDER_ICANHAZIP,
                        PROVIDER_MATRIX])

# get all public IP if exists
def lookup(providers=constants.ALL_PROVIDERS, retries=3):
    """lookup your public ipv4 and ipv6 from random providers"""
    addrs = {}

    for _ in range(retries):
        start = time.perf_counter()

        # select provider in random mode
        if providers == constants.ALL_PROVIDERS: 
            _provider = randprov.pick_all()
            if len(_provider.dns_servers) and not len(_provider.http_servers) and not len(_provider.stuns_servers): 
                _ipproto = constants.PROTO_DNS
            elif not len(_provider.dns_servers) and len(_provider.http_servers) and not len(_provider.stuns_servers): 
                _ipproto = constants.PROTO_HTTP
            elif not len(_provider.dns_servers) and not len(_provider.http_servers) and len(_provider.stuns_servers): 
                _ipproto = constants.PROTO_STUNS
            else:
                _ipproto = randprov.pick_proto()

        elif providers == constants.DNS_PROVIDERS: 
            _provider = randprov.pick_dns()
            _ipproto = constants.PROTO_DNS

        elif providers == constants.HTTP_PROVIDERS: 
            _provider = randprov.pick_http()
            _ipproto = constants.PROTO_HTTP

        elif providers == constants.STUNS_PROVIDERS: 
            _provider = randprov.pick_stuns()
            _ipproto = constants.PROTO_STUNS

        else:
            logging.error("fetch invalid providers mode - %s" % providers)
            return addrs

        # lookup for public all ip
        addrs["ip4"] = _provider.lookup(ipversion=constants.IP_V4, ipproto=_ipproto)
        addrs["ip6"] = _provider.lookup(ipversion=constants.IP_V6, ipproto=_ipproto)
        addrs["provider"] = _provider.NAME
        addrs["proto"] = constants.PROTO_STR[_ipproto]

        request_time = time.perf_counter() - start
        addrs["duration"] = "{0:.3f}".format(request_time)

        if not(addrs["ip4"] is None and addrs["ip6"] is None):
            break

    return addrs

# return from a specific provider, the IPv4 or IPv6
def get(provider=PROVIDER_GOOGLE, ipversion=constants.IP_V4, ipproto=constants.PROTO_DNS):
    """return your public ipv4 or ipv6"""
    start = time.perf_counter()
    addr = {}
    addr["ip"] = provider.lookup(ipversion=ipversion, ipproto=ipproto)
    request_time = time.perf_counter() - start
    addr["duration"] = "{0:.3f}".format(request_time)
    return addr

