import enum
import os
import sys
import socket
import re

from mini_buildd import util, files

#: This should never ever be changed
CHAR_ENCODING = "UTF-8"

#: Compute python-version dependent install path
PY_PACKAGE_PATH = f"/usr/lib/python{sys.version_info[0]}/dist-packages"

#: Global constant object to use when signalling shutdown via message queues
SHUTDOWN = "SHUTDOWN"

#: Config for HTTPD (twisted) and event queue clients
MIN_HTTPD_THREADS = 10
MAX_HTTPD_THREADS = 100
MAX_EVENTS_QUEUE_CLIENTS = 70

#: HTTPD log file names
LOG_FILE = "daemon.log"
ACCESS_LOG_FILE = "access.log"

#: (Debian package) path for the internal package templates
PACKAGE_TEMPLATES = "/usr/share/mini-buildd/package-templates"

#: Sample message used for instance handshakes
HANDSHAKE_MESSAGE = "Hand Shake"


class AuthType(enum.Enum):
    NONE = enum.auto()
    LOGIN = enum.auto()
    STAFF = enum.auto()
    ADMIN = enum.auto()


class Auth():
    def __init__(self, auth):
        self.auth = auth

    def __str__(self):
        return self.auth.name

    def is_authorized(self, user):
        """Check if django user is authorized"""
        def active_login():
            return user.is_authenticated and user.is_active

        if self.auth == AuthType.NONE:
            return True
        if user is None:
            return False
        if self.auth == AuthType.LOGIN and active_login():
            return True
        if self.auth == AuthType.STAFF and active_login() and user.is_staff:
            return True
        if self.auth == AuthType.ADMIN and active_login() and user.is_superuser:
            return True
        return False


#: Auth shortcuts
AUTH_NONE = Auth(AuthType.NONE)
AUTH_LOGIN = Auth(AuthType.LOGIN)
AUTH_STAFF = Auth(AuthType.STAFF)
AUTH_ADMIN = Auth(AuthType.ADMIN)


class Uri:
    """URI string with some convenience functionality"""

    DJANGO_PATH_REGEX = r"(?:(?P<path>.*))?"

    def __init__(self, uri, auth=AUTH_NONE, with_index=False, cache_ttl=0, regex=None, django_with_path=False):
        self.uri = uri

        #: Auth
        self.auth = auth

        #: Enable directory listing
        self.with_index = with_index

        #: Browser cache TTL in seconds (0 for no cache)
        self.cache_ttl = cache_ttl

        #: Restrict access via regex
        if regex is not None:
            self.regex = re.compile(r"^" + self.join(regex))
        else:
            self.regex = regex

        #: Non-static: Add 'path' to django URI
        self.django_with_path = django_with_path

    def __str__(self):
        return self.uri

    def django(self):
        return self.uri.lstrip("/") + (self.DJANGO_PATH_REGEX if self.django_with_path else "")

    def twisted(self):
        return self.uri.strip("/")

    def join(self, *args, prefix=""):
        return os.path.join(prefix, self.uri, *args)

    def url_join(self, *args, endpoint=None):
        ep = util.http_endpoint() if endpoint is None else endpoint
        return ep.geturl(path=self.join(*args))

    @classmethod
    def uri2view(cls, uri):
        return uri.replace("static", "mini_buildd", 1)

    def to_view(self):
        return Uri(self.uri2view(self.uri))


