import arrow
import psycopg2.extras
import logging as log

from elasticsearch import Elasticsearch


def _sanitize_timestamp(message):
    """
    Coverts timestamp to utc timezone
    :param message: Message with a timestamp field
    """
    log.debug("Entering {}".format(_sanitize_timestamp.__name__))

    # Coverts UTC offsets to +00:00
    message['_source']['Local_Timestamp'] = arrow.get(message['_source']['Local_Timestamp']).to('utc').datetime
    log.debug("Exiting {}".format(_sanitize_timestamp.__name__))


def _catch_helper_geoip(message, parm):
    """
    Catches errors when accessing geo IP data
    :param message: Dictionary of information retrieved from Elastic Search
    :param parm: The key that you are searching for in the dictionary
    :return: Returns Value(String) or None if the key is not found
    """

    log.debug("Entering " + _catch_helper_geoip.__name__)

    # Looks at the geoip data part of the dictionary only
    geodata = message['_source']['geoip']

    # If the parameter is in geodata then return it or return None if not
    if parm in geodata:
        log.debug("The parameter {} has a value of {}".format(parm, geodata[parm]))
        log.debug("Exiting " + _catch_helper_geoip.__name__)
        return geodata[parm]
    else:
        log.debug("Could not find the parameter {}".format(parm))
        log.debug("Exiting " + _catch_helper_geoip.__name__)
        return None


def _catch_helper(message, parm):
    """
    Catches errors when accessing dictionary
    :param message: Dictionary of information retrieved from Elastic Search
    :param parm: The key that you are searching for in the dictionary
    :return: Returns Value(String) or None if the key is not found
    """

    log.debug("Entering " + _catch_helper.__name__)

    # Looks at the _source part of the dictionary only
    temp_data = message['_source']

    # If the parameter is in _source then return it or return None if not
    if parm in temp_data:
        log.debug("The parameter {} has a value of {}".format(parm, temp_data[parm]))
        log.debug("Exiting " + _catch_helper.__name__)
        return temp_data[parm]
    else:
        log.debug("Could not find the parameter {}".format(parm) + parm)
        log.debug("Exiting " + _catch_helper.__name__)
        return None


def _make_terms(id_):
    """
    Makes json objects for Elastic Search query
    :param id_: An ID of a message in Elastic Search
    :return: JSON object
    """

    log.debug("Entering " + _make_terms.__name__)

    log.debug("Make Term for ID: {}".format(id_))
    # Makes a json object needed to remove repeat IDs in pull
    log.debug("Exiting " + _make_terms.__name__)

    return {"term": {"_id": id_}}


def _connection_to_elasticsearch(address, username, password):
    """
    Makes a connection to Elastic Search and returns said connection
    :param address: IP address of the Elastic Search database
    :param username: Username of the Elastic Search database
    :param password: Password of the Elastic Search database
    :return: Elastic Search connection object
    """

    log.debug("Entering " + _connection_to_elasticsearch.__name__)

    # Creates a Elastic Search connection
    response = Elasticsearch([address], http_auth=(username, password))
    log.info("Elastic Search connection to {} has been established".format(address))

    log.debug("Exiting " + _connection_to_elasticsearch.__name__)
    return response


def _connection_to_postgres(dbname, address, username, password):
    """
    Makes a connection to posgtres and returns saud connection
    :param dbname: Database name your trying to connect too
    :param address: IP address of the postgres database
    :param username: Username of the posgtres database
    :param password: Password of the postgres database
    :return: Postgres connection object
    """

    log.debug("Entering " + _connection_to_postgres.__name__)

    # Creates a Postgres connection
    response = psycopg2.connect("dbname=" + dbname + " user=" + username + " host=" + address + " password=" + password)
    log.debug("Postgres connection to {} has been established".format(address))

    log.debug("Exiting " + _connection_to_postgres.__name__)
    return response


