# Path: lib/usps.py
# class USPSScraper:
# 
#     USPS_BASE_URL = 'https://tools.usps.com/go/ZipLookupResultsAction!input.action'
# 
#     @staticmethod
#     def usps_request(**kwargs):
# 
#         # construct get parameters string
#         params = {
#             'resultMode': 0,
#             'companyName': '',
#             'address1': kwargs.get('street_address', ''),
#             'address2': '',
#             'city': kwargs.get('city', ''),
#             'state': kwargs.get('state',''),
#             'urbanCode': '',
#             'postalCode': '',
#             'zip': kwargs.get('zip5', '')
#         }
# 
#         # make request. need to spoof headers or get infinite redirect
#         return requests.get(USPSScraper.USPS_BASE_URL, params=params, verify=True,
#                             headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36'
#                                                    ' (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36'},
#                             timeout=5)
# 
#     @staticmethod
#     def usps_address_lookup(queue=None, **kwargs):
# 
#         # parse html
#         soup = BeautifulSoup(USPSScraper.usps_request(**kwargs).text, 'html.parser')
# 
#         # get container div for results
#         results_content = soup.find(id='results-content')
# 
#         # build return dict
#         address = {
#             'street_address': '',
#             'city': '',
#             'state': '',
#             'zip5': '',
#             'zip4': ''
#         }
# 
#         try:
#             address['street_address'] = str(results_content.find('span', class_='address1').text).strip()
#         except:
#             print("Can't find street address")
#         try:
#             address['city'] = str(results_content.find('span', class_='city').text).strip().title()
#         except:
#             print("Can't find city")
#         try:
#             address['state'] = str(results_content.find('span', class_='state').text).strip()
#         except:
#             print("Can't find state")
#         try:
#             address['zip5'] = str(results_content.find('span', class_='zip').text).strip()
#         except:
#             print("Can't find zip5")
#         try:
#             address['zip4'] = str(results_content.find('span', class_='zip4').text).strip()
#         except:
#             print("Can't find zip4")
# 
#         if queue:
#             queue.put(address)
# 
#         return address
# 
#     @staticmethod
#     def usps_zip_lookup(street_address, city, state, z5=''):
#         """
# 
#         @param street_address: street address
#         @type street_address: string
#         @param city: city
#         @type city: string
#         @param state: state
#         @type state: string
#         @param z5: zip5
#         @type z5: string
#         @return: tuple of form (zip5, zip4)
#         @rtype: tuple
#         """
# 
#         address = USPSScraper.usps_address_lookup(street_address=street_address, city=city, state=state, zip5=z5)
#         return address['zip5'], address['zip4']
# 
# Path: services/geolocation_service.py
# def geolocate(**kwargs):
#     try:
#         geo = geocoder.Geocoder('TexasAm', {'apiKey': settings.CONFIG_DICT['api_keys']['texas_am']})
#         geo.lookup(**kwargs)
#         return geo.lat_long()
#     except KeyError:
#         raise
#         # TODO robust error handling
# 
# def reverse_geolocate(lat, lng, state=''):
#     try:
#         geo = geocoder.Geocoder('TexasAm', {'apiKey': settings.CONFIG_DICT['api_keys']['texas_am']})
#         geo.reverse_lookup(lat, lng, state=state)
#         return geo.address()
#     except KeyError:
#         raise
#         # TODO robust error handling


import stopit
from lib.usps import USPSScraper
from services.geolocation_service import geolocate, reverse_geolocate



def address_lookup(**kwargs):

    try:
        with stopit.ThreadingTimeout(4) as to_ctx_mgr:
            address = USPSScraper.usps_address_lookup(**kwargs)

        if to_ctx_mgr.state == to_ctx_mgr.EXECUTED:
            return address
        else:
            lat, lng = geolocate(**kwargs)
            return reverse_geolocate(lat, lng, state=kwargs.get('state', ''))
    except:
        return None


def zip4_lookup(street_address, city, state, zip5=''):
    # First try usps lookup because it doesn't eat up geolocation credits
    try:
        zip5, zip4 = USPSScraper.usps_zip_lookup(street_address, city, state, zip5)
        if zip4 is not None:
            return zip4
    except:
        # TODO proper error handling
        print("Error scraping from USPS ... moving on to geocoding")

    # If USPS is unable to determine zip4 then use geolocation method
    try:

