from parallels.core import messages
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.actions.pre_migration_checks.utils import should_run_service_checks
from parallels.core.hosting_check.run_checks import run_entities
from parallels.core.logging import get_logger
from parallels.hosting_check import Service
from parallels.hosting_check.panel_services import WindowsWebServiceBase, LinuxWebServiceBase, WindowsFTPServiceBase, \
    LinuxFTPService, WindowsMailServiceBase, LinuxMailServiceBase, WindowsDNSService, LinuxDNSService, \
    create_windows_plesk_service, create_linux_plesk_service, LinuxMysqlService, LinuxPostgresqlService, \
    WindowsMysqlService, WindowsMssqlServiceBase

logger = get_logger(__name__)


LinuxDBServices = {
    'mysql': LinuxMysqlService,
    'postgresql': LinuxPostgresqlService
}

WindowsDBServices = {
    'mysql': WindowsMysqlService,
    'mssql': WindowsMssqlServiceBase
}


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

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

    def get_failure_message(self, global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :rtype: str | unicode
        """
        return messages.ACTION_TEST_SERVICES_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_action(self, global_context):
        """Check whether we should run this action or not

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        return should_run_service_checks(global_context)

    def run(self, global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        services = self._get_service_check_entities(global_context)
        for service in services:
            report = global_context.test_services_report.subtarget(
                'Service', '%s on %s' % (service.service.service_type, service.description)
            )
            run_entities(global_context, [service], report)

    @staticmethod
    def _get_service_check_entities(global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :rtype: list[parallels.hosting_check.Service]
        """
        service_nodes = []

        def _add_service_node(service_node):
            for _service_node in service_nodes:
                if (
                    service_node.description == _service_node.description and
                    service_node.service == _service_node.service
                ):
                    return
            service_nodes.append(service_node)

        for target_service in global_context.target_panel_obj.get_service_nodes(global_context.conn.target):
            if target_service.service_type == 'web':
                web_service = WindowsWebServiceBase if target_service.node.is_windows() else LinuxWebServiceBase
                _add_service_node(
                    Service(
                        host=target_service.node.ip(),
                        description=target_service.node.description(),
                        server=target_service.node,
                        service=web_service
                    )
                )

                ftp_service = WindowsFTPServiceBase if target_service.node.is_windows() else LinuxFTPService
                _add_service_node(
                    Service(
                        host=target_service.node.ip(),
                        description=target_service.node.description(),
                        server=target_service.node,
                        service=ftp_service
                    )
                )
            elif target_service.service_type == 'mail':
                mail_service = WindowsMailServiceBase if target_service.node.is_windows() else LinuxMailServiceBase
                _add_service_node(
                    Service(
                        host=target_service.node.ip(),
                        description=target_service.node.description(),
                        server=target_service.node,
                        service=mail_service
                    )
                )
            elif target_service.service_type == 'dns':
                dns_service = WindowsDNSService if target_service.node.is_windows() else LinuxDNSService
                _add_service_node(
                    Service(
                        host=target_service.node.ip(),
                        description=target_service.node.description(),
                        server=target_service.node,
                        service=dns_service
                    )
                )
            else:
                db_services = WindowsDBServices if target_service.node.is_windows() else LinuxDBServices
                for db_type, db_service in db_services.items():
                    if target_service.service_type == db_type:
                        _add_service_node(
                            Service(
                                host=target_service.node.ip(),
                                description=target_service.node.description(),
                                server=target_service.node,
                                service=db_service
                            )
                        )

        if global_context.conn.target.is_windows:
            plesk_service = create_windows_plesk_service(global_context.conn.target.panel_port)
        else:
            plesk_service = create_linux_plesk_service(global_context.conn.target.panel_port)

        _add_service_node(
            Service(
                host=global_context.conn.target.main_node_ip,
                description=global_context.conn.target.main_node_description(),
                server=global_context.conn.target.plesk_server,
                service=plesk_service
            )
        )

        return service_nodes
