from __future__ import absolute_import
import os
import warnings
from flask import Flask, current_app
import six
from werkzeug.utils import import_string

# Shared objects
from . import db, csrf, requests_session
# Internal modules/packages
from . import util


def create_app(config=None, **kwargs):
    # Internal modules/packages
    from . import login, divisions, requests, views, json
    app = Flask('evesrp', **kwargs)
    app.request_class = util.AcceptRequest
    app.config.from_object('evesrp.default_config')
    if config is not None:
        app.config.from_pyfile(config)
    if app.config['SECRET_KEY'] is None and 'SECRET_KEY' in os.environ:
        app.config['SECRET_KEY'] = os.environ['SECRET_KEY']

    # Register SQLAlchemy monitoring before the DB is connected
    app.before_request(sqlalchemy_before)

    db.init_app(app)

    login.login_manager.init_app(app)

    before_csrf = list(app.before_request_funcs[None])
    csrf.init_app(app)
    # Remove the context processor that checks CSRF values. All it is used for
    # is the template function.
    app.before_request_funcs[None] = before_csrf

    app.add_url_rule(rule=u'/', view_func=views.index)
    for error_code in (400, 403, 404, 500):
        app.register_error_handler(error_code, views.error_page)
    app.register_blueprint(divisions.blueprint, url_prefix='/division')
    app.register_blueprint(login.blueprint)
    app.register_blueprint(requests.blueprint, url_prefix='/request')
    app.register_blueprint(views.api.api, url_prefix='/api')
    app.register_blueprint(views.api.filters, url_prefix='/api/filter')

    app.add_template_global(requests.request_count)

    app.json_encoder = json.SRPEncoder

    _config_authmethods(app)
    _config_requests_session(app)
    _config_killmails(app)
    _config_url_converters(app)

    # Configure the Jinja context
    # Inject variables into the context
    from .permission import PermissionType
    @app.context_processor
    def inject_enums():
        return {
            'ActionType': requests.ActionType,
            'PermissionType': PermissionType,
            'app_version': __version__,
            'site_name': app.config['SRP_SITE_NAME'],
            'url_for_page': requests.url_for_page,
        }
    # Auto-trim whitespace
    app.jinja_env.trim_blocks = True
    app.jinja_env.lstrip_blocks = True

    return app


# SQLAlchemy performance logging
def sqlalchemy_before():
    if util.DB_STATS.total_queries > 0:
        current_app.logger.debug(u"{} queries in {} ms.".format(
                DB_STATS.total_queries,
                round(DB_STATS.total_time * 1000, 3)))
    util.DB_STATS.clear()
    g.DB_STATS = util.DB_STATS


# Utility function for creating instances from dicts
def _instance_from_dict(instance_descriptor):
    type_name = instance_descriptor.pop('type')
    Type = import_string(type_name)
    return Type(**instance_descriptor)


# Utility function for raising config deprecation warnings
def _deprecated_object_instance(key, value):
    warnings.warn(u"Non-basic data types in configuration values are deprecated"
                 u"({}: {})".format(key, value), DeprecationWarning,
                 stacklevel=2)


# Auth setup
def _config_authmethods(app):
    auth_methods = []
    # Once the deprecated config value support is removed, this can be
    # rewritten as a dict comprehension
    for method in app.config['SRP_AUTH_METHODS']:
        if isinstance(method, dict):
            auth_methods.append(_instance_from_dict(method))
        elif isinstance(method, login.AuthMethod):
            _deprecated_object_instance('SRP_AUTH_METHODS', method)
            auth_methods.append(method)
    app.auth_methods = auth_methods


# Request detail URL setup
def _config_url_converters(app):
    url_transformers = {}
    for config_key, config_value in app.config.items():
        # Skip null config values
        if config_value is None:
            continue
        # Look for config keys in the format 'SRP_*_URL_TRANSFORMERS'
        if not config_key.endswith('_URL_TRANSFORMERS')\
                or not config_key.startswith('SRP_'):
            continue
        attribute = config_key.replace('SRP_', '', 1)
        attribute = attribute.replace('_URL_TRANSFORMERS', '', 1)
        attribute = attribute.lower()
        # Create Transformer instances for this attribute
        transformers = {}
        for transformer_config in config_value:
            if isinstance(transformer_config, tuple):
                # Special case for Transformers: A 2-tuple in the form:
                # (u'Transformer Name',
                #     'http://example.com/transformer/slug/{}')
                transformer = divisions.Transformer(*transformer_config)
            elif isinstance(transformer_config, dict):
                # Standard instance dictionary format
                # Provide a default type value
                transformer_config.setdefault('type',
                        'evesrp.divisions.Transformer')
                transformer = _instance_from_dict(transformer_config)
            elif isinstance(transformer_config, divisions.Transformer):
                # DEPRECATED: raw Transformer instance
                _deprecated_object_instance(config_key, transformer_config)
                transformer = transformer_config
            transformers[transformer.name] = transformer
        url_transformers[attribute] = transformers
    app.url_transformers = url_transformers


# Requests session setup
def _config_requests_session(app):
    try:
        ua_string = app.config['SRP_USER_AGENT_STRING']
    except KeyError as outer_exc:
        try:
            ua_string = 'EVE-SRP/{version} ({email})'.format(
                    email=app.config['SRP_USER_AGENT_EMAIL'],
                    version=__version__)
        except KeyError as inner_exc:
            raise inner_exc
    requests_session.headers.update({'User-Agent': ua_string})
    app.user_agent = ua_string


# Killmail verification
def _config_killmails(app):
    killmail_sources = []
    # For now, use a loop with checks. After removing the depecated config
    # method it can be rewritten as a list comprehension
    for source in app.config['SRP_KILLMAIL_SOURCES']:
        if isinstance(source, six.string_types):
            killmail_sources.append(import_string(source))
        elif isinstance(source, type):
            _deprecated_object_instance('SRP_KILLMAIL_SOURCES', source)
            killmail_sources.append(source)
    app.killmail_sources = killmail_sources
