from parallels.core.logging import get_logger
from parallels.core import messages
from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.utils.plesk_utils import set_mime_types
from parallels.core.utils.restore_hosting_utils import restore_hosting_settings_lock
from parallels.core.utils.common.threading_utils import synchronized_by_lock

logger = get_logger(__name__)


class TransferMIMETypesBase(SubscriptionAction):
    def get_description(self):
        return "Transfer MIME types"

    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 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

        # filter only subscriptions w/ virtual hosting
        if not subscription.converted_dump.is_virtual_hosting:
            logger.debug(messages.SKIP_TRANSFER_MIME_TYPES_FOR_SUBSCRIPTION % subscription.name)
            return False

        return True

    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.FAILED_TRANSFER_MIME_TYPES_FOR_SUBSCRIPTION % subscription.name

    def run(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        logger.debug(messages.TRANSFER_MIME_TYPES_FOR_SITES_SUBSCRIPTION % subscription.name)

        for site in subscription.converted_dump.iter_domains():
            if not site.is_virtual_hosting:
                logger.debug(
                    messages.SKIP_TRANSFER_MIME_TYPES_FOR_SITE % (
                        site.name, subscription.name
                    )
                )
                continue

            if site.hosting_type == 'sub_hst':
                logger.debug(
                    messages.SKIP_TRANSFER_MIME_TYPES_FOR_SUBDOMAIN_ON_SUBFOLDER.format(
                        site=site.name, subscription=subscription.name
                    )
                )
                continue

            with global_context.safe.try_subscription(
                subscription.name,
                messages.TRANSFER_MIME_TYPES_SITE_S_AND % site.name,
                is_critical=False
            ):
                self._transfer_site_mime_types(global_context, subscription, site)

    def _transfer_site_mime_types(self, global_context, subscription, site):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        # Get MIME types from source panel
        vhost_mime_types = self._get_site_mime_types(global_context, subscription, site)
        vdirs_mime_types = self._get_site_vdir_mime_types(global_context, subscription, site)

        # Set MIME types on target panel
        self._restore_site_mime_types(site, subscription, vdirs_mime_types, vhost_mime_types)

    @staticmethod
    # we need to use this lock because calling "websrvmng" when Plesk restore is running could
    # result in "Access is denied. (Error code 5) at create mutex Global" error
    @synchronized_by_lock(lock=restore_hosting_settings_lock)
    def _restore_site_mime_types(site, subscription, vdirs_mime_types, vhost_mime_types):
        if vhost_mime_types is not None:
            logger.debug(messages.RESTORE_MIME_TYPES_VIRTUAL_HOSTS_ROOT)
            set_mime_types(
                subscription.web_target_server, site.name,
                vhost_mime_types
            )
        if vdirs_mime_types is not None:
            for vdir_name, vdir_mime_types in vdirs_mime_types.items():
                logger.debug(messages.RESTORE_MIME_TYPES_VIRTUAL_DIRECTORY_S, vdir_name)
                set_mime_types(
                    subscription.web_target_server, site.name,
                    vdir_mime_types, vdir_name
                )

    def _get_site_mime_types(self, global_context, subscription, site):
        """Get MIME types of a site

        Override in child classes.

        Returns dictionary with keys - extensions, values - corresponding MIME types.
        If there are no MIME types for a site - returns None (default behavior)

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        return None

    def _get_site_vdir_mime_types(self, global_context, subscription, site):
        """Get MIME types of all virtual directories inside a site

        Override in child classes.

        Returns dictionary with:
        - key - virtual directory path (as it is presented on target server)
        - value - dictionary with:
            - key - extensions
            - values - corresponding MIME types
        If there are no virtual directories with MIME types - returns None (default behavior)

        The dictionary looks in the following way:
        {
            virtual directory name: {
                extension: mime type
            }
        }

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        return None
