from parallels.core.logging import get_logger
from parallels.core import messages
from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.actions.utils.multithreading_properties import MultithreadingProperties
from parallels.core.reports.model.issue import Issue
from parallels.core.utils import plesk_api_utils
from parallels.plesk.hosting_repository.subscription import UserAlreadyExistsException, LoginIsInvalidException

logger = get_logger(__name__)


class RestoreSysuserLogins(SubscriptionAction):
    """Restore system user login on target panel.

    The default workflow for system user logins is the following:
    1. Subscription is created with *some* system user login. It may be the same as on source system,
    or it may be auto generated by migrator or target panel.
    2. During subscription creation, migrator with target panel take care of new login uniqueness and correctness.
    So even if login from source system already exists on target system within another subscription,
    subscription is still created. Moreover, no error messages are emitted.
    3. Then, in this action we try to change login to the original one from source system.
    If update fails, we detect situations when:
    - such login already exists
    - login is not valid for target system
    and report them to subscription's report that is displayed to migrator's user.
    4. But anyway, even if there was an issue when restoring system user login, migration will go further.
    If restored login is not the same as on source system, it will be updated in backup at ChangeSysuserLogin action.
    """
    def get_description(self):
        return messages.RESTORE_LOGINS_SYSTEM_USERS_1

    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_RESTORE_LOGIN_SYSTEM_USER_FOR % (subscription.name)

    def get_multithreading_properties(self):
        """Get how multithreading should be applied for that action

        :rtype: parallels.core.actions.utils.multithreading_properties.MultithreadingProperties
        """
        return MultithreadingProperties(can_use_threads=True)

    def run(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        sysuser_name = subscription.raw_dump.phosting_sysuser_name
        if sysuser_name is None:
            logger.debug(messages.THERE_IS_NO_SYSTEM_USER_LOGIN)
            return

        for char in global_context.target_panel_obj.sysuser_name_disallowed_chars:
            # replace all disallowed characters to underline
            sysuser_name = sysuser_name.replace(char, '_')

        target_sysuser_name = plesk_api_utils.get_subscription_sysuser_name(
            subscription.panel_target_server.plesk_api(), subscription.name
        )

        if target_sysuser_name is None:
            # subscription has no system user on target
            # now we simply ignore that case, but probably it would be
            # better to handle such situation better to avoid issues
            # on the next steps of migration
            logger.debug(messages.THERE_IS_NO_SYSTEM_USER_LOGIN_1)
            return

        if target_sysuser_name != sysuser_name:
            try:
                global_context.hosting_repository.subscription.set_username(subscription.name, sysuser_name)
            except UserAlreadyExistsException:
                subscription.add_report_issue(
                    global_context.pre_check_report,
                    'unable_to_restore_original_sysuser_name_already_exists', Issue.SEVERITY_WARNING,
                    messages.UNABLE_RESTORE_ORIGINAL_USERNAME_FOR_SYSTEM % (subscription.name, sysuser_name),
                    messages.NOTIFY_SUBSCRIPTION_OWNER_NEW_FTP_USERNAME % target_sysuser_name
                )
            except LoginIsInvalidException:
                subscription.add_report_issue(
                    global_context.pre_check_report,
                    'unable_to_restore_original_sysuser_name_does_not_conform_to_target_rules',
                    Issue.SEVERITY_WARNING,
                    messages.UNABLE_RESTORE_ORIGINAL_USERNAME_S_FOR % (sysuser_name, subscription.name),
                    messages.NOTIFY_SUBSCRIPTION_OWNER_NEW_FTP_USERNAME_1 % target_sysuser_name
                )
