"""Flask middleware definitions. This is also where template filters are defined.

To be imported by the application.current_app() factory.
"""
import os
from logging import getLogger

from flask import current_app, jsonify, request, session, g, url_for, safe_join
from flask_babel import gettext, format_datetime
from pritunl_dedicated.extensions import navigation, babel, db, jwt
from pritunl_dedicated.tools.formaters import format_bytes, format_boolean
from pritunl_dedicated.models.pritunl_dedicated import User
from pritunl_dedicated.tools.helpers import get_hash
from markupsafe import Markup

LOG = getLogger(__name__)
hash_cache = {}


# Setup default error templates.
@current_app.errorhandler(400)
@current_app.errorhandler(403)
@current_app.errorhandler(404)
@current_app.errorhandler(500)
def error_handler(e):
    code = getattr(e, 'code', 500)  # If 500, e == the exception.
    return jsonify({'message': 'Error {}'.format(code)}), code


@current_app.url_defaults
def add_hash_for_static_files(endpoint, values):
    """Add content hash argument for url to make url unique.
    It's have sense for updates to avoid caches.
    """
    if endpoint != 'static':
        return
    filename = values['filename']
    if filename in hash_cache:
        values['hash'] = hash_cache[filename]
        return

    filepath = safe_join(current_app.static_folder, filename)
    if os.path.isfile(filepath):
        with open(filepath, 'rb') as static_file:
            filehash = get_hash(static_file.read(), 8)
            values['hash'] = hash_cache[filename] = filehash


@jwt.user_loader_callback_loader
def user_loader_callback(identity) -> User:
    return User.query.get(int(identity['id']))


@babel.localeselector
def get_locale():
    if current_app.config['LANGUAGE']:
        return current_app.config['LANGUAGE']
    # if a user is logged in, use the locale from the user settings
    user = getattr(g, 'user', None)
    if user is not None:
        return user.locale
    # otherwise try to guess the language from the user accept
    # header the browser transmits.  We support de/fr/en in this
    # example.  The best match wins.
    return request.accept_languages.best_match(current_app.config['SUPPORTED_LANGUAGES'].keys())


@babel.timezoneselector
def get_timezone():
    user = g.get('user', None)
    if user is not None:
        return user.timezone


@current_app.before_request
def before_request():
    pass


@current_app.template_filter('format_bytes')
def format_bytes_filter(num):
    return format_bytes(num)


@current_app.template_filter('format_datetime')
def format_datetime_filter(date_time):
    return format_datetime(date_time)


@current_app.template_filter('int_to_bin')
def int_to_bin(variable):
    return '0b{:08b}'.format(variable)


@current_app.template_filter('int_to_chr')
def int_to_chr(variable):
    return repr(chr(variable))


@current_app.template_filter('format_boolean')
def format_boolean_filter(var: bool, values: list=None):
    return Markup(format_boolean(var, values))


# Template filters.
@current_app.template_filter()
def whitelist(value):
    """Whitelist specific HTML tags and strings.
    Positional arguments:
    value -- the string to perform the operation on.
    Returns:
    Markup() instance, indicating the string is safe.
    """
    translations = {
        '&amp;quot;': '&quot;',
        '&amp;#39;': '&#39;',
        '&amp;lsquo;': '&lsquo;',
        '&amp;nbsp;': '&nbsp;',
        '&lt;br&gt;': '<br>',
    }
    escaped = str(Markup.escape(value))  # Escapes everything.
    for k, v in translations.items():
        escaped = escaped.replace(k, v)  # Un-escape specific elements using str.replace.
    return Markup(escaped)  # Return as 'safe'.