# Path: lib/usps.py
# class USPSScraper:
# 
#     USPS_BASE_URL = 'https://tools.usps.com/go/ZipLookupResultsAction!input.action'
# 
#     @staticmethod
#     def usps_request(**kwargs):
# 
#         # construct get parameters string
#         params = {
#             'resultMode': 0,
#             'companyName': '',
#             'address1': kwargs.get('street_address', ''),
#             'address2': '',
#             'city': kwargs.get('city', ''),
#             'state': kwargs.get('state',''),
#             'urbanCode': '',
#             'postalCode': '',
#             'zip': kwargs.get('zip5', '')
#         }
# 
#         # make request. need to spoof headers or get infinite redirect
#         return requests.get(USPSScraper.USPS_BASE_URL, params=params, verify=True,
#                             headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36'
#                                                    ' (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36'},
#                             timeout=5)
# 
#     @staticmethod
#     def usps_address_lookup(queue=None, **kwargs):
# 
#         # parse html
#         soup = BeautifulSoup(USPSScraper.usps_request(**kwargs).text, 'html.parser')
# 
#         # get container div for results
#         results_content = soup.find(id='results-content')
# 
#         # build return dict
#         address = {
#             'street_address': '',
#             'city': '',
#             'state': '',
#             'zip5': '',
#             'zip4': ''
#         }
# 
#         try:
#             address['street_address'] = str(results_content.find('span', class_='address1').text).strip()
#         except:
#             print("Can't find street address")
#         try:
#             address['city'] = str(results_content.find('span', class_='city').text).strip().title()
#         except:
#             print("Can't find city")
#         try:
#             address['state'] = str(results_content.find('span', class_='state').text).strip()
#         except:
#             print("Can't find state")
#         try:
#             address['zip5'] = str(results_content.find('span', class_='zip').text).strip()
#         except:
#             print("Can't find zip5")
#         try:
#             address['zip4'] = str(results_content.find('span', class_='zip4').text).strip()
#         except:
#             print("Can't find zip4")
# 
#         if queue:
#             queue.put(address)
# 
#         return address
# 
#     @staticmethod
#     def usps_zip_lookup(street_address, city, state, z5=''):
#         """
# 
#         @param street_address: street address
#         @type street_address: string
#         @param city: city
#         @type city: string
#         @param state: state
#         @type state: string
#         @param z5: zip5
#         @type z5: string
#         @return: tuple of form (zip5, zip4)
#         @rtype: tuple
#         """
# 
#         address = USPSScraper.usps_address_lookup(street_address=street_address, city=city, state=state, zip5=z5)
#         return address['zip5'], address['zip4']
# 
# Path: services/geolocation_service.py
# def geolocate(**kwargs):
#     try:
#         geo = geocoder.Geocoder('TexasAm', {'apiKey': settings.CONFIG_DICT['api_keys']['texas_am']})
#         geo.lookup(**kwargs)
#         return geo.lat_long()
#     except KeyError:
#         raise
#         # TODO robust error handling
# 
# def reverse_geolocate(lat, lng, state=''):
#     try:
#         geo = geocoder.Geocoder('TexasAm', {'apiKey': settings.CONFIG_DICT['api_keys']['texas_am']})
#         geo.reverse_lookup(lat, lng, state=state)
#         return geo.address()
#     except KeyError:
#         raise
#         # TODO robust error handling


# Path: services/address_inferrence_service.py
import stopit
from lib.usps import USPSScraper
from services.geolocation_service import geolocate, reverse_geolocate




def address_lookup(**kwargs):

    try:
        with stopit.ThreadingTimeout(4) as to_ctx_mgr:
            address = USPSScraper.usps_address_lookup(**kwargs)

        if to_ctx_mgr.state == to_ctx_mgr.EXECUTED:
            return address
        else:
            lat, lng = geolocate(**kwargs)
            return reverse_geolocate(lat, lng, state=kwargs.get('state', ''))
    except:
        return None


def zip4_lookup(street_address, city, state, zip5=''):
    # First try usps lookup because it doesn't eat up geolocation credits
    try:
        zip5, zip4 = USPSScraper.usps_zip_lookup(street_address, city, state, zip5)
        if zip4 is not None:
            return zip4
    except:
        # TODO proper error handling
        print("Error scraping from USPS ... moving on to geocoding")

    # If USPS is unable to determine zip4 then use geolocation method
    try:

        lat, lng = geolocate(street_address=street_address, city=city, state=state, zip5=zip5)