from xml.etree import ElementTree

from parallels.core.hosting_repository.extension import ExtensionModel, ExtensionEntity
from parallels.core.utils.mysql import escape_args_list
from parallels.plesk.hosting_repository.base import PleskBaseModel
from parallels.plesk.hosting_repository.utils.cli.backup_encrypt import BackupEncryptDecryptCli
from parallels.plesk.hosting_repository.utils.cli.backup_restore_helper import \
    BackupRestoreHelperGetExtensionHooksCli, BackupRestoreHelperBackupExtensionCli, \
    BackupRestoreHelperRestoreExtensionCli, BackupRestoreHelperPostBackupExtensionCli


class PleskExtensionModel(ExtensionModel, PleskBaseModel):
    def get_list(self, filter_name=None):
        """Retrieve list of Plesk extensions

        :type filter_name: list[str] | None
        :rtype: list[parallels.core.hosting_repository.extension.ExtensionEntity]
        """
        if filter_name is not None and len(filter_name) == 0:
            # filter by name is empty, so no one extension could be retrieved
            return []

        hidden_field = " AND `hidden` = 0" if int(self.plesk_server.query_panel_db("""
        SELECT count(*) as count
          FROM information_schema.COLUMNS
          WHERE TABLE_SCHEMA = 'psa'
          AND TABLE_NAME = 'Modules'
          AND COLUMN_NAME = 'hidden';
        """)[0]['count']) > 0 else ""

        query = """
            SELECT
                `id`, `name`, `version`, `release`
            FROM
                `Modules`
            WHERE 1
        """ + hidden_field
        query_args = {}
        if filter_name is not None:
            filter_name_placeholders, filter_name_values = escape_args_list(filter_name, 'name')
            query += ' AND name in ({filter_name_placeholders_str})'.format(
                filter_name_placeholders_str=', '.join(filter_name_placeholders)
            )
            query_args.update(filter_name_values)

        command = BackupRestoreHelperGetExtensionHooksCli(self.plesk_cli_runner)
        extensions_xml = command.run()
        extensions_node = ElementTree.fromstring(extensions_xml)

        extensions = []

        rows = self.plesk_server.query_panel_db(query, query_args)
        for row in rows:
            extension_id = row['id']
            extension = ExtensionEntity(extension_id, row['name'], row['version'], row['release'])
            hooks_node = extensions_node.find('extension[@id="%s"]/hooks' % extension_id)
            if hooks_node is not None:
                for hook_name in ExtensionEntity.hook_names:
                    is_available = hooks_node.attrib.get(hook_name) == 'true'
                    extension.set_hook(hook_name, is_available)
            extensions.append(extension)

        return extensions

    def make_backup(self, extension_id, backup_type, entity_id=None):
        """Backup given Plesk extension

        :type extension_id: int
        :type backup_type: str
        :type entity_id: int | None
        :rtype xml.etree.ElementTree.Element | None
        """
        # backup data of given extension
        command = BackupRestoreHelperBackupExtensionCli(self.plesk_cli_runner, extension_id, backup_type, entity_id)
        command.set_tracer(self.tracer)
        backup_xml = command.run()

        if backup_xml is None:
            return None

        # wrap encrypted extension backup to make it similar to regular Plesk backup
        backup_node = ElementTree.Element('migration-dump')
        extensions_node = ElementTree.Element('extensions')
        extensions_node.append(ElementTree.fromstring(backup_xml))
        backup_node.append(extensions_node)
        # upload wrapped backup into temporary file
        backup_file_path = self.plesk_server.get_session_file_path('backup_extension_%s' % extension_id)
        with self.plesk_server.runner() as runner:
            runner.upload_file_content(backup_file_path, ElementTree.tostring(backup_node))
        # perform decryption of wrapped backup
        command = BackupEncryptDecryptCli(self.plesk_cli_runner, backup_file_path)
        command.set_tracer(self.tracer)
        command.run()
        # retrieve decrypted backup content
        with self.plesk_server.runner() as runner:
            backup_xml = runner.get_file_contents(backup_file_path)

        # extract extension backup from wrapped backup
        backup_node = ElementTree.fromstring(backup_xml)
        extension_backup_node = backup_node.find('extensions/extension')

        return extension_backup_node

    def clear_backup_temp_data(self, extension_id, backup_type, entity_id=None):
        """Clean up temporary data created during backup step

        :type extension_id: int
        :type backup_type: str
        :type entity_id: int
        """
        command = BackupRestoreHelperPostBackupExtensionCli(
            self.plesk_cli_runner, extension_id, backup_type, entity_id
        )
        command.set_tracer(self.tracer)
        command.run()

    def restore_backup(self, extension_backup_node, backup_type, entity_name=None, temp_content_path=None):
        """Restore given server level extension backup

        :type extension_backup_node: xml.etree.ElementTree.Element
        :type backup_type: str
        :type entity_name: str | None
        :type temp_content_path: str | None
        """
        # prepare restore node which control restoration of extension backup
        restore_node = ElementTree.Element('restore')
        restore_node.set('object-type', backup_type)
        if entity_name is not None:
            restore_node.set('object-name', entity_name)
        restore_node.set('plesk-version', self.plesk_server.plesk_version_str)
        if temp_content_path is not None:
            restore_node.set('content', temp_content_path)
        # append restore node into extension backup
        extension_backup_node.append(restore_node)

        # run restoration command
        command = BackupRestoreHelperRestoreExtensionCli(
            # Extension restoration must be started via CLIGate.
            # When using direct CLI call files of extension get incorrect owner ('root' instead of 'psaadm').
            self._plesk_server.cligate_runner(),
            ElementTree.tostring(extension_backup_node)
        )
        command.set_tracer(self.tracer)
        command.run()