def _update_connection(message, db_connection, connection_id):
    """
    Updates the connection entry in the databases with the disconnect information
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    :param connection_id: ID of the connection that needs to be updated
    """

    log.debug("Entering " + _update_connection.__name__)

    # Query that updates the end_time and reason by connection ID since a disconnect messages has been processed
    log.debug("Query Info for {} {{End_Time: {} Reason: {} Connection_ID: {}}}".format(message["_id"],
              message['_source']['Local_Timestamp'], message['_source']['Reason'], connection_id))

    cursor = db_connection.cursor()
    cursor.execute(
        'UPDATE Connections SET end_time = %s, reason = %s WHERE connection_id = %s',
        (message['_source']['Local_Timestamp'],
         message['_source']['Reason'],
         connection_id)
    )

    # Commit Changes
    db_connection.commit()
    log.debug("Changes committed to the database")
    cursor.close()

    log.debug("Exiting " + _update_connection.__name__)


def _add_connection(message, attack_id, db_connection):
    """
    Creates a connection entry in the database with information from Elastic Search message
    :param message: Dictionary of information retrieved from Elastic Search
    :param attack_id: Attack ID associated with the connection 
    :param db_connection: Database Connection object
    """

    log.debug("Entering " + _add_connection.__name__)

    # Query that insets a connection entry into the database using information from elastic search message
    log.debug("Query Info  for {} {{Client_Software: {} Client_Protocol: {} Session_ID: {} Port: {} IP: {} "
              "Start_Time {} Attack_ID: {}}}".format(message["_id"], message['_source']['Client_Software'],
              message['_source']['Client_Protocol'], message['_source']['Session_ID'], message['_source']['Des_Port'],
              message['_source']['Attacker_IP'], message['_source']['Local_Timestamp'], attack_id))

    cursor = db_connection.cursor()
    cursor.execute(
        'INSERT INTO Connections (client_software, client_protocol, session_id, port, ip, start_time, attack_id) '
        'VALUES (%s, %s, %s, %s, %s, %s, %s)',
        (message['_source']['Client_Software'],
         message['_source']['Client_Protocol'],
         message['_source']['Session_ID'],
         message['_source']['Des_Port'],
         message['_source']['Attacker_IP'],
         message['_source']['Local_Timestamp'],
         attack_id)
    )

    # Commit entry
    db_connection.commit()
    log.debug("Entry committed to the database")
    cursor.close()

    log.debug("Exiting " + _add_connection.__name__)


def _add_attack(message, db_connection):
    """
    Creating an attack entry in the database based off of information from the Elastic Search message
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database connection object
    """

    log.debug("Entering " + _add_attack.__name__)

    # Query that inserts the attack entry. Will need to adjust default values with threads or some other method
    log.debug("Query Info for {} {{Attack Level: {} Attack_Type: {} Confidence_Level: {} HPID: {}}}".format(
              message["_id"], "Low", "Low", 0, message['_source']['HPID']))

    cursor = db_connection.cursor()
    cursor.execute(
        'INSERT INTO Attacks (attack_level, attack_type, confidence_level, honeypot_id) VALUES  (%s, %s, %s, %s)',
        ('Low', 'Low', 0, message['_source']['HPID'])
    )

    # Commit entry
    db_connection.commit()
    log.debug("Entry committed to the database")
    cursor.close()

    log.debug("Exiting " + _add_attack.__name__)


def _add_attempt(message, db_connection, connection_id):
    """
    Creating an attempt entry in the database based off of information from Elastic Search message
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    :param connection_id: Connection ID associated with the attempt
    """

    log.debug("Entering " + _add_attempt.__name__)

    # Query that inserts an attempt using information from elastic search and the passed in connection ID
    log.debug("Query Info for {} {{Username: {} Password: {} Timestamp: {} Connection_ID: {}}}".format(
              message["_id"], _catch_helper(message, 'Username'), _catch_helper(message, 'Password'),
              message['_source']['Local_Timestamp'], connection_id))

    cursor = db_connection.cursor()
    cursor.execute(
        'INSERT INTO Attempts (username, password, timestamp, connection_id) VALUES (%s, %s, %s, %s)',
        (_catch_helper(message, 'Username'),
         _catch_helper(message, 'Password'),
         message['_source']['Local_Timestamp'],
         connection_id)
    )

    # Commit entry
    db_connection.commit()
    log.debug("Entry committed to the database")
    cursor.close()

    log.debug("Exiting " + _add_attempt.__name__)


