# MultiWSGI - an inetd-esque WSGI superserver.
# by Sam van Kampen, 2018
import sys
import importlib
import json
import os
import cherrypy
from werkzeug.exceptions import NotImplemented
from werkzeug.wsgi import get_host, get_current_url
from traceback import print_exc as print_stack_trace
import logging

logger = logging.Logger('wsgi-server')

my_path = os.path.realpath(os.getcwd())

class DomainDispatcher(object):
    """Simple domain dispatch"""
    def __init__(self, domain_handlers, default_handler=None):
        self.domain_handlers = domain_handlers
        self.default_handler = domain_handlers.get("default", default_handler)
        if self.default_handler is None:
            self.default_handler = NotImplemented()

    def __call__(self, environ, start_response):
        host = get_host(environ)
        for key, (opts, handler) in self.domain_handlers.items():
            if host in key:
                url = get_current_url(environ)
                # logger.warn(f'Handling request for {url} using handler {opts["name"]}')
                os.chdir(os.path.join(my_path, opts["name"]))
                try:
                    resp = handler(environ, start_response)
                except:
                    logger.warn("Exception occurred!")
                    print_stack_trace(file=sys.stderr)
                    return ''
                else:
                    return resp
        logger.warn(f'Unhandled request for url {get_current_url(environ)}')
        resp = self.default_handler(environ, start_response)
        return resp

def force_https(app):
    # Because of unknown reasons, wsgi.url_scheme does not get inherited from nginx.
    # Set it to HTTPS here, so we don't get bogus mixed content warnings.
    def https_app(environ, start_response):
        environ['wsgi.url_scheme'] = 'https'
        return app(environ, start_response)
    return https_app

def load_app(appdesc):
    path, name = os.path.split(appdesc["name"])
    if path:
        sys.path.insert(0, path)

    os.chdir(my_path)
    os.chdir(appdesc["name"])

    module = importlib.import_module("{0}.{0}".format(name, name))

    os.chdir(my_path)
    if hasattr(module.app, 'config'):
        module.app.config['catchall'] = False
    return force_https(module.app)

def load_apps(config_path):
    config = json.load(open(config_path, 'r'))
    return {(*app["domains"],):(app, load_app(app)) for app in config["apps"]}

if __name__ == "__main__":
    dispatch_dict = load_apps("config.json")
    dispatcher = DomainDispatcher(dispatch_dict)

    cherrypy.tree.graft(dispatcher, '/')
    cherrypy.engine.start()
    cherrypy.engine.block()

