import os

from parallels.core.registry import Registry
from parallels.core.utils.common import is_string
from parallels.plesk import messages


class HostingDescriptionFilesValidator(object):
    """Check that files from hosting description file actually exist on source server"""

    def validate_model(self, model, content_server):
        """
        :type model: parallels.plesk.hosting_description.model.HostingDescriptionModel
        """
        errors = []
        errors += self._validate_mailbox_directories(model, content_server)
        errors += self._validate_database_dumps(model)
        errors += self._validate_web_root_dirs(model, content_server)
        errors += self._validate_web_files(model, content_server)
        return errors

    def _validate_mailbox_directories(self, model, server):
        errors = []
        for subscription in model.iter_all_subscriptions():
            if subscription.mail_service is not None and subscription.mail_service.mail_server is not None:
                # external mail server is specified in hosting description
                global_context = Registry.get_instance().get_context()
                mail_server = global_context.conn.get_external_mail_server(
                    subscription.mail_service.mail_server
                )
                if mail_server is None:
                    errors.append(messages.VALIDATION_MAIL_SERVER_NOT_SPECIFIED.format(
                        subscription=subscription.name, mail_server=subscription.mail_service.mail_server
                    ))
                    continue
            else:
                # mail server is default content server
                mail_server = server

            for mailbox in subscription.iter_mailboxes():
                if not is_string(mailbox.source_directory):
                    continue
                if server is None:
                    errors.append(messages.VALIDATION_FILES_MAILBOX_DIR_SERVER_NOT_SPECIFIED.format(
                        subscription=subscription.name, mailbox=mailbox.name
                    ))
                elif not self._file_exists(mail_server, mailbox.source_directory):
                    errors.append(messages.VALIDATION_FILES_MAILBOX_DIR_NOT_EXIST.format(
                        subscription=subscription.name, mailbox=mailbox.name, directory=mailbox.source_directory,
                        server=server.description()
                    ))

        return errors

    @staticmethod
    def _validate_database_dumps(model):
        errors = []
        for subscription in model.iter_all_subscriptions():
            for database in subscription.iter_databases():
                if not is_string(database.dump):
                    continue
                if not os.path.exists(database.dump):
                    errors.append(messages.VALIDATION_FILES_DB_DUMP_NOT_EXIST.format(
                        subscription=subscription.name, database=database.name, file=database.dump
                    ))

        return errors

    def _validate_web_root_dirs(self, model, server):
        errors = []
        for subscription in model.iter_all_subscriptions():
            subscription_src_webspace_root = self._get_path(subscription.source_webspace_root)
            if subscription_src_webspace_root is not None:
                if server is None:
                    errors.append(messages.VALIDATION_FILES_SUBSCRIPTION_WEBSPACE_ROOT_SERVER_NOT_SPECIFIED.format(
                        subscription=subscription.name
                    ))
                elif not self._file_exists(server, subscription_src_webspace_root):
                    errors.append(messages.VALIDATION_FILES_SUBSCRIPTION_WEBSPACE_ROOT_NOT_EXIST.format(
                        subscription=subscription.name, source_webspace_root=subscription_src_webspace_root,
                        server=server.description()
                    ))

            subscription_src_doc_root = self._get_path(subscription.source_document_root)
            if subscription_src_doc_root is not None:
                if server is None:
                    errors.append(messages.VALIDATION_FILES_SUBSCRIPTION_DOC_ROOT_SERVER_NOT_SPECIFIED.format(
                        subscription=subscription.name
                    ))
                elif not self._file_exists(server, subscription_src_doc_root):
                    errors.append(messages.VALIDATION_FILES_SUBSCRIPTION_DOC_ROOT_NOT_EXIST.format(
                        subscription=subscription.name, source_document_root=subscription_src_doc_root,
                        server=server.description()
                    ))

            for addon_domain in subscription.iter_addon_domains():
                errors += self._validate_addon_document_root(subscription, addon_domain, server)
            for subdomain in subscription.iter_subdomains():
                errors += self._validate_subdomain_document_root(subscription, subdomain, server)

        return errors

    def _validate_addon_document_root(self, subscription, addon_domain, server):
        errors = []

        addon_src_document_root = self._get_path(addon_domain.source_document_root)
        if addon_src_document_root is not None:
            if server is None:
                errors.append(messages.VALIDATION_FILES_ADDON_DOMAIN_DOC_ROOT_SERVER_NOT_SPECIFIED.format(
                    subscription=subscription.name, addon_domain=addon_domain.name
                ))
            elif not self._file_exists(server, addon_src_document_root):
                errors.append(messages.VALIDATION_FILES_ADDON_DOMAIN_DOC_ROOT_NOT_EXIST.format(
                    subscription=subscription.name, addon_domain=addon_domain.name,
                    source_document_root=addon_src_document_root, server=server.description()
                ))

        return errors

    def _validate_subdomain_document_root(self, subscription, subdomain, server):
        errors = []

        subdomain_src_document_root = self._get_path(subdomain.source_document_root)
        if subdomain_src_document_root is not None:
            if server is None:
                errors.append(messages.VALIDATION_SUBDOMAIN_DOC_ROOT_SERVER_NOT_SPECIFIED.format(
                    subscription=subscription.name, subdomain=subdomain.name
                ))
            elif not self._file_exists(server, subdomain_src_document_root):
                errors.append(messages.VALIDATION_SUBDOMAIN_DOC_ROOT_NOT_EXIST.format(
                    subscription=subscription.name, subdomain=subdomain.name,
                    source_document_root=subdomain_src_document_root, server=server.description()
                ))

        return errors

    def _validate_web_files(self, model, server):
        errors = []

        for subscription in model.iter_all_subscriptions():
            is_web_files_specified = False
            for web_file in subscription.iter_web_files():
                if not is_string(web_file.source):
                    continue
                is_web_files_specified = True
                if server is None:
                    continue
                if not web_file.skip_if_source_not_exists and not self._file_exists(server, web_file.source):
                    errors.append(messages.VALIDATION_FILES_WEB_FILE_FOR_SUBSCRIPTION_NOT_EXIST.format(
                        subscription=subscription.name, filename=web_file.source, server=server.description()
                    ))
            if is_web_files_specified and server is None:
                errors.append(messages.VALIDATION_FILES_WEB_FILE_FOR_SUBSCRIPTION_SERVER_NOT_SPECIFIED.format(
                    subscription=subscription.name
                ))

            for addon_domain in subscription.iter_addon_domains():
                is_web_files_specified = False
                for web_file in addon_domain.iter_web_files():
                    if not is_string(web_file.source):
                        continue
                    is_web_files_specified = True
                    if server is None:
                        continue
                    if not web_file.skip_if_source_not_exists and not self._file_exists(server, web_file.source):
                        errors.append(messages.VALIDATION_FILES_WEB_FILE_FOR_ADDON_DOMAIN_NOT_EXIST.format(
                            subscription=subscription.name, addon_domain=addon_domain.name,
                            filename=web_file.source, server=server.description()
                        ))
                if is_web_files_specified and server is None:
                    errors.append(messages.VALIDATION_FILES_WEB_FILE_FOR_ADDON_DOMAIN_SERVER_NOT_SPECIFIED.format(
                        subscription=subscription.name, addon_domain=addon_domain.name
                    ))

            for subdomain in subscription.iter_subdomains():
                is_web_files_specified = False
                for web_file in subdomain.iter_web_files():
                    if not is_string(web_file.source):
                        continue
                    is_web_files_specified = True
                    if server is None:
                        continue
                    if not web_file.skip_if_source_not_exists and not self._file_exists(server, web_file.source):
                        errors.append(messages.VALIDATION_FILES_WEB_FILE_FOR_SUBDOMAIN_NOT_EXIST.format(
                            subscription=subscription.name, subdomain=subdomain.name,
                            filename=web_file.source, server=server.description()
                        ))
                if is_web_files_specified and server is None:
                    errors.append(messages.VALIDATION_FILES_WEB_FILE_FOR_SUBDOMAIN_SERVER_NOT_SPECIFIED.format(
                        subscription=subscription.name, subdomain=subdomain.name
                    ))

        return errors

    @staticmethod
    def _get_path(path_struct):
        if is_string(path_struct):
            return path_struct
        elif isinstance(path_struct, dict):
            return path_struct.get('path')
        else:
            return None

    @staticmethod
    def _file_exists(server, filename):
        with server.runner() as runner:
            return runner.file_exists(filename)
