import re
import os.path
import logging

import twisted.internet.endpoints
import twisted.web.wsgi
import twisted.web.static
import twisted.web.resource
import twisted.logger
import twisted.python.logfile

import mini_buildd.misc
import mini_buildd.config

LOG = logging.getLogger(__name__)


class Site(twisted.web.server.Site):
    def _openLogFile(self, path):  # noqa (pep8 N802)
        return twisted.python.logfile.LogFile(os.path.basename(path), directory=os.path.dirname(path), rotateLength=5000000, maxRotatedFiles=9)


class RootResource(twisted.web.resource.Resource):
    """Twisted root resource needed to mix static and wsgi resources."""

    def __init__(self, wsgi_resource):
        super().__init__()
        self._wsgi_resource = wsgi_resource

    def getChild(self, path, request):  # noqa (pep8 N802)
        request.prepath.pop()
        request.postpath.insert(0, path)
        return self._wsgi_resource


class FileResource(twisted.web.static.File):
    """Twisted static resource enhanced with switchable index and regex matching support."""

    def __init__(self, *args, with_index=False, uri_regex=".*", **kwargs):
        super().__init__(*args, **kwargs)
        self.mbd_with_index = with_index
        self.mbd_uri_regex = re.compile(uri_regex)

    def directoryListing(self):  # noqa (pep8 N802)
        if not self.mbd_with_index:
            return self.forbidden
        return super().directoryListing()

    def getChild(self, path, request):  # noqa (pep8 N802)
        if not self.mbd_uri_regex.match(request.uri.decode("utf-8")):
            return self.forbidden
        child = super().getChild(path, request)
        child.mbd_with_index = self.mbd_with_index
        child.mbd_uri_regex = self.mbd_uri_regex
        return child


class HttpD():
    DOC_MISSING_HTML = """\
<html><body>
<h1>Online Manual Not Available (<tt>mini-buildd-doc</tt> not installed?)</h1>
Package <b><tt>mini-buildd-doc</tt></b> is not installed on this site (might be intentional to save space).
<p><a href="/">[Back]</a></p>
</body></html>
"""

    def _add_static_route(self, route, directory, with_index=False, uri_regex=".*", with_doc_missing_error=False):
        static = FileResource(with_index=with_index, uri_regex=uri_regex, path=directory)

        if with_doc_missing_error:
            static.childNotFound = twisted.web.resource.NoResource(self.DOC_MISSING_HTML)

        for k, v in self._mime_types.items():
            static.contentTypes[f".{k}"] = v
        self.resource.putChild(bytes(route, encoding=mini_buildd.config.CHAR_ENCODING), static)

    def __init__(self, wsgi_app):
        mime_text_plain = f"text/plain; charset={mini_buildd.config.CHAR_ENCODING}"
        self._mime_types = {"log": mime_text_plain,
                            "buildlog": mime_text_plain,
                            "changes": mime_text_plain,
                            "dsc": mime_text_plain}

        # Note: This import selects and initializes the twisted's default reactor.
        # Note: damonize() functionality (as currently used in mini-buildd) will break this if run before this import.
        from twisted.internet import reactor
        self.reactor = reactor

        # Bend twisted (not access.log) logging to ours
        twisted.logger.globalLogPublisher.addObserver(twisted.logger.STDLibLogObserver(name=__name__))

        # HTTP setup
        self.resource = RootResource(twisted.web.wsgi.WSGIResource(self.reactor, self.reactor.getThreadPool(), wsgi_app))
        self.site = Site(self.resource, logPath=mini_buildd.config.ACCESS_LOG_FILE)

        for ep in mini_buildd.config.HTTP_ENDPOINTS:
            twisted.internet.endpoints.serverFromString(self.reactor, ep.description).listen(self.site)

        # Custom static routes
        self._add_static_route("static", f"{mini_buildd.config.PY_PACKAGE_PATH}/mini_buildd/static")                                       # WebApp static directory
        self._add_static_route("doc", mini_buildd.config.MANUAL_DIR, with_doc_missing_error=True)                                                     # HTML manual
        self._add_static_route("repositories", mini_buildd.config.REPOSITORIES_DIR, with_index=True, uri_regex=r"^/repositories/.+/(pool|dists)/.*")  # Repositories
        self._add_static_route("log", mini_buildd.config.LOG_DIR, with_index=True, uri_regex=r"^/log/.+/.*")                                          # Logs

    def run(self):
        self.reactor.run(installSignalHandlers=0)
