import re
import os
from vodka import get_instance
import vodka.plugins
import vodka.config
import vodka.config.validators


def application():
    return WSGIPlugin.wsgi_application


class SSLConfiguration(vodka.config.Handler):

    """
    Allows you to configure the ssl context of a wsgi
    plugin
    """

    enabled = vodka.config.Attribute(
        bool, default=False, help_text="enable ssl encryption"
    )

    key = vodka.config.Attribute(
        vodka.config.validators.path,
        default="",
        help_text="location of your ssl private key file",
    )

    cert = vodka.config.Attribute(
        vodka.config.validators.path,
        default="",
        help_text="location of your ssl certificate file",
    )


class WSGIPlugin(vodka.plugins.PluginBase):

    wsgi_application = None

    class Configuration(vodka.plugins.PluginBase.Configuration):
        # DEPRECATE: 3.0
        host = vodka.config.Attribute(
            str,
            default="localhost",
            help_text="host address",
            deprecated="Well be removed in 3.0, it's being replaced by the 'bind' config attribute",
        )

        # DEPRECATE: 3.0
        port = vodka.config.Attribute(
            int,
            default=80,
            help_text="host port",
            deprecated="Will be removed in 3.0, it's being replaced by the 'bind' config attribute",
        )

        bind = vodka.config.Attribute(
            vodka.config.validators.host,
            default="localhost:80",
            help_text="bind server to this address. e.g localhost:80",
        )

        debug = vodka.config.Attribute(
            bool,
            help_text="run wsgi server in debug mode (if applicable)",
            default=False,
        )

        server = vodka.config.Attribute(
            str,
            help_text="specify which wsgi server should be used",
            default="self",
            choices=["uwsgi", "gunicorn", "self", "gevent"],
        )

        static_url_path = vodka.config.Attribute(
            str,
            default="/static",
            help_text="url path where static files can be requested from",
        )

        routes = vodka.config.Attribute(
            dict,
            help_text="routing of request endpoints to vodka application end points",
            default={},
        )

        static_routes = vodka.config.Attribute(
            dict,
            help_text="routing of request endpoints to static file locations",
            default={},
        )

        ssl = vodka.config.Attribute(
            dict, help_text="ssl encryption", default={}, handler=SSLConfiguration
        )

    @classmethod
    def set_wsgi_app(cls, app):
        WSGIPlugin.wsgi_application = app

    def init(self):
        if "bind" in self.pluginmgr_config:
            parts = re.match("^(.+):(\d+)$", self.get_config("bind"))
            if not parts:
                raise ValueError("Unable to parse host and port from `bind` config")

            self.host = parts.group(1).strip("[]")
            self.port = int(parts.group(2))

        # DEPRECATE: 4.0.0
        else:
            self.host = self.get_config("host").strip("[]")
            self.port = self.get_config("port")

    def setup(self):

        self.static_url_prefixes = {}
        for name in list(vodka.instances.keys()):
            self.static_url_prefixes[name] = self.static_url_prefix(name)
        self.set_routes()

    def set_server(self, wsgi_app, fnc_serve=None):
        """
        figures out how the wsgi application is to be served
        according to config
        """

        self.set_wsgi_app(wsgi_app)

        ssl_config = self.get_config("ssl")
        ssl_context = {}

        if self.get_config("server") == "gevent":

            if ssl_config.get("enabled"):
                ssl_context["certfile"] = ssl_config.get("cert")
                ssl_context["keyfile"] = ssl_config.get("key")

            from gevent.pywsgi import WSGIServer

            http_server = WSGIServer((self.host, self.port), wsgi_app, **ssl_context)

            self.log.debug("Serving WSGI via gevent.pywsgi.WSGIServer")

            fnc_serve = http_server.serve_forever

        elif self.get_config("server") == "uwsgi":
            self.pluginmgr_config["start_manual"] = True

        elif self.get_config("server") == "gunicorn":
            self.pluginmgr_config["start_manual"] = True

        elif self.get_config("server") == "self":
            fnc_serve = self.run

        # figure out async handler

        if self.get_config("async") == "gevent":

            # handle async via gevent
            import gevent

            self.log.debug("Handling wsgi on gevent")

            self.worker = gevent.spawn(fnc_serve)

        elif self.get_config("async") == "thread":

            self.worker = fnc_serve

        else:

            self.worker = fnc_serve

    def run(self):
        pass

    def set_route(self, path, target, methods=None):
        pass

    def static_url_prefix(self, app_name):
        return os.path.join(self.get_config("static_url_path"), app_name, "")

    def request_env(self, req=None, **kwargs):
        renv = {"static_url": self.get_config("static_url_path"), "request": req}
        for name, instance in list(vodka.instances.items()):
            appenv = {
                "static_url": self.static_url_prefixes.get(name, ""),
                "instance": instance,
            }
            renv[name] = appenv
        renv.update(**kwargs)
        if "url" in kwargs:
            renv["host"] = "{}://{}".format(kwargs["url"].scheme, kwargs["url"].netloc)
        return renv

    def set_static_routes(self):
        pass

    def set_routes(self):

        self.set_static_routes()

        for path, target in list(self.get_config("routes").items()):

            if type(target) == str:
                target = {"target": target, "methods": ["GET"]}
            elif type(target) == dict:
                if not target.get("methods"):
                    target["methods"] = "GET"

            if target["target"].find("->") > -1:
                # target is an application method
                app_name, fnc_name = tuple(target["target"].split("->"))
                inst = get_instance(app_name)

                # FIXME: if for some reason we want to support different wsgi
                # plugins pointing to methods on the same app in the same
                # instance, this needs to change
                inst.wsgi_plugin = self

                meth = getattr(inst, fnc_name)

                # apply route decorators (specified by config keys other than
                # "target" and "methods"
                for k, v in list(target.items()):
                    decorator = getattr(self, "decorate_route_%s" % k, None)
                    if decorator:
                        meth = decorator(**v).__call__(meth)

                self.set_route(path, meth, methods=target.get("methods", []))
            else:
                # target is a static path
                # FIXME: handled via static directory routing, probably
                # redundant and not implemented at this point
                continue

@@ -885,33 +885,33 @@
    # DEPRECATE: 
-3
+4
 .0%0A        host 
@@ -1052,54 +1052,16 @@
 ed=%22
-Well be removed in 3.0, it's being replaced by
+4.0. Use
  the
@@ -1076,32 +1076,41 @@
 config attribute
+ instead.
 %22,%0A        )%0A%0A  
@@ -1128,17 +1128,17 @@
 RECATE: 
-3
+4
 .0%0A     
@@ -1275,54 +1275,16 @@
 ed=%22
-Will be removed in 3.0, it's being replaced by
+4.0. Use
  the
@@ -1307,16 +1307,25 @@
 ttribute
+ instead.
 %22,%0A     

fix #14: deprecation message for wsgi.host and wsgi.port