# SPDX-License-Identifier: AGPL-3.0-or-later
"""Configure MediaWiki."""

import os
import subprocess
import tempfile
from typing import Optional

from plinth.actions import privileged
from plinth.utils import generate_password

MAINTENANCE_SCRIPTS_DIR = "/usr/share/mediawiki/maintenance"
CONF_FILE = '/etc/mediawiki/FreedomBoxSettings.php'
LOCAL_SETTINGS_CONF = '/etc/mediawiki/LocalSettings.php'


def _get_php_command():
    """Return the PHP command that should be used on CLI.

    This is workaround for /usr/bin/php pointing to a different version than
    what php-defaults (and php-mbstring, php-xml) point to. See:
    https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=959742

    """
    version = ''

    try:
        process = subprocess.run(['dpkg', '-s', 'php'], stdout=subprocess.PIPE,
                                 check=True)
        for line in process.stdout.decode().splitlines():
            if line.startswith('Version:'):
                version = line.split(':')[-1].split('+')[0].strip()
    except subprocess.CalledProcessError:
        pass

    return f'php{version}'


@privileged
def setup():
    """Run the installer script to create database and configuration file."""
    data_dir = '/var/lib/mediawiki-db/'
    if not os.path.exists(data_dir):
        os.mkdir(data_dir)

    if not os.path.exists(os.path.join(data_dir, 'my_wiki.sqlite')) or \
       not os.path.exists(LOCAL_SETTINGS_CONF):
        install_script = os.path.join(MAINTENANCE_SCRIPTS_DIR, 'install.php')
        password = generate_password()
        with tempfile.NamedTemporaryFile() as password_file_handle:
            password_file_handle.write(password.encode())
            password_file_handle.flush()
            subprocess.check_call([
                _get_php_command(), install_script,
                '--confpath=/etc/mediawiki', '--dbtype=sqlite',
                '--dbpath=' + data_dir, '--scriptpath=/mediawiki',
                '--passfile', password_file_handle.name, 'Wiki', 'admin'
            ])
    subprocess.run(['chmod', '-R', 'o-rwx', data_dir], check=True)
    subprocess.run(['chown', '-R', 'www-data:www-data', data_dir], check=True)
    _include_custom_config()


def _include_custom_config():
    """Include FreedomBox specific configuration in LocalSettings.php."""
    with open(LOCAL_SETTINGS_CONF, 'r', encoding='utf-8') as conf_file:
        lines = conf_file.readlines()

    static_settings_index = None
    settings_index = None
    for line_number, line in enumerate(lines):
        if 'FreedomBoxSettings.php' in line:
            settings_index = line_number

        if 'FreedomBoxStaticSettings.php' in line:
            static_settings_index = line_number

    if settings_index is None:
        settings_index = len(lines)
        lines.append('include dirname(__FILE__)."/FreedomBoxSettings.php";\n')

    if static_settings_index is None:
        lines.insert(
            settings_index,
            'include dirname(__FILE__)."/FreedomBoxStaticSettings.php";\n')

    with open(LOCAL_SETTINGS_CONF, 'w', encoding='utf-8') as conf_file:
        conf_file.writelines(lines)


@privileged
def change_password(username: str, password: str):
    """Change the password for a given user."""
    change_password_script = os.path.join(MAINTENANCE_SCRIPTS_DIR,
                                          'changePassword.php')

    subprocess.check_call([
        _get_php_command(), change_password_script, '--user', username,
        '--password', password
    ])


@privileged
def update():
    """Run update.php maintenance script when version upgrades happen."""
    update_script = os.path.join(MAINTENANCE_SCRIPTS_DIR, 'update.php')
    subprocess.check_call([_get_php_command(), update_script, '--quick'])


@privileged
def public_registrations(command: str) -> Optional[bool]:
    """Enable or Disable public registrations for MediaWiki."""
    if command not in ('enable', 'disable', 'status'):
        raise ValueError('Invalid command')

    with open(CONF_FILE, 'r', encoding='utf-8') as conf_file:
        lines = conf_file.readlines()

    def is_pub_reg_line(line):
        return line.startswith("$wgGroupPermissions['*']['createaccount']")

    if command == 'status':
        conf_lines = list(filter(is_pub_reg_line, lines))
        return bool(conf_lines and 'true' in conf_lines[0])

    with open(CONF_FILE, 'w', encoding='utf-8') as conf_file:
        for line in lines:
            if is_pub_reg_line(line):
                words = line.split()
                if command == 'enable':
                    words[-1] = 'true;'
                else:
                    words[-1] = 'false;'
                conf_file.write(" ".join(words) + '\n')
            else:
                conf_file.write(line)

    return None


@privileged
def private_mode(command: str):
    """Enable or Disable Private mode for wiki."""
    if command not in ('enable', 'disable', 'status'):
        raise ValueError('Invalid command')

    with open(CONF_FILE, 'r', encoding='utf-8') as conf_file:
        lines = conf_file.readlines()

    def is_read_line(line):
        return line.startswith("$wgGroupPermissions['*']['read']")

    read_conf_lines = list(filter(is_read_line, lines))
    if command == 'status':
        return (read_conf_lines and 'false' in read_conf_lines[0])

    with open(CONF_FILE, 'w', encoding='utf-8') as conf_file:
        conf_value = 'false;' if command == 'enable' else 'true;'
        for line in lines:
            if is_read_line(line):
                words = line.split()
                words[-1] = conf_value
                conf_file.write(" ".join(words) + '\n')
            else:
                conf_file.write(line)

        if not read_conf_lines:
            conf_file.write("$wgGroupPermissions['*']['read'] = " +
                            conf_value + '\n')


def _update_setting(setting_name, setting_line):
    """Update the value of one setting in the config file."""
    with open(CONF_FILE, 'r', encoding='utf-8') as conf_file:
        lines = conf_file.readlines()

        inserted = False
        for i, line in enumerate(lines):
            if line.strip().startswith(setting_name):
                lines[i] = setting_line
                inserted = True
                break

        if not inserted:
            lines.append(setting_line)

    with open(CONF_FILE, 'w', encoding='utf-8') as conf_file:
        conf_file.writelines(lines)


@privileged
def set_default_skin(skin: str):
    """Set a default skin."""
    _update_setting('$wgDefaultSkin ', f'$wgDefaultSkin = "{skin}";\n')


@privileged
def set_server_url(server_url: str):
    """Set the value of $wgServer for this MediaWiki server."""
    # This is a required setting from MediaWiki 1.34
    _update_setting('$wgServer', f'$wgServer = "{server_url}";\n')


@privileged
def set_site_name(site_name: str):
    """Set the value of $wgSitename for this MediaWiki server."""
    _update_setting('$wgSitename', f'$wgSitename = "{site_name}";\n')
