# -*- coding: utf-8 -*-
"""
app.factory
~~~~~~~~~~~

Create the WSGI application.

:copyright: (c) 2017 European Spallation Source ERIC
:license: BSD 2-Clause, see LICENSE for more details.

"""
import logging
import sqlalchemy as sa
import rq_dashboard
import sentry_sdk
from flask import Flask
from whitenoise import WhiteNoise
from elasticsearch import Elasticsearch
from sentry_sdk.integrations.flask import FlaskIntegration
from . import settings, models
from .extensions import (
    db,
    migrate,
    login_manager,
    ldap_manager,
    admin,
    mail,
    jwt,
    toolbar,
    session_redis_store,
    fsession,
    cache,
)
from .admin.views import (
    AdminModelView,
    ItemAdmin,
    UserAdmin,
    TokenAdmin,
    NetworkAdmin,
    TaskAdmin,
)
from .main.views import bp as main
from .inventory.views import bp as inventory
from .network.views import bp as network
from .task.views import bp as task
from .user.views import bp as user
from .api.user import bp as user_api
from .api.inventory import bp as inventory_api
from .api.network import bp as network_api
from .commands import register_cli
from ._version import __version__
from . import utils


def create_app(config=None):
    app = Flask(__name__)

    app.config.from_object(rq_dashboard.default_settings)
    app.config.from_object(settings)
    app.config.from_envvar("LOCAL_SETTINGS", silent=True)
    app.config.update(config or {})

    app.jinja_env.filters["datetimeformat"] = utils.format_datetime
    app.jinja_env.filters["toyaml"] = utils.pretty_yaml
    app.jinja_env.globals["__version__"] = __version__

    if app.config["SENTRY_DSN"]:
        sentry_sdk.init(
            dsn=app.config["SENTRY_DSN"],
            environment=app.config["CSENTRY_ENVIRONMENT"],
            send_default_pii=True,
            integrations=[FlaskIntegration()],
        )

    if not app.debug:
        # Log to stderr
        handler = logging.StreamHandler()
        handler.setFormatter(
            logging.Formatter(
                "%(asctime)s %(levelname)s: %(message)s " "[in %(pathname)s:%(lineno)d]"
            )
        )
        # Set app logger level to DEBUG
        # otherwise only WARNING and above are propagated
        app.logger.setLevel(logging.DEBUG)
        handler.setLevel(logging.DEBUG)
        app.logger.addHandler(handler)
    app.logger.info("CSEntry created!")
    # Remove variables that contain a password
    settings_to_display = {
        key: value
        for key, value in app.config.items()
        if key
        not in (
            "SENTRY_DSN",
            "SECRET_KEY",
            "LDAP_BIND_USER_PASSWORD",
            "SQLALCHEMY_DATABASE_URI",
        )
    }
    # The repr() of make_url hides the password
    settings_to_display["SQLALCHEMY_DATABASE_URI"] = repr(
        sa.engine.url.make_url(app.config["SQLALCHEMY_DATABASE_URI"])
    )
    settings_string = "\n".join(
        [f"{key}: {settings_to_display[key]}" for key in sorted(settings_to_display)]
    )
    app.logger.info(f"Settings:\n{settings_string}")

    # Force RQ_DASHBOARD_REDIS_URL to RQ_REDIS_URL
    app.config["RQ_DASHBOARD_REDIS_URL"] = app.config["RQ_REDIS_URL"]

    db.init_app(app)
    migrate.init_app(app)
    login_manager.init_app(app)
    login_manager.login_view = "user.login"
    ldap_manager.init_app(app)
    mail.init_app(app)
    jwt.init_app(app)
    toolbar.init_app(app)
    session_redis_store.init_app(app)
    app.config["SESSION_REDIS"] = session_redis_store
    fsession.init_app(app)
    cache.init_app(app)
    app.elasticsearch = (
        Elasticsearch([app.config["ELASTICSEARCH_URL"]])
        if app.config["ELASTICSEARCH_URL"]
        else None
    )

    admin.init_app(app)
    admin.add_view(UserAdmin(models.User, db.session, endpoint="users"))
    admin.add_view(TokenAdmin(models.Token, db.session))
    admin.add_view(AdminModelView(models.Action, db.session))
    admin.add_view(AdminModelView(models.Manufacturer, db.session))
    admin.add_view(AdminModelView(models.Model, db.session))
    admin.add_view(AdminModelView(models.Location, db.session))
    admin.add_view(AdminModelView(models.Status, db.session))
    admin.add_view(ItemAdmin(models.Item, db.session))
    admin.add_view(AdminModelView(models.ItemComment, db.session))
    admin.add_view(AdminModelView(models.Domain, db.session))
    admin.add_view(AdminModelView(models.NetworkScope, db.session))
    admin.add_view(NetworkAdmin(models.Network, db.session, endpoint="networks"))
    admin.add_view(AdminModelView(models.DeviceType, db.session))
    admin.add_view(AdminModelView(models.AnsibleGroup, db.session))
    admin.add_view(AdminModelView(models.Host, db.session))
    admin.add_view(AdminModelView(models.Interface, db.session))
    admin.add_view(AdminModelView(models.Mac, db.session))
    admin.add_view(AdminModelView(models.Cname, db.session))
    admin.add_view(TaskAdmin(models.Task, db.session, endpoint="tasks"))

    app.register_blueprint(main)
    app.register_blueprint(inventory, url_prefix="/inventory")
    app.register_blueprint(network, url_prefix="/network")
    app.register_blueprint(task, url_prefix="/task")
    app.register_blueprint(user, url_prefix="/user")
    app.register_blueprint(user_api, url_prefix="/api/v1/user")
    app.register_blueprint(inventory_api, url_prefix="/api/v1/inventory")
    app.register_blueprint(network_api, url_prefix="/api/v1/network")
    app.register_blueprint(rq_dashboard.blueprint, url_prefix="/rq")

    app.wsgi_app = WhiteNoise(app.wsgi_app, root=app.config["CSENTRY_STATIC_DIR"])

    register_cli(app)

    return app