from parallels.core.logging import get_logger
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.actions.utils.logging_properties import LoggingProperties
from parallels.core.reports.model.issue import Issue
from parallels.core.utils.common import format_list
from parallels.plesk import messages
from parallels.plesk.utils.plesk_components import get_installed_components

logger = get_logger(__name__)


class CheckMissingComponents(CommonAction):
    """Pre-migration check for missing Perl/Python components on target Plesk for Windows

    We need to perform such check because:
    1) Since Plesk 12.5 Perl and Python can not be installed automatically via Plesk autoinstaller
    due to the license reasons.
    2) It is common to have Perl/Python enabled on old source servers for practically every subscription,
    even if it does not actually use it. Actually the issue should affect only small amount of domains.
    3) Plesk restore for hosting settings emits and *error* message for *every* domain with Perl/Python enabled.
    4) Plesk restore for APS applications also report the issue.
    4) Capability checks report the issue too.

    So, to avoid many error messages (for every subscription - 2 for hosting settings restoration,
    2 for APS applications, 2 for capability checks) in migration and pre-migration reports,
    we control this situation ourselves, without Plesk restore and Plesk capability checker.

    See also:
    parallels.plesk.actions.hosting_settings.convert.remove_missing_components.RemoveMissingComponents
    """
    def get_description(self):
        return messages.CHECK_MISSING_COMPONENTS_DESCRIPTION

    def get_failure_message(self, global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        return messages.CHECK_MISSING_COMPONENTS_FAILURE

    def is_critical(self):
        """If action is critical or not

        If action is critical and it failed, migration tool completely stops.
        Otherwise it proceeds to the next steps of migrations.

        This action affects only error reporting, so it is not critical, always return False.

        :rtype: bool
        """
        return False

    def filter_action(self, global_context):
        """Check whether we should run this action or not. Applicable only for Plesk for Windows.

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :rtype: bool
        """
        return global_context.conn.target.is_windows

    def get_logging_properties(self):
        return LoggingProperties(info_log=False)

    def run(self, global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        for component, installed in get_installed_components(global_context.conn.target.plesk_server).items():
            if component in {'perl', 'python'} and not installed:
                domains = self.get_domains_with_component(global_context, component)
                if len(domains) > 0:
                    # Add the issue to pre-migration report's general section, not per-subscription section
                    global_context.pre_check_report.add_issue(
                        'missing_component_%s' % component,
                        Issue.SEVERITY_WARNING, messages.MISSING_COMPONENT_ISSUE.format(
                            component=component, domains=format_list([d.name for d in domains])
                        ),
                        messages.MISSING_COMPONENT_SOLUTION
                    )

    @staticmethod
    def get_domains_with_component(global_context, component):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type component: str
        :rtype: list
        """
        domains = []
        for subscription in global_context.iter_all_subscriptions():
            for domain in subscription.raw_dump.iter_domains():
                if domain.scripting is not None and domain.scripting.is_option_enabled(component):
                    domains.append(domain)
        return domains