# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Configuration parser and default values for configuration options.
"""

import configparser
import logging
import os
import pathlib

logger = logging.getLogger(__name__)

# [Path] section
file_root = '/usr/share/plinth'
config_dir = '/etc/plinth'
data_dir = '/var/lib/plinth'
custom_static_dir = '/var/www/plinth/custom/static'
store_file = data_dir + '/plinth.sqlite3'
actions_dir = '/usr/share/plinth/actions'
doc_dir = '/usr/share/freedombox'
server_dir = '/plinth'

# [Network] section
host = '127.0.0.1'
port = 8000

# Enable the following only if Plinth is behind a proxy server.  The
# proxy server should properly clean and the following HTTP headers:
#   X-Forwarded-For
#   X-Forwarded-Host
#   X-Forwarded-Proto
# If you enable these unnecessarily, this will lead to serious security
# problems. For more information, see
# https://docs.djangoproject.com/en/1.7/ref/settings/
#
# These are enabled by default in FreedomBox because the default
# configuration allows only connections from localhost
#
# Leave the values blank to disable
use_x_forwarded_for = True
use_x_forwarded_host = True
secure_proxy_ssl_header = 'HTTP_X_FORWARDED_PROTO'

# [Misc] section
box_name = 'FreedomBox'

# Other globals
develop = False

config_files = []


def expand_to_dot_d_paths(file_paths):
    """Expand a list of file paths to include file.d/* also."""
    final_list = []
    for file_path in file_paths:
        final_list.append(str(file_path))
        path = pathlib.Path(file_path)
        path_d = path.with_suffix(path.suffix + '.d')
        try:
            for dot_d_file in sorted(path_d.glob('*' + path.suffix)):
                final_list.append(str(dot_d_file))
        except Exception as exception:
            logger.warning('Unable to read from directory %s: %s', path_d,
                           exception)

    return final_list


def get_develop_config_path():
    """Return config path of current source folder for development mode."""
    root_directory = os.path.dirname(os.path.realpath(__file__))
    root_directory = os.path.realpath(root_directory)
    config_path = os.path.join(root_directory, 'develop.config')
    return config_path


def get_config_paths():
    """Get default config paths."""
    return [
        '/usr/share/freedombox/freedombox.config',
        '/etc/plinth/plinth.config',
        '/etc/freedombox/freedombox.config',
    ]


def read():
    """Read all configuration files."""
    config_paths = get_config_paths()
    for config_path in expand_to_dot_d_paths(config_paths):
        read_file(config_path)


def read_file(config_path):
    """Read and merge into defaults a single configuration file."""
    if not os.path.isfile(config_path):  # Does not throw exceptions
        # Ignore missing configuration files
        return

    # Keep a note of configuration files read.
    config_files.append(config_path)

    parser = configparser.ConfigParser(
        defaults={
            'parent_dir':
                pathlib.Path(config_path).parent.resolve(),
            'parent_parent_dir':
                pathlib.Path(config_path).parent.parent.resolve(),
        })
    parser.read(config_path)  # Ignores all read errors

    config_items = (
        ('Path', 'file_root', 'string'),
        ('Path', 'config_dir', 'string'),
        ('Path', 'data_dir', 'string'),
        ('Path', 'custom_static_dir', 'string'),
        ('Path', 'store_file', 'string'),
        ('Path', 'actions_dir', 'string'),
        ('Path', 'doc_dir', 'string'),
        ('Path', 'server_dir', 'string'),
        ('Network', 'host', 'string'),
        ('Network', 'port', 'int'),
        ('Network', 'secure_proxy_ssl_header', 'string'),
        ('Network', 'use_x_forwarded_for', 'bool'),
        ('Network', 'use_x_forwarded_host', 'bool'),
        ('Misc', 'box_name', 'string'),
    )

    for section, name, datatype in config_items:
        try:
            value = parser.get(section, name)
        except (configparser.NoSectionError, configparser.NoOptionError):
            # Use default values for any missing keys in configuration
            continue
        else:
            if datatype == 'int':
                value = int(value)
            elif datatype == 'bool':
                value = (value.lower() == 'true')

            globals()[name] = value
