#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import os.path
import sys

import djcelery

from django.conf import global_settings
from django.contrib.messages import constants as messages

djcelery.setup_loader()


DEFAULT_MIDDLEWARE_CLASSES = (
    'whitenoise.middleware.WhiteNoiseMiddleware',
    # this middlewware is for generate human redeable errors

    # alwaws before CommonMiddleware
    'corsheaders.middleware.CorsMiddleware',

    'django.contrib.sessions.middleware.SessionMiddleware',
    # 'django.middleware.locale.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',

    # 2015-04-08: disabling SSLify until we make this work better
    # 'sslify.middleware.SSLifyMiddleware',
)


def collapse_to_unique_list(*args):
    """Create a new list with all elements from a given lists without reapeated
    elements

    """
    combined = []
    for arg in args:
        for elem in arg or ():
            if elem not in combined:
                combined.append(elem)
    return combined


def get_default_settings(initial_settings=None):
    if initial_settings is None:
        initial_settings = {}
    logging = {
        'version': 1,
        'disable_existing_loggers': False,
        'root': {
            'level': 'DEBUG',
            'handlers': ['console']
        },
        'formatters': {
            'verbose': {
                'format': '[%(levelname)s|%(asctime)s] %(name)s > %(message)s'
            },
            'simple': {
                'format': '%(levelname)s %(message)s'
            },
        },
        'handlers': {
            'console': {
                'level': 'INFO',
                'class': 'logging.StreamHandler',
                'formatter': 'verbose'
            },
        },
        'loggers': {
            'otree.test.core': {
                'handlers': ['console'],
                'propagate': False,
                'level': 'INFO',
            },
        }
    }

    page_footer = (
        'Powered By <a href="http://otree.org" target="_blank">oTree</a>'
    )

    return {
        # pages with a time limit for the player can have a grace period
        # to compensate for network latency.
        # the timer is started and stopped server-side,
        # so this grace period should account for time spent during
        # download, upload, page rendering, etc.
        'TIMEOUT_LATENCY_ALLOWANCE_SECONDS': 10,

        'SESSION_SAVE_EVERY_REQUEST': True,
        'TEMPLATE_DEBUG': initial_settings.get('DEBUG', False),
        'STATIC_ROOT': os.path.join(
            initial_settings.get('BASE_DIR', ''),
            '_static_root'),
        'STATIC_URL': '/static/',
        'STATICFILES_STORAGE': (
            'whitenoise.django.GzipManifestStaticFilesStorage'
        ),
        'ROOT_URLCONF': 'otree.default_urls',

        'TIME_ZONE': 'UTC',
        'USE_TZ': True,
        'SESSION_SERIALIZER': (
            'django.contrib.sessions.serializers.PickleSerializer'
        ),
        'ALLOWED_HOSTS': ['*'],

        'TEMPLATE_CONTEXT_PROCESSORS': collapse_to_unique_list(
            global_settings.TEMPLATE_CONTEXT_PROCESSORS, (
                'django.core.context_processors.request',
                'otree.context_processors.otree_context'
            )
        ),

        # SEO AND FOOTER
        'PAGE_FOOTER': page_footer,

        # list of extra string to positioning you experiments on search engines
        # Also if you want to add a particular set of SEO words to a particular
        # page add to template context "page_seo" variable.
        # See: http://en.wikipedia.org/wiki/Search_engine_optimization
        'SEO': (),

        'LOGGING': logging,

        'REAL_WORLD_CURRENCY_CODE': 'USD',
        'REAL_WORLD_CURRENCY_LOCALE': 'en_US',
        'REAL_WORLD_CURRENCY_DECIMAL_PLACES': 2,
        'USE_POINTS': True,

        'POINTS_DECIMAL_PLACES': 0,

        # eventually can remove this,
        # when it's present in otree-library
        # that most people downloaded
        'USE_L10N': True,

        'WSGI_APPLICATION': 'otree.wsgi.application',
        'SECURE_PROXY_SSL_HEADER': ('HTTP_X_FORWARDED_PROTO', 'https'),
        'MTURK_HOST': 'mechanicalturk.amazonaws.com',
        'MTURK_SANDBOX_HOST': 'mechanicalturk.sandbox.amazonaws.com',
        'CREATE_DEFAULT_SUPERUSER': True,

        # The project can override the routing.py used as entry point by
        # setting CHANNEL_DEFAULT_ROUTING.

        #'CHANNEL_LAYERS': {
        #     'default': {
        #         'BACKEND': 'channels.database_layer.DatabaseChannelLayer',
        #         'ROUTING': initial_settings.get(
        #             'CHANNEL_DEFAULT_ROUTING',
        #             'otree.channels.default_routing.channel_routing'),
        #     },
        # },

        'CHANNEL_LAYERS': {
             'default': {
                 "BACKEND": "otree.channels.asgi_redis.RedisChannelLayer",
                 "CONFIG": {
                     "hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')],
                 },
                 'ROUTING': initial_settings.get(
                     'CHANNEL_DEFAULT_ROUTING',
                     'otree.channels.default_routing.channel_routing'),
             },
         },

        'CELERY_APP': 'otree.celery.app:app',

        # since workers on Amazon MTurk can return the hit
        # we need extra participants created on the
        # server.
        # The following setting is ratio:
        # num_participants_server / num_participants_mturk
        'MTURK_NUM_PARTICIPANTS_MULT': 2,

        'MIDDLEWARE_CLASSES': DEFAULT_MIDDLEWARE_CLASSES,
        'LOCALE_PATHS': [
            os.path.join(initial_settings.get('BASE_DIR', ''), 'locale')
        ]
    }


