# Path: app/cache.py
# class RedisClusterCache(object):
#     """
#     Class used for accessing the redis cluster used for caching.
#     """
#     def __init__(self):
#         self.client = self._create_client()
# 
#     def _create_client(self):
#         """
#         Function to connect to the redis cluster and init the client.
#         """
#         server_list = settings.REDIS_SERVER_LIST.replace(' ', '').split(',')
# 
#         nodes = []
#         for server in server_list:
#             if ':' not in server:
#                 continue
#             host, port = server.split(':')
#             nodes.append({'host': host, 'port': port})
# 
#         return StrictRedisCluster(startup_nodes=nodes, decode_responses=True)
# 
#     def get(self, key):
#         return self.client.get(key)
# 
#     def exists(self, key):
#         return self.client.exists(key)
# 
#     def set(self, key, value, timeout=DEFAULT_TIMEOUT):
#         self.client.set(key, value, timeout)
# 
# Path: app/utils.py
# def log_middleware_information(log_statement, dict_with_variables, log_level, device=None):
#     """
#     Function that handles the logging for the middleware.
# 
#     Args:
#         log_statement (str): The message to log.
#         dict_with_variables (OrderedDict): OrderedDict that contains the
#             variables we want to insert into the logging statement.
#         log_level (int): The level on which to log.
#         device (Device): The device for which we want to log to Logentries.
#     """
#     remote_logging_id = device.remote_logging_id if device and device.remote_logging_id else 'No remote logging ID'
#     django_log_statement = fill_log_statement(log_statement, dict_with_variables)
#     django_logger.log(log_level, '{0} - middleware - {1}'.format(remote_logging_id, django_log_statement))
# 
#     if device and device.remote_logging_id:
#         log_statement = fill_log_statement(log_statement, dict_with_variables, anonymize=True)
#         logentries_token = device.app.logentries_token
#         log_to_logentries(log_statement, log_level, logentries_token, device, remote_logging_id)
#         if device.app.partner_logentries_token:
#             # Log to the Logentries environment of the partner with a different token.
#             logentries_token = device.app.partner_logentries_token
#             log_to_logentries(log_statement, log_level, logentries_token, device, remote_logging_id)
# 
# Path: app/models.py
# ANDROID_PLATFORM = 'android'
# 
# APNS_PLATFORM = 'apns'
# 
# GCM_PLATFORM = 'gcm'


from collections import OrderedDict
from time import time
from urllib.parse import urljoin
from apns2.client import APNsClient
from apns2.errors import APNsException, BadDeviceToken, DeviceTokenNotForTopic, Unregistered
from apns2.payload import Payload
from django.conf import settings
from gcm.gcm import GCM, GCMAuthenticationException
from pyfcm import FCMNotification
from pyfcm.errors import AuthenticationError, FCMServerError, InternalPackageError
from app.cache import RedisClusterCache
from app.utils import log_middleware_information
from .models import ANDROID_PLATFORM, APNS_PLATFORM, GCM_PLATFORM
import datetime
import logging
import os
        )


# The APNSv2 connection pool. The connection pool stores an APNSv2 connection for
# an app_id. This connection pool can be shared among multiple threads.
# Don't use the connection pool directly but use `get_apns2_connection`.
apns2_connection_pool = {}


def get_apns2_connection(app, device, unique_key):
    """
    Get the active APNSv2 connection for the device.

    This returns a reference to the connection object
    for the app and with the device's sandbox setting.
    Initializes the connection if it was not yet set up.

    Args:
        app (App): App requesting the connection.
        device (Device): Device requesting the connection.
        unique_key (str): Unique key used for logging.

    Returns:
        APNsClient.
    """
    global apns2_connection_pool
    key = (app.app_id, device.sandbox)
    if key not in apns2_connection_pool:
        full_cert_path = os.path.join(settings.CERT_DIR, app.push_key)
        apns2_connection = APNsClient(full_cert_path, use_sandbox=device.sandbox)