def _add_geolocation(message, db_connection):
    """
    Creates a Geo_Location entry in the database using information from the Elastic Search message
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    """

    log.debug("Entering " + _add_geolocation.__name__)

    # Query that inserts Geo_location data. Only happens if the latlong data is not there. STR is called to make sure
    # latitude and longitude get inserted correctly
    log.debug("Query Info for {} {{Latitude: {} Longitude: {} City: {} Continent_Code: {} Country: {} Postal_Code: {} "
              "Region: {} Timezone: {}}}".format(message["_id"], message['_source']['geoip']['latitude'],
              message['_source']['geoip']['longitude'], _catch_helper_geoip(message, 'city_name'),
              _catch_helper_geoip(message, 'continent_code'), _catch_helper_geoip(message, 'country_name'),
              _catch_helper_geoip(message, 'postal_code'), _catch_helper_geoip(message, 'region_name'),
              _catch_helper_geoip(message, 'timezone')))

    cursor = db_connection.cursor()
    cursor.execute(
        'INSERT INTO Geo_Location (latLong, city, continent_code, country, postal_code, region, time_zone) VALUES (ST_GeomFromText(%s, 4326), %s, %s, %s, %s, %s, %s)',
        (("POINT( " + str(message['_source']['geoip']['latitude']) + " " + str(
            message['_source']['geoip']['longitude']) + " )"),
         _catch_helper_geoip(message, 'city_name'),
         _catch_helper_geoip(message, 'continent_code'),
         _catch_helper_geoip(message, 'country_name'),
         _catch_helper_geoip(message, 'postal_code'),
         _catch_helper_geoip(message, 'region_name'),
         _catch_helper_geoip(message, 'timezone'))
    )

    # Commit entry
    db_connection.commit()
    log.debug("Entry committed to the database")
    cursor.close()

    log.debug("Exiting " + _add_geolocation.__name__)


def _add_ip_data(message, db_connection):
    """
    Creates an IP_Data entry in the database using information from Elastic Search message
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    """

    log.debug("Entering " + _add_ip_data.__name__)

    # Query that inserts a connection between IP and latitude and longitude. Again need to call STR.
    log.debug("Query Info for {} {{IP: {} Latitude: {} Longitude: {} Create_Time: {}}}".format(message["_id"],
              message['_source']['Attacker_IP'], message['_source']['geoip']['latitude'],
              message['_source']['geoip']['longitude'], message['_source']['Local_Timestamp']))

    cursor = db_connection.cursor()
    cursor.execute(
        'INSERT INTO IP_Data (ip, latLong, create_time) VALUES (%s, ST_GeomFromText(%s, 4326), %s)',
        (message['_source']['Attacker_IP'],
         ("POINT( " + str(message['_source']['geoip']['latitude']) + " " + str(message['_source']['geoip']['longitude']) + " )"),
         message['_source']['Local_Timestamp'])
    )

    # Commit entry
    db_connection.commit()
    log.debug("Entry committed to the database")
    cursor.close()

    log.debug("Exiting " + _add_ip_data.__name__)


def _check_attack_entry(message, db_connection):
    """
    Checks if there is an attack ID, so you can associated with a connection 
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    :return: Attack ID(Int) or None
    """

    log.debug("Entering " + _check_attack_entry.__name__)

    # Searches for attack ID that happened in the last 3 mins. If any longer and new attack entry is generated.
    log.debug("Query Info for {} {{IP: {} Timestamp: {} HPID: {}}}".format(message["_id"],
              message['_source']['Attacker_IP'], message['_source']['Local_Timestamp'], message['_source']['HPID']))

    cursor = db_connection.cursor(cursor_factory=psycopg2.extras.DictCursor)
    cursor.execute(
        'SELECT attack_id FROM all_timestamps WHERE ip = %s AND timestamp >= %s::TIMESTAMPTZ - INTERVAL \'3 minutes\' '
        'AND honeypot_id = %s ORDER BY timestamp DESC limit 1',
        (message['_source']['Attacker_IP'],
         message['_source']['Local_Timestamp'],
         message['_source']['HPID'])
    )

    # Row holds the attack ID
    row = cursor.fetchone()
    cursor.close()

    # Return attack ID if row is set if not return None
    if row:
        log.debug("Attack ID: {}".format(row["attack_id"]))

        log.debug("Exiting " + _check_attack_entry.__name__)
        return row['attack_id']
    else:
        log.debug("No Attack ID was found!")

        log.debug("Exiting " + _check_attack_entry.__name__)
        return None


