"""Utility function to run hosting checks"""

from parallels.core.logging import get_logger
from parallels.core import messages, safe_utf8_encode
from parallels.core.hosting_check.config import MigratorHostingCheckersConfig
from parallels.core.reports.model.issue import Issue
from parallels.core.utils.common import get_executable_file_name, open_no_inherit, cached
from parallels.hosting_check import Service
from parallels.hosting_check import DomainMySQLDatabases
from parallels.hosting_check import DomainMSSQLDatabases
from parallels.hosting_check import DomainPostgreSQLDatabases
from parallels.hosting_check import DomainWebService
from parallels.hosting_check import DomainFTPService
from parallels.hosting_check import DomainSSHService
from parallels.hosting_check import DomainRDPService
from parallels.hosting_check import DomainMailService
from parallels.hosting_check import DomainDNSService
from parallels.hosting_check import DomainDNSForwarding
from parallels.hosting_check import DomainDNSExternal
from parallels.hosting_check.checkers.dns_checker import DNSChecker
from parallels.hosting_check.checkers.dns_external_checker import DNSExternalChecker
from parallels.hosting_check.checkers.dns_forwarding_checker import DNSForwardingChecker
from parallels.hosting_check.checkers.ftp_auth_checker import FTPAuthChecker
from parallels.hosting_check.checkers.mail_auth_checker import MailAuthChecker
from parallels.hosting_check.checkers.mssql_database_checker import MSSQLDatabaseChecker
from parallels.hosting_check.checkers.mysql_database_checker import MySQLDatabaseChecker
from parallels.hosting_check.checkers.postgresql_database_checker import PostgreSQLDatabaseChecker
from parallels.hosting_check.checkers.rdp_auth_checker import RDPAuthChecker
from parallels.hosting_check.checkers.service_checker import ServiceChecker
from parallels.hosting_check.checkers.ssh_auth_checker import SSHAuthChecker
from parallels.hosting_check.checkers.website_availability_checker import WebsiteAvailabilityChecker
from parallels.hosting_check.messages import get_solutions

logger = get_logger(__name__)


def create_checkers(config):
    """
    :type config: parallels.core.hosting_check.config.HostingCheckersConfig
    :rtype: dict[Entity, object]
    """
    return {
        Service: ServiceChecker(),
        DomainMySQLDatabases: MySQLDatabaseChecker(),
        DomainMSSQLDatabases: MSSQLDatabaseChecker(),
        DomainPostgreSQLDatabases: PostgreSQLDatabaseChecker(),
        DomainWebService: WebsiteAvailabilityChecker(
            config.website_availability_check_timeout, config.website_availability_depth,
            config.website_availability_group_directories
        ),
        DomainFTPService: FTPAuthChecker(),
        DomainSSHService: SSHAuthChecker(),
        DomainRDPService: RDPAuthChecker(),
        DomainMailService: MailAuthChecker(config.messages_delta_limit),
        DomainDNSService: DNSChecker(
            save_external_report_data_function=config.save_external_report_data_function,
            dns_server_name=config.dns_server_name,
            difference_file_panel_id=config.panel_file_id
        ),
        DomainDNSForwarding: DNSForwardingChecker(
            save_external_report_data_function=config.save_external_report_data_function,
        ),
        DomainDNSExternal: DNSExternalChecker(
            external_dns_servers=config.external_dns_servers,
            save_external_report_data_function=config.save_external_report_data_function,
        ),
    }


def run_entities(global_context, entities, report):
    """Run post-migration checks for specified post-migration check entities, reporting issues to specified report

    :type global_context: parallels.core.global_context.GlobalMigrationContext
    :type entities: list[parallels.core.utils.entity.Entity]
    :type report: parallels.core.reports.model.report.Report
    """
    util_name = get_executable_file_name()
    solutions = get_solutions(
        global_context.target_panel_obj.get_hosting_check_messages_panel_id(),
        util_name
    )
    checkers = create_checkers_for_context(global_context)

    for hosting_check_entity in entities:
        try:
            checker = checkers[type(hosting_check_entity)]
            issues = checker.check([hosting_check_entity])
            for issue in issues:
                report.add_issue(
                    issue.category, issue.severity, issue.problem,
                    solutions.get(issue.category, '')
                )
        except Exception as e:
            logger.debug("Exception: ", exc_info=True)
            report.add_issue(
                'generic-internal-error', Issue.SEVERITY_ERROR, 'Generic internal error: %s' % e
            )


@cached
def create_checkers_for_context(global_context):
    """Create checker objects for specified global context object; cache by global context so checkers are created once

    :type global_context: parallels.core.global_context.GlobalMigrationContext
    :rtype: dict[Entity, object]
    """
    checkers_config = MigratorHostingCheckersConfig(
        global_context.config,
        save_external_report_data_function=(
            lambda filename, data: _save_external_report_data(global_context, filename, data)
        )
    )
    return create_checkers(checkers_config)


def _save_external_report_data(global_context, filename, data):
    """
    :type global_context: parallels.core.global_context.GlobalMigrationContext
    :type filename: str | unicode
    :rtype: str | unicode | None
    """
    full_filename = global_context.migrator_server.get_session_file_path(filename)
    try:
        with open_no_inherit(full_filename, "w+") as f:
            f.write(safe_utf8_encode(data))
        return full_filename
    except IOError as e:
        logger.exception()
        logger.ferror(messages.FAILED_TO_SAVE_EXTERNAL_REPORT, filename=full_filename, reason=e)
        return None
