""" Distribution specific override class for CentOS family (RHEL, Fedora) """
import logging
from typing import Any

from certbot import errors
from certbot import util
from certbot._internal.plugins.apache import apache_util
from certbot._internal.plugins.apache import configurator
from certbot._internal.plugins.apache import parser
from certbot._internal.plugins.apache.configurator import OsOptions

logger = logging.getLogger(__name__)


class CentOSConfigurator(configurator.ApacheConfigurator):
    """CentOS specific ApacheConfigurator override class"""

    OS_DEFAULTS = OsOptions(
        server_root="/etc/httpd",
        vhost_root="/etc/httpd/conf.d",
        vhost_files="*.conf",
        logs_root="/var/log/httpd",
        ctl="apachectl",
        apache_bin="httpd",
        version_cmd=['apachectl', '-v'],
        restart_cmd=['apachectl', 'graceful'],
        restart_cmd_alt=['apachectl', 'restart'],
        conftest_cmd=['apachectl', 'configtest'],
        challenge_location="/etc/httpd/conf.d",
    )

    def config_test(self) -> None:
        """
        Override config_test to mitigate configtest error in vanilla installation
        of mod_ssl in Fedora. The error is caused by non-existent self-signed
        certificates referenced by the configuration, that would be autogenerated
        during the first (re)start of httpd.
        """

        os_info = util.get_os_info()
        fedora = os_info[0].lower() == "fedora"

        try:
            super().config_test()
        except errors.MisconfigurationError:
            if fedora:
                self._try_restart_fedora()
            else:
                raise

    def _rhel9_or_newer(self) -> bool:
        os_name, os_version = util.get_os_info()
        rhel_derived = os_name in [
            "centos", "centos linux",
            "cloudlinux",
            "ol", "oracle",
            "rhel", "redhatenterpriseserver", "red hat enterprise linux server",
            "scientific", "scientific linux",
        ]
        # It is important that the loose version comparison below is not made
        # if the OS is not RHEL derived. See
        # https://github.com/certbot/certbot/issues/9481.
        if not rhel_derived:
            return False
        at_least_v9 = util.parse_loose_version(os_version) >= util.parse_loose_version('9')
        return at_least_v9

    def _override_cmds(self) -> None:
        super()._override_cmds()

        # As of RHEL 9, apachectl can't be passed flags like "-v" or "-t -D", so
        # instead use options.bin (i.e. httpd) for version_cmd and the various
        # get_X commands
        if self._rhel9_or_newer():
            if not self.options.bin:
                raise ValueError("OS option apache_bin must be set for CentOS") # pragma: no cover

            self.options.version_cmd[0] = self.options.bin
            self.options.get_modules_cmd[0] = self.options.bin
            self.options.get_includes_cmd[0] = self.options.bin
            self.options.get_defines_cmd[0] = self.options.bin

        if not self.options.restart_cmd_alt:  # pragma: no cover
            raise ValueError("OS option restart_cmd_alt must be set for CentOS.")
        self.options.restart_cmd_alt[0] = self.options.ctl

    def _try_restart_fedora(self) -> None:
        """
        Tries to restart httpd using systemctl to generate the self signed key pair.
        """

        try:
            util.run_script(['systemctl', 'restart', 'httpd'])
        except errors.SubprocessError as err:
            raise errors.MisconfigurationError(str(err))

        # Finish with actual config check to see if systemctl restart helped
        super().config_test()

    def get_parser(self) -> "CentOSParser":
        """Initializes the ApacheParser"""
        return CentOSParser(
            self.options.server_root, self, self.options.vhost_root, self.version)


class CentOSParser(parser.ApacheParser):
    """CentOS specific ApacheParser override class"""
    def __init__(self, *args: Any, **kwargs: Any) -> None:
        # CentOS specific configuration file for Apache
        self.sysconfig_filep: str = "/etc/sysconfig/httpd"
        super().__init__(*args, **kwargs)

    def update_runtime_variables(self) -> None:
        """ Override for update_runtime_variables for custom parsing """
        # Opportunistic, works if SELinux not enforced
        super().update_runtime_variables()
        self.parse_sysconfig_var()

    def parse_sysconfig_var(self) -> None:
        """ Parses Apache CLI options from CentOS configuration file """
        defines = apache_util.parse_define_file(self.sysconfig_filep, "OPTIONS")
        for k, v in defines.items():
            self.variables[k] = v