def _check_lat_long_message(message):
    """
    Checks if there is latitude and longitude data in the Elastic Search message
    :param message: Dictionary of information retrieved from Elastic Search
    :return: True or False
    """

    log.debug("Entering " + _check_lat_long_message.__name__)

    # If latitude and longitude are set then we can do processing on geolocation data
    if _catch_helper_geoip(message, 'latitude') and _catch_helper_geoip(message, 'longitude'):
        log.debug("Latitude and Longitude found!")

        log.debug("Exiting " + _check_lat_long_message.__name__)
        return True
    else:
        log.warning("No Geolocation data for {} on message {}".format(
                    message['_source']['Attacker_IP'],  message['_id']))

        log.debug("Exiting " + _check_lat_long_message.__name__)
        return False


def _check_lat_long_db(message, db_connection):
    """
    Check if there is an entry for the latitude and longitude in the database for a given IP
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    :return: True/False
    """

    log.debug("Entering " + _check_lat_long_db.__name__)

    # Searches the database to see if there is geolocation data for an IP
    log.debug("Query Info for {} {{IP: {} Latitude: {} Longitude: {}}}".format(message["_id"],
              message['_source']['Attacker_IP'], message['_source']['geoip']['latitude'],
              message['_source']['geoip']['longitude']))

    cursor = db_connection.cursor(cursor_factory=psycopg2.extras.DictCursor)
    cursor.execute(
        'SELECT IP FROM ip_data_latlong WHERE ip = %s AND lat = %s AND long = %s',
        (message['_source']['Attacker_IP'],
         message['_source']['geoip']['latitude'],
         message['_source']['geoip']['longitude'])
    )

    # Row holds an IP
    row = cursor.fetchone()
    cursor.close()

    # Return the true if row is set or return None if not
    if row:
        log.debug("Found latitude and longitude data for ".format(row['ip']))

        log.debug("Exiting " + _check_lat_long_db.__name__)
        return True
    else:
        log.debug("Did not find latitude and longitude data for " + message['_source']['Attacker_IP'] + "!")

        log.debug("Exiting " + _check_lat_long_db.__name__)
        return False


def _check_geolocation_data(message, db_connection):
    """
    Checks if there is a entry for a latitude and longitude in database
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    :return: True or False
    """

    log.debug("Entering " + _check_geolocation_data.__name__)

    # Searches the database for a lat_long entry for the given lat and long. Used to determine if we need to insert more
    # information
    log.debug("Query Info for {} {{Latitude: {} Longitude: {}}}".format(message['_id'],
              message['_source']['geoip']['latitude'], message['_source']['geoip']['longitude']))

    cursor = db_connection.cursor(cursor_factory=psycopg2.extras.DictCursor)
    cursor.execute(
        'SELECT latLong FROM geo_location where latLong = ST_GeomFromText(%s, 4326)',
        (("POINT( " + str(message['_source']['geoip']['latitude']) + " " + str(
            message['_source']['geoip']['longitude']) + " )"),)
    )

    # Row holds an Lat_Long Point, special Postgist type
    row = cursor.fetchone()
    cursor.close()

    # Return the true if row is set or return None if not
    if row:
        log.debug("Found latLong entry for latitude {} and longitude {})".format(
                  message['_source']['geoip']['latitude'], message['_source']['geoip']['longitude']))

        log.debug("Exiting " + _check_geolocation_data.__name__)
        return True
    else:
        log.debug("No entry for latitude {} and longitude {})".format(
            message['_source']['geoip']['latitude'], message['_source']['geoip']['longitude']))

        log.debug("Exiting " + _check_geolocation_data.__name__)
        return False


def _get_latest_attack_id(db_connection):
    """
    Retrieves the latest attack from the database 
    :param db_connection: Database Connection object
    :return: Attack ID
    """

    log.debug("Entering " + _get_latest_attack_id.__name__)

    # Grabbing the latest attack id by sorting by desc
    cursor = db_connection.cursor(cursor_factory=psycopg2.extras.DictCursor)
    cursor.execute(
        'SELECT attack_id FROM attacks ORDER BY attack_id DESC limit 1'
    )

    # Row holds the attack_id
    row = cursor.fetchone()
    cursor.close()

    log.debug("Latest Attack ID: {}".format(row['attack_id']))

    log.debug("Exiting " + _get_latest_attack_id.__name__)
    return row['attack_id']


