# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright (C) 2020 Freie Universität Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.

import json
import os
import tempfile
import time

import base


__author__ = "Martine S. Lenders"
__copyright__ = "Copyright (C) 2020 Freie Universität Berlin"
__credits__ = ["Martine S. Lenders"]
__license__ = "LGPLv2.1"
__maintainer__ = "Martine S. Lenders"
__email__ = "m.lenders@fu-berlin.de"


class KeaConfig(object):
    def __init__(self, interface, prefix, prefix_len, delegated_len=None,
                 valid_lifetime=40000, preferred_lifetime=30000,
                 renew_timer=10000, rebind_timer=20000):
        if not prefix.endswith("::"):
            raise ValueError("prefix must end with '::'")
        if int(prefix_len) < 1 or int(prefix_len) > 128:
            raise ValueError("prefix_len must be between 1 and 128")
        if int(valid_lifetime) <= 0:
            raise ValueError("valid_lifetime must be greater than 0")
        if int(preferred_lifetime) <= 0:
            raise ValueError("preferred_lifetime must be greater than 0")
        if int(renew_timer) <= 0:
            raise ValueError("renew_timer must be greater than 0")
        if int(rebind_timer) <= 0:
            raise ValueError("rebind_timer must be greater than 0")
        if delegated_len is None:
            delegated_len = prefix_len
        self._config_dict = {
            "Dhcp6": {
                "interfaces-config": {
                    "interfaces": [interface]
                },
                "lease-database": {
                    "type": "memfile"
                },
                "valid-lifetime": int(valid_lifetime),
                "preferred-lifetime": int(preferred_lifetime),
                "renew-timer": int(renew_timer),
                "rebind-timer": int(rebind_timer),
                "subnet6": [{
                    "interface": interface,
                    "subnet": "{}/{}".format(prefix, prefix_len),
                    "pd-pools": [{
                        "prefix": prefix,
                        "prefix-len": prefix_len,
                        "delegated-len": delegated_len
                    }],
                }],
            },
        }
        self.config_file = None

    def __del__(self):
        if self.config_file is not None:
            self.config_file.close()

    def _dump_json(self):
        json.dump(self._config_dict, self.config_file)
        self.config_file.flush()

    def __str__(self):
        if self.config_file is None:
            self.config_file = tempfile.NamedTemporaryFile(mode="w")
        self._dump_json()
        return self.config_file.name

    @property
    def interface(self):
        return self._config_dict["Dhcp6"]["interfaces-config"]["interfaces"][0]


class KeaServer(base.DHCPv6Server):
    command = ["kea-dhcp6", "-c"]
    package = {
            "generic": {"name": "kea-dhcp6", "url": "https://kea.isc.org/"},
            "Debian": {"name": "kea-dhcp6-server"},
            "Arch": {"name": "kea"},
        }

    def __init__(self, config, next_hop="fe80::2", *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.next_hop = next_hop
        self.config = config

    def pre_run(self):
        # create config file in daemon so it is not automatically deleted
        self.command.append(str(self.config))
        if self.daemonized:
            # need to wait for interface to connect before we can run server
            time.sleep(2)

    def run(self):
        if not self.is_installed():
            self.install()
            if self.installer.os in ["Arch"] and \
               not os.path.exists("/var/run/kea/"):
                # workaround: Arch does not create that directory on first
                # install
                os.makedirs("/var/run/kea/")
        # workaround: Ubuntu does not create that directory automatically
        os.makedirs("/run/kea/", exist_ok=True)
        super().run()
