import re
import ntpath

from parallels.core import messages
from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.runners.base import BaseRunner
from parallels.core.runners.entities import ExecutionOptions
from parallels.core.utils.windows_utils import get_system_root


class FixWebConfigIntegratedMode(SubscriptionAction):
    """Fix legacy web.config files so they could work in Integrated ASP.NET mode

    IIS 6 by has Classic mode only. IIS 7 has Integrated mode by default.
    web.config file has different layout of settings for Classic and Integrated modes,
    which makes web.config file from Classic mode not to work in Integrated mode.
    To resolve that issue, we update web.config files with "appcmd migrate config" command, so it
    could work in both modes.

    Example of configuration file before fix:
    =====================================================================
    <configuration>
        <system.web>
            <httpHandlers>
                <add verb="*" path="*My.axd" type="MyHttpHandler"/>
            </httpHandlers>
            <httpModules>
                <add name="MyModule" type="MyHttpModule"/>
            </httpModules>
        </system.web>
    </configuration>
    =====================================================================
    After fix:
    =====================================================================
    <configuration>
        <system.web>
            <httpHandlers>
                <add verb="*" path="*My.axd" type="MyHttpHandler" />
            </httpHandlers>
            <httpModules>
                <add name="My1Module" type="MyHttpModule" />
            </httpModules>
        </system.web>
        <system.webServer>
            <handlers>
                <add
                    name="*My.axd_*" path="*My.axd" verb="*" type="MyHttpHandler"
                    preCondition="integratedMode,runtimeVersionv2.0" />
            </handlers>
        </system.webServer>
    </configuration>
    =====================================================================
    """
    def get_description(self):
        """Get short description of action as string

        :rtype: str
        """
        return messages.FIX_WEB_CONFIG_INTEGRATED_MODE_ACTION_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.FIX_WEB_CONFIG_INTEGRATED_MODE_ACTION_FAILURE

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

        if not subscription.converted_dump.is_virtual_hosting:
            # filter only subscriptions w/ virtual hosting
            return False

        # We do not run this action for IDN domains, as there are problems running "appcmd"
        # utility for them. For example, "appcmd list" simply does not output national symbols,
        # only ASCII symbols of domain name are displayed, making domains names completely wrong in the output.
        # Also, it does not accept domains names in punycode.
        # Considering that there should be not too much IDN domains on old IIS,
        # which are affected by Classic to Integrated mode switch, we do not run the action for such domains.
        for domain in subscription.converted_dump.iter_domains():
            if domain.is_idn:
                return False

        return True

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

        If action is critical and it failed for a subscription, migration tool
        won't run the next operations for the subscription.

        :rtype: bool
        """
        return False

    def run(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        with subscription.web_target_server.runner() as runner:
            assert isinstance(runner, BaseRunner)
            for domain in subscription.converted_dump.iter_domains():
                if not domain.is_virtual_hosting:
                    continue

                # List all applications of domain
                appcmd_bin = ntpath.join(get_system_root(subscription.web_target_server), r'system32\inetsrv\appcmd')
                app_lines = runner.execute_command(
                    r'{appcmd_bin} list app /site.name:{site_name}',
                    dict(
                        site_name=domain.name,
                        appcmd_bin=appcmd_bin
                    )
                ).stdout.splitlines()

                for app_line in app_lines:
                    m = re.match('^APP\s"(.*?)"\s.*', app_line)
                    if m is not None:
                        # Run web.config conversion for that application
                        app_name = m.group(1)

                        # We intentionally ignore exit code here. If "appcmd migrate config" has not made any changes,
                        # it returns non-zero exit code, and that is ok for applications that is already
                        # in Integrated mode.
                        runner.execute_command(
                            r'{appcmd_bin} migrate config {app_name}',
                            dict(
                                app_name=app_name,
                                appcmd_bin=appcmd_bin
                            ),
                            ExecutionOptions(ignore_exit_code=True)
                        )