def _get_connection_id(message, db_connection):
    """
    Retrieves a connection ID based on information from the Elastic Search message
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    :return: Connection ID
    """

    log.debug("Entering " + _get_connection_id.__name__)

    # Gets the connection ID that should be linked with connection and disconnect messages
    log.debug("Query Info for {} {{HPID: {} IP: {} Session_ID: {}}}".format(message["_id"], message['_source']['HPID'],
              message['_source']['Attacker_IP'], message['_source']['Session_ID']))

    cursor = db_connection.cursor(cursor_factory=psycopg2.extras.DictCursor)
    cursor.execute(
        'SELECT connection_id FROM attacks_connections WHERE honeypot_id = %s AND ip = %s AND end_time is NULL AND '
        'session_id = %s ORDER BY start_time DESC limit 1',
        (message['_source']['HPID'],
         message['_source']['Attacker_IP'],
         message['_source']['Session_ID'])
    )

    # Row holds connection ID
    row = cursor.fetchone()
    cursor.close()

    try:
        log.debug("Connection ID: {}".format(row['connection_id']))
    except TypeError:
        # If this error is thrown something is very wrong messages.....
        log.error("Could not find connection ID, attempt message might be after disconnect message!")
        raise

    log.debug("Entering " + _get_connection_id.__name__)
    return row['connection_id']


def _get_repeat_messages(messages):
    """
    Generates a list of repeat IDs
    :param messages: The data retrieved from Elastic Search 
    :return: List of repeat IDs
    """

    log.debug("Entering " + _get_repeat_messages.__name__)

    repeats = []

    timestamp = messages[len(messages) - 1]['_source']['@timestamp']
    log.info("Latest timestamp of pull is {}".format(timestamp))

    for message in reversed(messages):
        if message['_source']['@timestamp'] == timestamp:
            log.debug("Repeat ID found: {}".format(message["_id"]))
            repeats.append(message["_id"])
        else:
            break

    log.debug("Exiting " + _get_repeat_messages.__name__)
    return repeats


def _get_repeat_messages_failed_pull(messages, _id, timestamp):
    """
    Generates a list of repeat IDs
    :param messages: The data retrieved from Elastic Search 
    :return: List of repeat IDs
    """

    log.debug("Entering " + _get_repeat_messages_failed_pull.__name__)

    repeats = []
    check = False

    for message in reversed(messages):
        if message['_id'] == _id and message['_source']['@timestamp'] == timestamp:
            check = True
        else:
            if check:
                if message['_source']['@timestamp'] == timestamp:
                    log.debug("Repeat ID found: {}".format(message["_id"]))
                    repeats.append(message["_id"])
                else:
                    break

    log.debug("Exiting " + _get_repeat_messages_failed_pull.__name__)
    return repeats


def _get_latest_timestamp(messages):
    """
    Gets the latest timestamp out of data retrieved from Elastic Search
    :param messages: The data retrieved from Elastic Search 
    :return: Latest Timestamp(timestamp)
    """

    log.debug("Entering " + _get_latest_timestamp.__name__)

    latest_timestamp = messages[len(messages) - 1]['_source']['@timestamp']
    log.info("Latest timestamp of pull is {}".format(latest_timestamp))

    log.debug("Exiting " + _get_latest_timestamp.__name__)
    return latest_timestamp


