# -*- coding: utf-8 -*-
#
# Copyright (C) 2018-2022 Matthias Klumpp <matthias@tenstral.net>
#
# Licensed under the GNU Lesser General Public License Version 3
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the license, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this software.  If not, see <http://www.gnu.org/licenses/>.

import os
import sys

import tomlkit

thisfile = __file__
if not os.path.isabs(thisfile):
    thisfile = os.path.normpath(os.path.join(os.getcwd(), thisfile))


__all__ = ['GlobalConfig']


class GlobalConfig:
    '''
    Global configuration singleton affecting all of Debspawn.
    '''

    _instance = None

    class __GlobalConfig:
        def load(self, fname=None):
            if not fname:
                fname = '/etc/debspawn/global.toml'

            cdata = {}
            if os.path.isfile(fname):
                with open(fname) as f:
                    try:
                        cdata = tomlkit.load(f)
                    except tomlkit.exceptions.ParseError as e:
                        print(
                            'Unable to parse global configuration (global.toml): {}'.format(str(e)),
                            file=sys.stderr,
                        )
                        sys.exit(8)

            self._dsrun_path = os.path.normpath(os.path.join(thisfile, '..', 'dsrun'))
            if not os.path.isfile(self._dsrun_path):
                print(
                    'Debspawn is not set up properly: Unable to find file "{}". Can not continue.'.format(
                        self._dsrun_path
                    ),
                    file=sys.stderr,
                )
                sys.exit(4)

            self._osroots_dir = cdata.get('OSImagesDir', '/var/lib/debspawn/images/')
            self._results_dir = cdata.get('ResultsDir', '/var/lib/debspawn/results/')
            self._aptcache_dir = cdata.get('APTCacheDir', '/var/lib/debspawn/aptcache/')
            self._injected_pkgs_dir = cdata.get('InjectedPkgsDir', '/var/lib/debspawn/injected-pkgs/')
            self._temp_dir = cdata.get('TempDir', '/var/tmp/debspawn/')
            self._default_bootstrap_variant = cdata.get('DefaultBootstrapVariant', 'buildd')
            self._allow_unsafe_perms = cdata.get('AllowUnsafePermissions', False)
            self._cache_packages = bool(cdata.get('CachePackages', True))
            self._bootstrap_tool = cdata.get('BootstrapTool', 'debootstrap')

            self._syscall_filter = cdata.get('SyscallFilter', 'compat')
            if self._syscall_filter == 'compat':
                # permit some system calls known to be needed by packages that sbuild & Co.
                # build without problems.
                self._syscall_filter = ['@memlock', '@pkey', '@clock', '@cpu-emulation']
            elif self._syscall_filter == 'nspawn-default':
                # make no additional changes, so nspawn's built-in defaults are used
                self._syscall_filter = []
            else:
                if type(self._syscall_filter) is not list:
                    print(
                        (
                            'Configuration error (global.toml): Entry "SyscallFilter" needs to be either a '
                            'string value ("compat" or "nspawn-default"), or a list of permissible '
                            'system call names as listed by the syscall-filter command of systemd-analyze(1)'
                        ),
                        file=sys.stderr,
                    )
                    sys.exit(8)

        @property
        def dsrun_path(self) -> str:
            return self._dsrun_path

        @dsrun_path.setter
        def dsrun_path(self, v):
            self._dsrun_path = v

        @property
        def osroots_dir(self) -> str:
            return self._osroots_dir

        @property
        def results_dir(self) -> str:
            return self._results_dir

        @property
        def aptcache_dir(self) -> str:
            return self._aptcache_dir

        @property
        def injected_pkgs_dir(self) -> str:
            return self._injected_pkgs_dir

        @property
        def temp_dir(self) -> str:
            return self._temp_dir

        @property
        def default_bootstrap_variant(self) -> str:
            return self._default_bootstrap_variant

        @property
        def syscall_filter(self) -> list:
            """Customize which syscalls should be filtered."""
            return self._syscall_filter

        @property
        def allow_unsafe_perms(self) -> bool:
            """Whether usage of unsafe permissions is allowed."""
            return self._allow_unsafe_perms

        @property
        def cache_packages(self) -> bool:
            """Whether APT packages should be cached by debspawn."""
            return self._cache_packages

        @property
        def bootstrap_tool(self) -> str:
            """The chroot bootstrap tool that we should use."""
            return self._bootstrap_tool

    def __init__(self, fname=None):
        if not GlobalConfig._instance:
            GlobalConfig._instance = GlobalConfig.__GlobalConfig()
            GlobalConfig._instance.load(fname)

    def __getattr__(self, name):
        return getattr(self._instance, name)
