#!/usr/bin/python3

from pathlib import Path

from nginx.config.api import Section, Location, EmptyBlock, Comment, KeyValueOption, KeyMultiValueOption
from nginx.config.helpers import duplicate_options

PROXY_OPTIONS = [
        ["Host", "$http_host"],
        ["X-Real-IP", "$remote_addr"],
        ["X-Forwarded-For", "$proxy_add_x_forwarded_for"],
        ["X-Forwarded-Host", "$host,443"],
        ["X-Forwarded-Server", "$host"],
        ["X-Forwarded-Proto", "$scheme"],
        ["X-Forwarded-Uri", "$request_uri"],
        ["X-Forwarded-Ssl", "on"],
        ["X-Real-IP", "$remote_addr"]
]

FPM_OPTIONS = [
    ["HTTPS", "on"],
    ["PATH_INFO", "$path_info"],
    ["SCRIPT_FILENAME", "$document_root$fastcgi_script_name"],
    ["modHeadersAvailable", "true"],
    ["front_controller_active", "true"]
]

PROXY_WEBSOCKETS_OPTIONS = [
    ["Upgrade", "$http_upgrade"],
    ["Connection", "Upgrade"]
    ]


class NginxSiteBuilder:

    def __init__(self, domain, site_number=0, root="/srv/www/htdocs",
                 authelia=True):

        self._filename = f"{site_number:02d}-{domain}.conf"
        self._domain = domain
        self._add_base_template(authelia)
        self.add_logging()

    def __str__(self):
        return str(self._config)

    __repr__ = __str__

    def _add_base_template(self, authelia=True):
        server = Section(
            "server",
            duplicate_options("listen", [["443 ssl http2"],
                                         ["[::]:443 ssl http2"]]),
            server_name=self.domain,
            server_tokens="off"
        )
        blocks = [["common_ssl.conf"]]
        if authelia:
            blocks.append(["authelia.conf"])
        includes = duplicate_options("include", blocks)
        server.sections.add(includes)

        self._config = server

    @property
    def filename(self):
        return self._filename

    @property
    def domain(self):
        return self._domain

    @property
    def config(self):
        return self._config

    def write(self, path: Path):
        destination = Path / self.filename
        with destination.open("w") as handle:
            handle.write(str(self._config))

    def add_logging(self, suffix="nginx_"):

        syslog = f"syslog:server=unix:/dev/log,tag={self.domain}_nginx,"
        access = syslog + "severity=info,nohostname"
        error = syslog + "severity=error,nohostname"

        logs = EmptyBlock(
            access_log=access,
            error_log=error)
        self._config.sections.add(logs)

    def add_proxied_location(self, location="/", address="127.0.0.1",
                             port=8443, websocket=True, auth=True):

        location = Location(location,
                            proxy_pass=f"http://{address}:{port}",
                            gzip="off")

        if auth:
            location.sections.add(KeyValueOption("include", "auth.conf"))

        proxy_options = duplicate_options(
            "proxy_set_header",
            PROXY_OPTIONS
            )

        if websocket:
            comment = Comment(comment="Websockets")
            proxy_options.sections.add(comment)
            http_version = EmptyBlock(proxy_http_version="1.1")
            websocket_cfg = duplicate_options(
                "proxy_set_header",
                PROXY_WEBSOCKETS_OPTIONS
            )
            http_version.sections.add(websocket_cfg)
            proxy_options.sections.add(http_version)

        location.sections.add(proxy_options)
        self._config.sections.add(location)

    def add_php_try_files(self, location="/"):

        location = Location(location,
                            try_files=["$uri", "$uri/",
                                       f"{location}index.php?$query_string"])

        self._config.sections.add(location)

    def add_php_block(self):

        location = Location(r"~ ^(?<script_name>.+?\.php)(?<path_info>/.*)?$",
                            try_files="$script_name = 404",
                            include="fastcgi_params",
                            fastcgi_pass="unix:/run/php-fpm/php-fpm.sock",
                            fastcgi_hide_header="X-Powered-By")

        location.sections.add(duplicate_options("fastcgi_param", FPM_OPTIONS))

        self._config.sections.add(location)