def _process_geolocation(message, db_connection, geo_cached_ips):
    """
    Process geolocation messages 
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    :param geo_cached_ips: Lists if IPs that have been processed during the pull
    """

    log.debug("Entering " + _process_geolocation.__name__)

    log.debug("Adding {} to Geo_cached IPs".format(message['_source']['Attacker_IP']))
    geo_cached_ips.append(message['_source']['Attacker_IP'])

    log.debug("Calling {}".format(_check_lat_long_db.__name__))
    if not _check_lat_long_db(message, db_connection):
        log.debug("Called {}".format(_check_lat_long_db.__name__))
        log.debug("Could not find latitude and longitude are not in database!")

        log.debug("Calling {}".format(_check_geolocation_data.__name__))
        if not _check_geolocation_data(message, db_connection):
            log.debug("Called {}".format(_check_geolocation_data.__name__))
            log.debug("Could not find geolocation data is not in the database!")

            log.debug("Calling {}".format(_add_geolocation.__name__))
            _add_geolocation(message, db_connection)
            log.debug("Called {}".format(_add_geolocation.__name__))
        else:
            log.debug("Called {}".format(_check_geolocation_data.__name__))
            log.debug("Geolocation data is in the database")

        log.debug("Calling {}".format(_add_ip_data.__name__))
        _add_ip_data(message, db_connection)
        log.debug("Called {}".format(_add_ip_data.__name__))
    else:
        log.debug("Called {}".format(_check_lat_long_db.__name__))
        log.debug("Latitude and Longitude are in the database")

    log.debug("Exiting " + _process_geolocation.__name__)


def _process_connection_message(message, db_connection):
    """
    Processes connection messages
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    """

    log.debug("Entering " + _process_connection_message.__name__)

    log.debug("Calling {}".format(_check_attack_entry.__name__))
    result = _check_attack_entry(message, db_connection)
    log.debug("Called {}".format(_check_attack_entry.__name__))

    # Check if the result is set to a attack_id
    if result:
        log.debug("Calling {}".format(_add_connection.__name__))
        _add_connection(message, result, db_connection)
        log.debug("Called {}".format(_add_connection.__name__))
    else:
        log.debug("No attack id found!")
        log.debug("Calling {}".format(_add_attack.__name__))
        _add_attack(message, db_connection)
        log.debug("Called {}".format(_add_attack.__name__))

        log.debug("Calling {}".format(_get_latest_attack_id.__name__))
        attack_id = _get_latest_attack_id(db_connection)
        log.debug("Called {}".format(_get_latest_attack_id.__name__))

        log.debug("Calling {}".format(_add_connection.__name__))
        _add_connection(message, attack_id, db_connection)
        log.debug("Called {}".format(_add_connection.__name__))

    log.debug("Exiting " + _process_connection_message.__name__)


def _process_attempt_message(message, db_connection, geo_cached_ips):
    """
    Processes attempt messages
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    :param geo_cached_ips: Lists if IPs that have been processed during the pull
    """

    log.debug("Entering " + _process_attempt_message.__name__)

    log.debug("Calling {}".format(_get_connection_id.__name__))
    connection_id = _get_connection_id(message, db_connection)
    log.debug("Called {}".format(_get_connection_id.__name__))

    # Check if connection
    if connection_id:
        log.debug("Calling {}".format(_add_attempt.__name__))
        _add_attempt(message, db_connection, connection_id)
        log.debug("Called {}".format(_add_attempt.__name__))

        log.debug("Checking if message has Geolocation Data and is not in Geo_cached IPs")
        if _check_lat_long_message(message) and message['_source']['Attacker_IP'] not in geo_cached_ips:
            log.debug("Check successful")

            log.debug("Calling {}".format(_process_geolocation.__name__))
            _process_geolocation(message, db_connection, geo_cached_ips)
            log.debug("Called {}".format(_process_geolocation.__name__))
        else:
            log.debug("Check unsuccessful!")
    else:
        # Need to repull messages and try to fix it
        log.error("No Connection ID found when processing {}".format(message["_id"]))

    log.debug("Exiting " + _process_attempt_message.__name__)


def _process_disconnect_message(message, db_connection):
    """
    Processes disconnect messages
    :param message: Dictionary of information retrieved from Elastic Search
    :param db_connection: Database Connection object
    """

    log.debug("Entering " + _process_disconnect_message.__name__)

    log.debug("Calling {}".format(_get_connection_id.__name__))
    connection_id = _get_connection_id(message, db_connection)
    log.debug("Called {}".format(_get_connection_id.__name__))

    if connection_id:
        log.debug("Calling {}".format(_update_connection.__name__))
        _update_connection(message, db_connection, connection_id)
        log.debug("Called {}".format(_update_connection.__name__))
    else:
        # Need to repull messages and try to fix it
        log.error("No Connection ID found when processing {}".format(message["_id"]))

    log.debug("Exiting " + _process_disconnect_message.__name__)
