# Copyright 2022 The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Config reader for the debusine clients."""
import sys
from configparser import ConfigParser
from pathlib import Path


class ConfigHandler(ConfigParser):
    """
    ConfigHandler for the configuration (.ini file) of the Debusine client.

    If the configuration file is not valid it writes an error message
    to stderr and aborts.
    """

    DEFAULT_CONFIG_FILE_PATH = Path.home() / Path(
        '.config/debusine/client/config.ini'
    )

    def __init__(
        self,
        *,
        server_name=None,
        config_file_path=DEFAULT_CONFIG_FILE_PATH,
        stdout=sys.stdout,
        stderr=sys.stderr,
    ):
        """
        Initialize variables and reads the configuration file.

        :param server_name: None for the default server from the configuration
        :param config_file_path: location of the configuration file
        """
        super().__init__()

        self._server_name = server_name
        self._config_file_path = config_file_path

        self._stdout = stdout
        self._stderr = stderr

        if not self.read(self._config_file_path):
            self._fail(f'Cannot read {self._config_file_path} .')

    def server_configuration(self):
        """
        Return the server configuration.

        Uses the server specified in the __init__() or the default server
        in the configuration file.
        """
        if self._server_name is None:
            server_name = self._default_server_name()
        else:
            server_name = self._server_name

        return self._server_configuration(server_name)

    def _fail(self, message):
        """Write message to self._stderr and aborts with exit code 3."""
        self._stderr.write(message + "\n")
        raise SystemExit(3)

    def _default_server_name(self):
        """Return default server name or aborts."""
        if 'General' not in self:
            self._fail(
                f'[General] section and default-server key must exist '
                f'in {self._config_file_path} to use the default server. '
                f'Add them or specify the server in the command line.'
            )

        if 'default-server' not in self['General']:
            self._fail(
                f'default-server key must exist in [General] in '
                f'{self._config_file_path} . Add it or specify the server '
                f'in the command line.'
            )

        return self['General']['default-server']

    def _server_configuration(self, server_name):
        """Return configuration for server_name or aborts."""
        section_name = f'server:{server_name}'

        if section_name not in self:
            self._fail(
                f'[{section_name}] section not found '
                f'in {self._config_file_path} .'
            )

        server_configuration = self[section_name]
        self._ensure_server_configuration(server_configuration, section_name)

        return server_configuration

    def _ensure_server_configuration(
        self, server_configuration, server_section_name
    ):
        """Check keys for server_section_name. Aborts if there are errors."""
        keys = ('url', 'token')

        missing_keys = keys - server_configuration.keys()

        if len(missing_keys) > 0:
            missing_keys = ', '.join(sorted(missing_keys))
            self._fail(
                f'Missing required keys in the section '
                f'[{server_section_name}]: {missing_keys} in '
                f'{self._config_file_path} .'
            )

        extra_keys = server_configuration.keys() - keys

        if len(extra_keys) > 0:
            extra_keys = ', '.join(sorted(extra_keys))
            self._fail(
                f'Invalid keys in the section '
                f'[{server_section_name}]: {extra_keys} in '
                f'{self._config_file_path} .'
            )

        return True
