"""
Provides factory methods to assemble the Galaxy web application
"""

import atexit
import logging

from paste import httpexceptions

import galaxy.webapps.base.webapp
from galaxy.structured_app import BasicSharedApp
from galaxy.util import asbool
from galaxy.util.properties import load_app_properties
from galaxy.web import url_for
from galaxy.web.framework.middleware.error import ErrorMiddleware
from galaxy.web.framework.middleware.request_id import RequestIDMiddleware
from galaxy.web.framework.middleware.xforwardedhost import XForwardedHostMiddleware
from galaxy.webapps.base.webapp import (
    build_url_map,
    GalaxyWebTransaction,
)
from galaxy.webapps.util import wrap_if_allowed

log = logging.getLogger(__name__)


class ToolShedGalaxyWebTransaction(GalaxyWebTransaction):
    @property
    def repositories_hostname(self) -> str:
        # Use configured tool_shed_url if available
        if tool_shed_url := self.app.config.tool_shed_url:
            return tool_shed_url.rstrip("/")
        # Fall back to request-based URL
        return url_for("/", qualified=True).rstrip("/")

    def get_or_create_default_history(self):
        # tool shed has no concept of histories
        raise NotImplementedError


class CommunityWebApplication(galaxy.webapps.base.webapp.WebApplication):
    injection_aware: bool = True

    def transaction_chooser(self, environ, galaxy_app: BasicSharedApp, session_cookie: str):
        return ToolShedGalaxyWebTransaction(environ, galaxy_app, self, session_cookie)


def add_ui_controllers(webapp, app):
    """Register the HgController — the only remaining WSGI controller."""
    from tool_shed.webapp.controllers.hg import HgController

    webapp.add_ui_controller("hg", HgController(app))


def app_factory(*args, **kwargs):
    """
    Return a wsgi application serving the root object
    """
    return app_pair(*args, **kwargs)[0]


def app_pair(global_conf, load_app_kwds=None, **kwargs):
    """Return a wsgi application serving the root object"""
    # Create the Galaxy tool shed application unless passed in
    load_app_kwds = load_app_kwds or {}
    kwargs = load_app_properties(kwds=kwargs, config_prefix="TOOL_SHED_CONFIG_", **load_app_kwds)
    if "app" in kwargs:
        app = kwargs.pop("app")
        import tool_shed.webapp.app

        tool_shed.webapp.app.app = app
    else:
        try:
            import tool_shed.webapp.app

            app = tool_shed.webapp.app.UniverseApplication(global_conf=global_conf, **kwargs)
            tool_shed.webapp.app.app = app
        except Exception:
            import sys
            import traceback

            traceback.print_exc()
            sys.exit(1)
    atexit.register(app.shutdown)
    # Create the universe WSGI application
    webapp = CommunityWebApplication(app, session_cookie="galaxycommunitysession", name="tool_shed")
    add_ui_controllers(webapp, app)
    # Enable 'hg clone' functionality on repos by letting hgwebapp handle the request
    webapp.add_route("/repos/*path_info", controller="hg", action="handle_request", path_info="/")
    webapp.finalize_config()
    # Wrap the webapp in some useful middleware
    if kwargs.get("middleware", True):
        webapp = wrap_in_middleware(webapp, global_conf, app.application_stack, **kwargs)
    if asbool(kwargs.get("static_enabled", True)):
        webapp = wrap_if_allowed(webapp, app.application_stack, build_url_map, args=(global_conf,), kwargs=kwargs)
    return webapp, app


def wrap_in_middleware(app, global_conf, application_stack, **local_conf):
    """Based on the configuration wrap `app` in a set of common and useful middleware."""
    stack = application_stack
    # Merge the global and local configurations
    conf = global_conf.copy()
    conf.update(local_conf)
    # First put into place httpexceptions, which must be most closely
    # wrapped around the application (it can interact poorly with
    # other middleware):
    app = wrap_if_allowed(app, stack, httpexceptions.make_middleware, name="paste.httpexceptions", args=(conf,))
    assert not asbool(conf.get("use_remote_user", False))

    # Transaction logging (apache access.log style)
    if asbool(conf.get("use_translogger", True)):
        from paste.translogger import TransLogger

        app = wrap_if_allowed(app, stack, TransLogger)

    # X-Forwarded-Host handling
    app = wrap_if_allowed(app, stack, XForwardedHostMiddleware)
    # Request ID handling
    app = wrap_if_allowed(app, stack, RequestIDMiddleware)
    # Error middleware
    app = wrap_if_allowed(app, stack, ErrorMiddleware, args=(conf,))
    return app
