from parallels.core.logging import get_logger
from parallels.core.runners.base import BaseRunner
from parallels.core.runners.entities import ExecutionOptions
from parallels.core.utils.common import safe_string_repr
from parallels.core.utils.windows_utils import get_binary_full_path
from parallels.hosting_check import DomainIssue
from parallels.hosting_check import Severity
from parallels.hosting_check import RDPAuthIssueType
from parallels.hosting_check.messages import MSG
from parallels.hosting_check.utils.list_utils import none_list

logger = get_logger(__name__)


class RDPAuthChecker(object):
    """Check RDP access for users

    RDP auth checker is simple. It checks only that users are assigned to a
    group. In the future it may have more complex checks by actually trying to
    log in to a system by RDP, but they are much more complex to implement (and
    complex to make them work on all platforms).
    """
    def check(self, domains_to_check):
        issues = []

        for domain_to_check in domains_to_check:
            self._check_single_domain(domain_to_check, issues)
        return issues

    def _check_single_domain(self, domain_to_check, issues):
        if not self._check_web_ip_present(domain_to_check, issues):
            return 

        if domain_to_check.server is None:
            return

        for user in domain_to_check.users:
            try:
                user_in_group = self._check_rdp_group(
                    domain_to_check.server, domain_to_check.web_server_ip, user
                )
                if not user_in_group:
                    issues.append(DomainIssue(
                        domain_name=domain_to_check.domain_name, 
                        severity=Severity.ERROR, 
                        category=RDPAuthIssueType.ACTIVE_USER_NOT_CONFIGURED, 
                        problem=MSG(
                            RDPAuthIssueType.ACTIVE_USER_NOT_CONFIGURED,
                            server_ip=domain_to_check.web_server_ip,
                            login=user.login, 
                            reason=MSG('rdp_auth_user_not_in_group')
                        )
                    ))
            except KeyboardInterrupt:
                # for compatibility with python 2.4
                raise
            except Exception as e:
                logger.exception()
                issues.append(DomainIssue(
                    domain_name=domain_to_check.domain_name,
                    severity=Severity.WARNING, 
                    category=RDPAuthIssueType.INTERNAL_ERROR,
                    problem=MSG(
                        RDPAuthIssueType.INTERNAL_ERROR,
                        server_ip=domain_to_check.web_server_ip,
                        login=user.login, error_message=safe_string_repr(e)
                    )
                ))
        for user in none_list(domain_to_check.inactive_users):
            try:
                user_in_group = self._check_rdp_group(
                    domain_to_check.server, domain_to_check.web_server_ip, user
                )
                if user_in_group:
                    issues.append(DomainIssue(
                        domain_name=domain_to_check.domain_name, 
                        severity=Severity.ERROR, 
                        category=RDPAuthIssueType.INACTIVE_USER_CONFIGURED, 
                        problem=MSG(
                            RDPAuthIssueType.INACTIVE_USER_CONFIGURED,
                            server_ip=domain_to_check.web_server_ip,
                            login=user.login,
                            reason=MSG('rdp_auth_user_in_group')
                        )
                    ))
            except KeyboardInterrupt:
                # for compatibility with python 2.4
                raise
            except Exception as e:
                logger.exception()
                issues.append(DomainIssue(
                    domain_name=domain_to_check.domain_name,
                    severity=Severity.WARNING, 
                    category=RDPAuthIssueType.INTERNAL_ERROR,
                    problem=MSG(
                        RDPAuthIssueType.INTERNAL_ERROR,
                        server_ip=domain_to_check.web_server_ip,
                        login=user.login, error_message=safe_string_repr(e)
                    )
                ))

    @staticmethod
    def _check_web_ip_present(domain_to_check, issues):
        if domain_to_check.web_server_ip is None:
            issues.append(
                DomainIssue(
                    domain_name=domain_to_check.domain_name,
                    severity=Severity.WARNING, 
                    category=RDPAuthIssueType.WEB_IP_NOT_DEFINED,
                    problem=MSG(RDPAuthIssueType.WEB_IP_NOT_DEFINED)
                )
            )
            return False
        return True

    @classmethod
    def _check_rdp_group(cls, server, server_ip, user):
        with server.runner() as runner:
            assert isinstance(runner, BaseRunner)
            logger.debug(MSG(
                'rdp_log_checking',
                user=user.login, server_ip=server_ip
            ))
            users_list = runner.execute_command(
                '{net_bin} localgroup "Remote Desktop Users"',
                dict(net_bin=get_binary_full_path(server, 'net')),
                ExecutionOptions(output_encoding=runner.native_output_encoding)
            ).stdout
            return user.login in users_list
