#!/usr/bin/python3
import contextlib
import inspect
import logging
import os
import signal

from mini_buildd import cli, config, httpd, net, util, webapp

LOG = logging.getLogger("mini_buildd")

#: Needed for man page hack in setup.py
DESCRIPTION = "Once minimal Debian build and repository daemon"


class HttpDRun:
    """Small wrapper allowing us to use contextlib.closing()"""

    def __init__(self, app):
        self.httpd = httpd.HttpD(wsgi_app=app, minthreads=config.MIN_HTTPD_THREADS, maxthreads=config.MAX_HTTPD_THREADS)
        self.httpd.start()

    def close(self):
        self.httpd.shutdown()
        self.httpd.join()


class CLI(cli.CLI):
    SHUTDOWN = [signal.SIGTERM, signal.SIGINT, signal.SIGHUP]

    def _setup(self):
        """Set global variables that really make no sense to propagate through"""
        config.DEBUG = self.args.debug.split(",")
        if self.args.hostname is not None:
            config.HOSTNAME_FQDN = self.args.hostname
        config.HOSTNAME = config.HOSTNAME_FQDN.partition(".")[0]

        if self.args.http_endpoint is not None:
            config.HTTP_ENDPOINTS = self.args.http_endpoint
        config.ROUTES = config.Routes(self.args.home)

        # Overwrite default shutdown signal handlers with no-ops (else, SIGHUP would just crash it).
        for s in self.SHUTDOWN:
            signal.signal(s, lambda x, y: None)

    def _setup_environment(self):
        os.environ.clear()
        os.environ["HOME"] = self.args.home
        os.environ["PATH"] = "/usr/bin:/bin:/usr/sbin:/sbin"
        os.environ["LANG"] = "C.UTF-8"
        for name in ["USER", "LOGNAME"]:
            os.environ[name] = self._user

    def __init__(self):
        super().__init__("mini-buildd",
                         DESCRIPTION,
                         epilog="See also: Online manual, usually at http://localhost:8066/static/manual/.",
                         run_as_mini_buildd=True)

        group_conf = self.parser.add_argument_group("daemon arguments")

        group_conf.add_argument("-N", "--hostname", "--fqdn",
                                action="store",
                                help="Custom public (fully qualified) hostname used for all services (uses host's FQDN if none given).")

        group_conf.add_argument("-E", "--http-endpoint", action="store", nargs="+",
                                help=f"""\
HTTP Server Endpoint:

{inspect.getdoc(net.ServerEndpoint)}

You may give multiple endpoints -- the first endpoint given will be the primary.

(default: {config.HTTP_ENDPOINTS}), not""")
        group_conf.add_argument("-S", "--smtp", action="store", default=":@smtp://localhost:25",
                                help="SMTP credentials in format '[USER]:[PASSWORD]@smtp|ssmtp://HOST:PORT'.")
        group_conf.add_argument("-H", "--home", action="store", default="~",
                                help="Run with this home dir (you may use '~' for user expansion). The only use case to change this for debugging, really.")

        group_log = self.parser.add_argument_group("logging and debugging arguments")
        group_log.add_argument("-d", "--debug", action="store", default="", metavar="OPTION,..",
                               help="""\
Comma-separated list of special debugging options:
'webapp' (put web application [django] in debug mode),
'sbuild' (run sbuild in debug mode),
'keep' (keep temporary directories).""")

        group_db = self.parser.add_argument_group("database arguments")
        group_db.add_argument("-P", "--set-admin-password", action="store", metavar="PASSWORD",
                              help="Update password for superuser ('admin'); user is created if non-existent yet.")
        group_db.add_argument("-D", "--dumpdata", action="store", metavar="APP[.MODEL]",
                              help="Dump database contents for app[.MODEL] as JSON file (see 'django-admin dumpdata').")
        group_db.add_argument("-L", "--loaddata", action="store", metavar="FILE",
                              help="INTERNAL USE ONLY, use with care! Load JSON file into database (see 'django-admin loaddata').")
        group_db.add_argument("-R", "--remove-system-artifacts", action="store_true",
                              help="INTERNAL USE ONLY, use with care! Bulk-remove associated data of all objects that might have produced artifacts on the system.")

    def setup(self):
        # reproducible builds: Expand these for run, leave static for usage (as this is used to build man pages) (thx to Chris Lamb, see Debian Bug #833340)
        self.args.home = os.path.expanduser(self.args.home)

        self._setup()
        self._setup_environment()

        # Configure django
        webapp.configure(self.args.smtp)
        webapp.init()

    def runcli(self):
        self.setup()

        if self.args.set_admin_password:
            webapp.set_admin_password(self.args.set_admin_password)
        elif self.args.remove_system_artifacts:
            webapp.remove_system_artifacts()
        elif self.args.loaddata:
            webapp.loaddata(self.args.loaddata)
        elif self.args.dumpdata:
            webapp.dumpdata(self.args.dumpdata)
        else:
            util.daemon()  # Be sure to initially create Daemon singleton _before_ starting web services (avoid potential race on Singleton creation)

            with contextlib.closing(HttpDRun(webapp.get())):
                util.attempt(util.daemon().mbd_start)
                signal.sigwait(self.SHUTDOWN)
                util.daemon().mbd_stop()


if __name__ == "__main__":
    CLI().run()
