import os

from parallels.core.logging import get_logger
from parallels.core import messages
from parallels.core.registry import Registry
from parallels.core.utils.common import is_run_on_windows
from parallels.core.utils.migrator_utils import is_locked
from parallels.core.utils.windows_utils import is_run_with_elevated_privileges
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.actions.utils.logging_properties import LoggingProperties

from parallels.core.utils.json_utils import encode_json


logger = get_logger(__name__)


class CheckUpgradeAllowedAction(CommonAction):
    def get_description(self):
        return messages.ACTION_CHECK_UPGRADE_ALLOWED

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

    def run(self, global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        result = not is_locked()

        if result is True and is_run_on_windows() and is_run_with_elevated_privileges():
            result = self._is_directory_tree_availability(Registry.get_instance().get_base_dir())

        if Registry.get_instance().get_context().options.quiet:
            print(encode_json(result))
        else:
            if result:
                logger.info(messages.ACTION_CHECK_UPGRADE_ALLOWED_YES)
            else:
                logger.error(messages.ACTION_CHECK_UPGRADE_ALLOWED_NO)

    def get_logging_properties(self):
        """Get how action should be logged to migration tools end-user"""
        return LoggingProperties(info_log=False)

    def _is_directory_tree_availability(self, root_path):
        if not self._is_directory_availability(root_path):
            return False
        for root, dirs, files in os.walk(root_path):
            for name in dirs:
                if not self._is_directory_availability(os.path.join(root, name)):
                    return False
        return True

    @staticmethod
    def _is_directory_availability(directory_path):
        import win32file
        import pywintypes

        result = False

        handle = None
        try:
            handle = win32file.CreateFile(
                directory_path,
                win32file.GENERIC_WRITE,
                0,
                None,
                win32file.OPEN_EXISTING,
                win32file.FILE_FLAG_BACKUP_SEMANTICS,
                0
            )
            result = True
        except pywintypes.error as error:
            logger.debug(messages.ACTION_CHECK_UPGRADE_ALLOWED_DIRECTORY_UNAVAILABLE.format(
                directory_path=directory_path,
                error_message=error.strerror
            ))
        finally:
            if handle is not None:
                win32file.CloseHandle(handle)

        return result