# Path: app/cache.py
# class RedisClusterCache(object):
#     """
#     Class used for accessing the redis cluster used for caching.
#     """
#     def __init__(self):
#         self.client = self._create_client()
# 
#     def _create_client(self):
#         """
#         Function to connect to the redis cluster and init the client.
#         """
#         server_list = settings.REDIS_SERVER_LIST.replace(' ', '').split(',')
# 
#         nodes = []
#         for server in server_list:
#             if ':' not in server:
#                 continue
#             host, port = server.split(':')
#             nodes.append({'host': host, 'port': port})
# 
#         return StrictRedisCluster(startup_nodes=nodes, decode_responses=True)
# 
#     def get(self, key):
#         return self.client.get(key)
# 
#     def exists(self, key):
#         return self.client.exists(key)
# 
#     def set(self, key, value, timeout=DEFAULT_TIMEOUT):
#         self.client.set(key, value, timeout)
# 
# Path: app/utils.py
# def log_middleware_information(log_statement, dict_with_variables, log_level, device=None):
#     """
#     Function that handles the logging for the middleware.
# 
#     Args:
#         log_statement (str): The message to log.
#         dict_with_variables (OrderedDict): OrderedDict that contains the
#             variables we want to insert into the logging statement.
#         log_level (int): The level on which to log.
#         device (Device): The device for which we want to log to Logentries.
#     """
#     remote_logging_id = device.remote_logging_id if device and device.remote_logging_id else 'No remote logging ID'
#     django_log_statement = fill_log_statement(log_statement, dict_with_variables)
#     django_logger.log(log_level, '{0} - middleware - {1}'.format(remote_logging_id, django_log_statement))
# 
#     if device and device.remote_logging_id:
#         log_statement = fill_log_statement(log_statement, dict_with_variables, anonymize=True)
#         logentries_token = device.app.logentries_token
#         log_to_logentries(log_statement, log_level, logentries_token, device, remote_logging_id)
#         if device.app.partner_logentries_token:
#             # Log to the Logentries environment of the partner with a different token.
#             logentries_token = device.app.partner_logentries_token
#             log_to_logentries(log_statement, log_level, logentries_token, device, remote_logging_id)
# 
# Path: app/models.py
# ANDROID_PLATFORM = 'android'
# 
# APNS_PLATFORM = 'apns'
# 
# GCM_PLATFORM = 'gcm'


# Path: app/push.py
from collections import OrderedDict
from time import time
from urllib.parse import urljoin
from apns2.client import APNsClient
from apns2.errors import APNsException, BadDeviceToken, DeviceTokenNotForTopic, Unregistered
from apns2.payload import Payload
from django.conf import settings
from gcm.gcm import GCM, GCMAuthenticationException
from pyfcm import FCMNotification
from pyfcm.errors import AuthenticationError, FCMServerError, InternalPackageError
from app.cache import RedisClusterCache
from app.utils import log_middleware_information
from .models import ANDROID_PLATFORM, APNS_PLATFORM, GCM_PLATFORM
import datetime
import logging
import os

        )


# The APNSv2 connection pool. The connection pool stores an APNSv2 connection for
# an app_id. This connection pool can be shared among multiple threads.
# Don't use the connection pool directly but use `get_apns2_connection`.
apns2_connection_pool = {}


def get_apns2_connection(app, device, unique_key):
    """
    Get the active APNSv2 connection for the device.

    This returns a reference to the connection object
    for the app and with the device's sandbox setting.
    Initializes the connection if it was not yet set up.

    Args:
        app (App): App requesting the connection.
        device (Device): Device requesting the connection.
        unique_key (str): Unique key used for logging.

    Returns:
        APNsClient.
    """
    global apns2_connection_pool
    key = (app.app_id, device.sandbox)
    if key not in apns2_connection_pool:
        full_cert_path = os.path.join(settings.CERT_DIR, app.push_key)
        apns2_connection = APNsClient(full_cert_path, use_sandbox=device.sandbox)

        log_middleware_information(