#: Static URI dict (by route name)
#: 'view': The default (django).
#: 'dir': Directory Listing (django).
#: 'static': Static delivery (twisted).
URIS = {
    # Note (cache_ttl=0): If browser cache should be enabled at some point, then here. However, we have no "hashed-on-change" support and it's confusing if
    # 'though F5, js still running old code'. Also a real PITA in most browsers to clear the cache.
    "static": {
        "static": Uri("/static/static/", cache_ttl=0),
    },
    "home": {
        "view": Uri("/mini_buildd/"),
    },
    "setup": {
        "view": Uri("/mini_buildd/setup/"),
    },
    "events": {
        "view": Uri("/mini_buildd/events/", django_with_path=True),
        "dir": Uri("/mini_buildd/events-dir/", django_with_path=True),
        "static": Uri("/static/events/", with_index=True),
        "attach": Uri("/static/events.attach"),
    },
    "builds": {
        "dir": Uri("/mini_buildd/builds-dir/", django_with_path=True),
        "static": Uri("/static/builds/", with_index=True),
    },
    "repositories": {
        "view": Uri("/mini_buildd/repositories/", django_with_path=True),
        "dir": Uri("/mini_buildd/repositories-dir/", django_with_path=True),
        "static": Uri("/repositories/", with_index=True, regex=r"(?![^/]+/(conf/.*|db/.*))"),
    },
    "builders": {
        "view": Uri("/mini_buildd/builders/"),
    },
    "crontab": {
        "view": Uri("/mini_buildd/crontab/"),
    },
    "api": {
        "view": Uri("/mini_buildd/api/"),
    },
    "log": {
        "view": Uri("/mini_buildd/log/", auth=AUTH_STAFF, django_with_path=True),
        "static": Uri("/static/log/", auth=AUTH_STAFF, with_index=True),
    },
    "admin": {
        "view": Uri("/admin/"),
    },
    "manual": {
        "static": Uri("/static/manual/"),
        "view": Uri("/mini_buildd/manual/"),
    },
    "sitemap": {
        "view": Uri("/mini_buildd/sitemap/"),
    },
    "accounts": {
        "base": Uri("/accounts/"),               # used to include django standard auth urls under, see urls.py
        "login": Uri("/accounts/login/"),        # is standard django, but we need this URI when we login internally, see net.py
        "register": Uri("/accounts/register/"),
        "activate": Uri("/accounts/activate/"),
        "profile": Uri("/accounts/profile/"),
        "null": Uri("/accounts/null/"),
    },
    "homepage": {
        "view": Uri("http://mini-buildd.installiert.net/"),
    },
}


class Route:
    """Link a path (may be None, may be run-time) to uris (may be empty)"""

    def __init__(self, path=None, uris=None):
        self.path = path
        self.uris = {} if uris is None else uris

    def static_uri(self, path=""):
        uri = self.uris.get("static")
        return uri.join(path) if uri else None


class Routes(dict):
    def _add(self, name, path):
        self[name] = Route(path, uris=URIS.get(name))

    def __init__(self, home):
        super().__init__()
        self.home = home

        def homepth(path):
            return os.path.join(self.home, path)

        self._add("home", files.Path(self.home))
        self._add("etc", files.Path(homepth("etc"), create=True))
        self._add("setup", None)
        self._add("static", files.Path(f"{PY_PACKAGE_PATH}/mini_buildd/static"))

        # 'var/shared': Shared with chroots
        self._add("builds", files.Path(homepth("var/shared/builds"), create=True))   # Build directory
        self._add("libdir", files.Path(homepth("var/shared/libdir"), create=True))   # Shared libdir: Can optionally be used for persistent shared data (like ccache)

        self._add("events", files.Path(homepth("var/events"), create=True))
        self._add("event_hooks", files.Path(homepth("etc/event-hooks"), create=True))

        self._add("repositories", files.Path(homepth("repositories"), create=True))
        self._add("chroots", files.Path(homepth("var/chroots"), create=True))
        self._add("builders", None)
        self._add("crontab", files.Path(homepth("var/crontab"), create=True))
        self._add("sitemap", None)
        self._add("api", None)

        self._add("log", files.Path(homepth("var/log"), create=True))

        self._add("tmp", files.Path(homepth("var/tmp"), create=True))
        self._add("incoming", files.Path(homepth("incoming"), create=True))

        self._add("manual", files.Path(os.path.realpath("/usr/share/doc/mini-buildd/html")))


#: Variable items (see mini-buildd main script)
DEBUG = []
HOSTNAME = socket.gethostname()
HOSTNAME_FQDN = socket.getfqdn()
#: HTTP endpoints strings
HTTP_ENDPOINTS = ["tcp6:port=8066"]
ROUTES = {}


def default_ftp_endpoint():
    """Compute default ftp endpoint string from main http endpoint"""
    port = util.http_endpoint().options.get("port")
    return util.http_endpoint().description.replace(f"port={port}", f"port={int(port)+1}")


def default_identity():
    return HOSTNAME
