import json
import os
import secrets
from pathlib import Path

import yaml
from flask import Flask, url_for
from flask_security import Security, SQLAlchemyUserDatastore
from flask_security.utils import hash_password
from rst2html import rst2html

from libertywiki.admin import setup_admin
from libertywiki.db import db, session
from libertywiki.models import User, Role
from libertywiki.views import wiki

DEFAULT_CONFIG = {
    'DEBUG': False,
    'SECRET_KEY': secrets.token_urlsafe(128),
    'SECURITY_PASSWORD_SALT': secrets.token_urlsafe(128),
    'SECURITY_USERNAME_ENABLE': False,
    'SECURITY_REGISTERABLE': True,
    'SECURITY_SEND_REGISTER_EMAIL': False,
    'SQLALCHEMY_DATABASE_URI': 'sqlite://',
    'SQLALCHEMY_ENGINE_OPTIONS': {'pool_pre_ping': True},
    'SQLALCHEMY_TRACK_MODIFICATIONS': False,
    'WIKI_MODE': 'PUBLIC'
}
CONFIG_VARS = [
    'LIBERTYWIKI_CONFIG',
    'LW_CONFIG',
    'WIKI_CONFIG'
]


def get_app():
    """Create and configure the application object"""
    app = Flask(__name__)

    # Set app defaults
    app.config.update(DEFAULT_CONFIG)

    # Load config from file
    for config_var in CONFIG_VARS:
        if os.environ.get(config_var):
            config_fname = Path(os.environ[config_var])
            if config_fname.suffix == 'json':
                loader = json.load
            elif config_fname.suffix in ['yaml', 'yml']:
                loader = yaml.safe_load
            with Path(os.environ[config_var]).open() as conf_file:
                app.config.update(**loader(conf_file))
            break

    # Load from environment variables
    app.config.update(**{key: os.environ[key] for key in DEFAULT_CONFIG.keys() if os.environ.get(key)})

    db.init_app(app)
    user_datastore = SQLAlchemyUserDatastore(db, User, Role)
    security = Security(app, user_datastore)
    admin, admin_helpers = setup_admin(app)

    with app.app_context():
        # Create all the tables in the database
        db.create_all()
        # Create the basic roles
        admin_role = user_datastore.find_or_create_role(name='admin', description='Administrator')
        user_datastore.find_or_create_role(name='end-user', description='End user')
        session.commit()
        # Create an administrator
        if not User.query.filter(User.roles.contains(admin_role)).first():
            user = user_datastore.create_user(name='Administrator', email='admin@example.com',
                                              password=hash_password('password1234'))
            user.roles.append(admin_role)
            session.commit()

    @app.template_filter('rst2html')
    def rst2html_filter(text):
        html, warning = rst2html(text)
        return html

    @security.context_processor
    def security_context_processor():
        return dict(
            admin_base_template=admin.base_template,
            admin_view=admin.index_view,
            h=admin_helpers,
            get_url=url_for
        )

    app.register_blueprint(wiki)

    return app
