from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.migrator_config import read_adjust_applications_enabled
from parallels.core.reports.model.issue import Issue
from parallels.core.utils.common import find_first, safe_format
from parallels.core.utils.plesk_utils import get_domain_dot_net_version
from parallels.core.utils.web_config_file import WebConfigFile
from parallels.plesk import messages


class FixWebConfigDotNet2To4(SubscriptionAction):
    def get_description(self):
        """Get short description of action as string

        :rtype: str
        """
        return messages.ACTION_FIX_WEB_CONFIG_DOT_NET_2_TO_4_DESCRIPTION

    def get_failure_message(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        return messages.ACTION_FIX_WEB_CONFIG_DOT_NET_2_TO_4_FAILURE

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

    def filter_subscription(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        # filter only subscriptions on windows physical servers
        source_info = subscription.get_source_info()
        if not source_info.is_server or not source_info.is_windows:
            return False

        return read_adjust_applications_enabled(global_context.config)

    def run(self, global_context, subscription):
        """Run action on given subscription

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        :rtype: None
        """
        for domain in subscription.converted_dump.iter_domains():
            self._run_for_domain(global_context, subscription, domain)

    @staticmethod
    def _run_for_domain(global_context, subscription, domain):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        :type domain: parallels.core.dump.data_model.Domain
        """
        if domain.scripting is None:
            # Do not handle domains which don't have scripting options: forwarding and no hosting domains
            return

        source_dot_net_version = find_first(
            domain.scripting.options, lambda option: option.name == 'managed_runtime_version'
        )

        # Note: we still try to modify web.config files if .NET version is not defined
        # (which means that ASP.NET is disabled), as web.config may exist for domains
        # with no .NET enabled, and it could contain data that must be changed to avoid
        # errors when restoring custom error documents.
        if source_dot_net_version is not None and source_dot_net_version.value is not None:
            try:
                source_version = tuple(int(v) for v in source_dot_net_version.value.split('.'))
            except ValueError:
                # Invalid ASP.NET version format: handle in the same way as absent ASP.NET version
                source_version = None
        else:
            source_version = None

        if source_version is not None and not ((2, 0) <= source_version < (4, 0)):
            # Need to fix files only when source .NET version is 2 - 3.5
            return

        target_version = get_domain_dot_net_version(subscription.panel_target_server, domain.name)

        if target_version is not None and target_version < (4, 0):
            # Need to fix files only when target .NET version is >= 4
            return

        FixWebConfigDotNet2To4._fix_web_config_files(global_context, subscription, domain)

    @staticmethod
    def _fix_web_config_files(global_context, subscription, domain):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        :type domain: parallels.core.dump.data_model.Domain
        """
        for web_config_file in WebConfigFile.iter_for_domain(subscription, domain):
            changed = False

            for config_sections_elem in web_config_file.xml.findall('configSections'):
                for section_group_elem in config_sections_elem.findall('sectionGroup'):
                    if section_group_elem.attrib.get('name') == 'system.web.extensions':
                        config_sections_elem.remove(section_group_elem)
                        changed = True

            if changed:
                web_config_file.save()

                subscription_report = global_context.application_adjust_report.subtarget(
                    'Subscription', subscription.name
                )
                subscription_report.add_issue(
                    'web_config_upgrade_dot_net_2_to_4',
                    Issue.SEVERITY_INFO, safe_format(
                        messages.UPGRADED_WEB_CONFIG_FILE, filename=web_config_file.filename
                    )
                )