def augment_settings(settings):

    if 'POINTS_CUSTOM_NAME' in settings:
        settings.setdefault(
            'POINTS_CUSTOM_FORMAT',
            '{} ' + settings['POINTS_CUSTOM_NAME']
        )

    all_otree_apps_set = set()

    if ('SESSION_CONFIGS' not in settings and
            'SESSION_TYPES' in settings):
        raise ValueError(
            'In settings.py, you should rename '
            'SESSION_TYPES to SESSION_CONFIGS, and '
            'SESSION_TYPE_DEFAULTS to SESSION_CONFIG_DEFAULTS.'
        )

    for s in settings['SESSION_CONFIGS']:
        for app in s['app_sequence']:
            all_otree_apps_set.add(app)

    all_otree_apps = list(all_otree_apps_set)

    no_experiment_apps = [
        'django.contrib.auth',
        'otree',
        'floppyforms',
        # need this for admin login
        'django.contrib.admin',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'otree.models_concrete',
        'otree.timeout',
        'channels',
        'djcelery',
        'kombu.transport.django',
        'rest_framework',
        'sslserver',
        'idmap',
        'corsheaders']

    if settings.get('SENTRY_DSN'):
        settings.setdefault(
            'RAVEN_CONFIG',
            {
                'dsn': settings['SENTRY_DSN'],
                'processors': ['raven.processors.SanitizePasswordsProcessor'],
            }
        )
        no_experiment_apps.append('raven.contrib.django.raven_compat')

    # order is important:
    # otree unregisters User & Group, which are installed by auth.
    # otree templates need to get loaded before the admin.
    no_experiment_apps = collapse_to_unique_list(
        no_experiment_apps,
        settings['INSTALLED_APPS']
    )

    new_installed_apps = collapse_to_unique_list(
        no_experiment_apps, all_otree_apps)

    additional_template_dirs = []
    template_dir = os.path.join(settings['BASE_DIR'], 'templates')
    # 2015-09-21: i won't put a deprecation warning because 'templates/'
    # is the django convention and someone might legitimately want it.
    # just remove this code at some point
    # same for static/ dir below
    if os.path.exists(template_dir):
        additional_template_dirs = [template_dir]

    _template_dir = os.path.join(settings['BASE_DIR'], '_templates')
    if os.path.exists(_template_dir):
        additional_template_dirs = [_template_dir]

    new_template_dirs = collapse_to_unique_list(
        settings.get('TEMPLATE_DIRS'),
        # 2015-5-2: 'templates' is deprecated in favor of '_templates'
        # remove it at some point
        additional_template_dirs,
    )

    static_dir = os.path.join(settings['BASE_DIR'], 'static')
    additional_static_dirs = []
    if os.path.exists(static_dir):
        additional_static_dirs = [static_dir]

    _static_dir = os.path.join(settings['BASE_DIR'], '_static')
    if os.path.exists(_static_dir):
        additional_static_dirs = [_static_dir]

    new_staticfiles_dirs = collapse_to_unique_list(
        settings.get('STATICFILES_DIRS'),
        # 2015-5-2: 'static' is deprecated in favor of '_static'
        # remove it at some point
        additional_static_dirs,
    )

    new_middleware_classes = collapse_to_unique_list(
        DEFAULT_MIDDLEWARE_CLASSES,
        settings.get('MIDDLEWARE_CLASSES'))

    augmented_settings = {
        'INSTALLED_APPS': new_installed_apps,
        'TEMPLATE_DIRS': new_template_dirs,
        'STATICFILES_DIRS': new_staticfiles_dirs,
        'MIDDLEWARE_CLASSES': new_middleware_classes,
        'NO_EXPERIMENT_APPS': no_experiment_apps,
        'INSTALLED_OTREE_APPS': all_otree_apps,
        'BROKER_URL': 'django://',
        'MESSAGE_TAGS': {messages.ERROR: 'danger'},
        'CELERY_ACCEPT_CONTENT': ['pickle', 'json', 'msgpack', 'yaml'],
        'LOGIN_REDIRECT_URL': 'sessions',
    }

    # CORS CONFS
    augmented_settings.update({
        'CORS_ORIGIN_ALLOW_ALL': True,
        'CORS_URLS_REGEX': r'^ping/$',
        'CORS_ALLOW_METHODS': ('GET',)
    })

    settings.setdefault('LANGUAGE_CODE', global_settings.LANGUAGE_CODE)

    CURRENCY_LOCALE = settings.get('CURRENCY_LOCALE', None)
    if not CURRENCY_LOCALE:

        # favor en_GB currency formatting since it represents negative amounts
        # with minus signs rather than parentheses
        if settings['LANGUAGE_CODE'][:2] == 'en':
            CURRENCY_LOCALE = 'en_GB'
        else:
            CURRENCY_LOCALE = settings['LANGUAGE_CODE']

    settings.setdefault('CURRENCY_LOCALE', CURRENCY_LOCALE.replace('-', '_'))

    overridable_settings = get_default_settings(settings)

    settings.update(augmented_settings)

    for k, v in overridable_settings.items():
        settings.setdefault(k, v)

    # this guarantee that the test always run on memory
    if 'test' in sys.argv:
        settings["DATABASES"] = {
            "default": {
                "ENGINE": 'django.db.backends.sqlite3',
                "NAME": ':memory:'
            }
        }
        settings["DEBUG"] = False
        settings["TEMPLATE_DEBUG"] = False
