import re
from typing import Any, Optional

import tmt.log
import tmt.utils
from tmt.container import container, field
from tmt.guest import Guest
from tmt.steps.prepare.feature import PrepareFeatureData, ToggleableFeature, provides_feature

SUPPORTED_DISTRO_PATTERNS = tuple(
    re.compile(pattern)
    for pattern in (r'Red Hat Enterprise Linux .*(7|8|9|10)', r'CentOS Stream (8|9|10)')
)


@container
class FipsStepData(PrepareFeatureData):
    fips: Optional[str] = field(
        default=None,
        option='--fips',
        metavar='enabled',
        help='Whether FIPS mode should be enabled',
    )


@provides_feature('fips')
class Fips(ToggleableFeature):
    """
    Enable FIPS mode on the guest.

    Enable FIPS mode on RHEL 7, 8, 9 and 10 and CentOS Stream
    8, 9 and 10 systems.

    .. code-block:: yaml

        prepare:
            how: feature
            fips: enabled

    .. code-block:: shell

        prepare --how feature --fips enabled

    .. note::

       In order to prevent issues with installation of packages signed by
       non-FIPS-compliant algorithms we recommend enabling FIPS mode after
       package installation prepare steps. Use ``order:`` to enforce that.

    .. note::

       This feature plugin is not supported with container provisioner
       or with ostree-based distributions, e.g. Image Mode RHEL,
       Fedora CoreOS, etc.
    """

    _data_class = FipsStepData

    PLAYBOOKS = {'fips-enable.yaml'}

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, **kwargs)

    @classmethod
    def disable(cls, guest: Guest, logger: tmt.log.Logger) -> None:
        raise tmt.utils.GeneralError('FIPS prepare feature does not support \'disabled\'.')

    @classmethod
    def enable(cls, guest: Guest, logger: tmt.log.Logger) -> None:
        if guest.facts.is_container or guest.facts.is_ostree or guest.facts.is_image_mode:
            raise tmt.utils.GeneralError(
                'FIPS prepare feature is not supported on container, ostree,'
                ' or image mode systems.'
            )
        if not (
            guest.facts.distro
            and any(pattern.match(guest.facts.distro) for pattern in SUPPORTED_DISTRO_PATTERNS)
        ):
            raise tmt.utils.GeneralError(
                'FIPS prepare feature is supported on RHEL 7 and RHEL/CentOS-Stream 8, 9 or 10.'
            )
        cls._run_playbook('enable', 'fips-enable.yaml', guest, logger)
