from parallels.core import messages
from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.actions.post_migration_checks.utils import check_web_ip, should_run_post_migration_checks
from parallels.core.hosting_check.run_checks import run_entities
from parallels.core.logging import get_logger
from parallels.hosting_check import DomainFTPService, User, DomainRDPService, DomainSSHService

logger = get_logger(__name__)


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

        :rtype: str | unicode
        """
        return messages.ACTIONS_TEST_USERS_DESCRIPTION

    def get_failure_message(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        :rtype: str | unicode
        """
        return messages.ACTIONS_TEST_USERS_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 filter_subscription(self, global_context, subscription):
        """Check if we should run this action on given subscription or not

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        :rtype: bool
        """
        return should_run_post_migration_checks(global_context, 'users')

    def run(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        subscription_report = subscription.get_report(global_context.post_migration_check_report_writer)

        ftp_service_report = subscription_report.subtarget(u'FTP service', subscription.name)
        ftp_service_entities = self._get_ftp_check_entities(subscription)
        run_entities(global_context, ftp_service_entities, ftp_service_report)

        if subscription.web_target_server.is_windows():
            rdp_service_report = subscription_report.subtarget(u'RDP service', subscription.name)
            rdp_service_entities = self._get_rdp_check_entities(subscription, rdp_service_report)
            run_entities(global_context, rdp_service_entities, rdp_service_report)
        else:
            ssh_service_report = subscription_report.subtarget(u'SSH service', subscription.name)
            ssh_service_entities = self._get_ssh_check_entities(subscription)
            run_entities(global_context, ssh_service_entities, ssh_service_report)

    def _get_ftp_check_entities(self, subscription):
        """
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        if subscription.converted_dump.hosting_type == 'none':
            # if there is no web hosting then there are no FTP users, so nothing to check
            return []

        if subscription.web_target_server is None:
            # subscription w/o web cannot has FTP users, so nothing to check
            return []

        subscription_disabled = self._is_subscription_login_disabled(subscription.converted_dump)

        service = DomainFTPService(
            domain_name=subscription.name,
            web_server_ip=subscription.target_web_ip or subscription.target_web_ipv6,
            users=[],
            inactive_users=[]
        )

        users = [
            User(login, password, password_type)
            for login, password, password_type in self._iter_all_sysusers_info(
                subscription.converted_dump
            )
        ]
        # skip checking Windows for inactive user because of a bug: users of
        # suspended subscriptions still can login over FTP
        if subscription_disabled and not subscription.web_target_server.is_windows():
            service.inactive_users.extend(users)
        elif not subscription_disabled:
            service.users.extend(users)

        return [service]

    def _get_rdp_check_entities(self, subscription, report):
        """
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        :type report: parallels.core.reports.model.report.Report
        """
        result = []

        if not check_web_ip(report, subscription, subscription.name):
            return result

        sysuser = subscription.converted_dump.get_phosting_sysuser()
        login, password, _ = self._get_main_sysuser_info(subscription.converted_dump)

        if login is None or password is None:
            # If there is no system user (for example, web hosting is disabled
            # for the subscription), or we can not use password (for example it
            # is not set, or it is encrypted)
            return result

        web_target_server = subscription.web_target_server

        service = DomainRDPService(
            domain_name=subscription.name,
            web_server_ip=subscription.target_web_ip or subscription.target_web_ipv6,
            users=[],
            inactive_users=[],
            server=web_target_server,
        )
        user = User(login, password)
        if sysuser.has_shell_access:
            service.users.append(user)
        else:
            service.inactive_users.append(user)
        result.append(service)

        return result

    def _get_ssh_check_entities(self, subscription):
        """
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        sysuser = subscription.converted_dump.get_phosting_sysuser()
        login, password, password_type = self._get_main_sysuser_info(
            subscription.converted_dump
        )

        if login is None or password is None:
            # If there is no system user (for example, web hosting is disabled
            # for the subscription), or we can not use password (for example it
            # is not set, or it is encrypted)
            return []

        service = DomainSSHService(
            domain_name=subscription.name,
            web_server_ip=subscription.target_web_ip or subscription.target_web_ipv6,
            users=[],
            inactive_users=[]
        )
        user = User(login, password, password_type)
        if (
            not self._is_subscription_login_disabled(subscription.converted_dump)
            and
            sysuser.has_shell_access
        ):
            service.users.append(user)
        else:
            service.inactive_users.append(user)

        return [service]

    @staticmethod
    def _is_subscription_login_disabled(subscription):
        """Return True if login to subscription is enabled, False otherwise"""
        return not subscription.is_webspace_enabled

    @classmethod
    def _iter_all_sysusers_info(cls, subscription):
        """Iterate through tuples (login, password) of all system users"""
        for user in subscription.iter_system_users():
            yield cls._get_sysuser_info(user)

    @classmethod
    def _get_main_sysuser_info(cls, subscription):
        """Return tuple (login, password) of main system user"""
        return cls._get_sysuser_info(subscription.get_phosting_sysuser())

    @staticmethod
    def _get_sysuser_info(sysuser):
        """Return tuple (login, password, pasword type) of specified system user"""
        if sysuser is None:
            return None, None, None
        if sysuser.name is None or sysuser.name == '':
            return None, None, None

        return sysuser.name, sysuser.password.password_text, sysuser.password.password_type
