from parallels.core import messages
from parallels.core.actions.base.subscription_backup_action import SubscriptionBackupAction
from parallels.core.actions.utils.logging_properties import LoggingProperties
from parallels.core.reports.model.issue import Issue


class ChangeFTPLogin(SubscriptionBackupAction):
    """FTP user login may not conform to Plesk rules. Fix it in that case.

    Example of such FTP user login is FTP user of addon domain in cPanel: it is always an e-mail address,
    with "@" symbol in it, which is not allowed by Plesk.
    """

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

        :rtype: str
        """
        return messages.ACTION_CHANGE_FTP_LOGIN_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_CHANGE_FTP_LOGIN_FAILURE

    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 get_logging_properties(self):
        """Get how action should be logged to migration tools end-user

        :rtype: parallels.core.actions.utils.logging_properties.LoggingProperties
        """
        return LoggingProperties(info_log=False)

    def _run_subscription_backup(
        self, global_context, subscription, subscription_backup
    ):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        target_plesk_version = global_context.conn.target.plesk_server.plesk_version
        max_ftp_login_length = self._max_ftp_user_login_length(target_plesk_version)
        for ftp_user in subscription_backup.iter_ftp_users():
            fixed_name = self._fix_ftp_user_login(ftp_user.name, max_ftp_login_length)
            if ftp_user.name != fixed_name:
                global_context.safe.add_issue_subscription(subscription.name, Issue(
                    'invalid_ftp_user_login', Issue.SEVERITY_WARNING,
                    messages.FTP_LOGIN_DOES_NOT_CONFORM_PLESK_RULES.format(
                        login=ftp_user.name, fixed_login=fixed_name
                    )
                ))
                ftp_user.name = fixed_name
                if ftp_user.sysuser is not None:
                    ftp_user.sysuser.name = ftp_user.name

    @staticmethod
    def _max_ftp_user_login_length(version):
        """
        Return max allowed FTP user login length according to Plesk version
        :type version: tuple
        :return: int
        """
        if version >= (17, 8):
            return 32
        else:
            return 16

    @staticmethod
    def _fix_ftp_user_login(login, max_length):
        """Fix FTP user login according to Plesk requirements"""
        result = ""

        if len(login) == 0:
            return login

        if not login[0].isalpha():
            result += 'a'

        for char in login:
            if not (char.isalnum() or char in ('_', '-', '.')):
                result += '_'
            else:
                result += char

        return result[:max_length]
