from parallels.core.utils.common import is_empty_iterator
from parallels.plesk.connections.target_connections import PleskTargetConnections
from parallels.plesk.hosting_repository.model import PleskHostingRepositoryModel
from parallels.plesk.source.plesk.server import PleskSourceServer


def is_extensions_supported(context):
    """Check if source and target Plesk servers support backup and restore of extensions

    :type context: parallels.plesk.source.plesk.global_context.PleskGlobalMigrationContext
    :rtype: bool
    """
    target_plesk_server = get_target_plesk_server(context)
    if target_plesk_server is None:
        return False
    if target_plesk_server.get_plesk_version() < (17, 0):
        return False
    if target_plesk_server.config.is_skip_extensions:
        return False
    return not is_empty_iterator(iter_source_plesk_servers(context))


def iter_extensions(context, filter_hook=None, filter_source_id=None, is_skip_missed=True):
    """Iterate extensions on source Plesk servers which data could be deployed on target Plesk

    :type context: parallels.plesk.source.plesk.global_context.PleskGlobalMigrationContext
    :type filter_hook: list[str]
    :type filter_source_id: list[str]
    :type is_skip_missed: bool
    :rtype: list[(
        parallels.core.hosting_repository.extension.ExtensionEntity,
        parallels.plesk.source.plesk.server.PleskSourceServer
    )]
    """
    if filter_hook is not None and len(filter_hook) == 0:
        # filter by hook is empty, so no one extension could be retrieved
        return
    if filter_source_id is not None and len(filter_source_id) == 0:
        # filter by source_id is empty, so no one extension could be retrieved
        return

    def is_hooks_available(_extension):
        if filter_hook is None:
            return True
        for extension_hook in filter_hook:
            if _extension.is_hook_available(extension_hook):
                return True
        # skip extensions which has no one hook listed in given filter
        return False

    target_extension_names = [e.name for e in context.hosting_repository.extension.get_list() if is_hooks_available(e)]
    if is_skip_missed and len(target_extension_names) == 0:
        # target Plesk server has no installed extensions, so no need to proceed
        return
    transferred_extension_names = []
    for source_plesk_server in iter_source_plesk_servers(context):
        if filter_source_id is not None and source_plesk_server.source_id not in filter_source_id:
            # skip sources which id not listed in given filter
            continue
        source_hosting_repository = PleskHostingRepositoryModel(source_plesk_server)
        for extension in source_hosting_repository.extension.get_list():
            if not is_hooks_available(extension):
                continue
            if is_skip_missed and extension.name not in target_extension_names:
                # skip extensions which not installed on target Plesk
                continue
            if extension.name in transferred_extension_names:
                # skip extensions which server level data already was transferred from another source Plesk
                continue
            if extension.name == 'panel-migrator':
                # no need to transfer own server level data
                continue
            yield extension, source_plesk_server
            transferred_extension_names.append(extension.name)


def get_target_plesk_server(context):
    """Retrieve target Plesk server

    :type context: parallels.plesk.source.plesk.global_context.PleskGlobalMigrationContext
        | parallels.plesk.source.plesk.global_context.GlobalMigrationContext
    :rtype: parallels.plesk.connections.target_server.PleskTargetServer | None
    """
    if not isinstance(context.conn.target, PleskTargetConnections):
        return None
    return context.conn.target.plesk_server


def iter_source_plesk_servers(context):
    """Iterate all source Plesk servers which could have any extensions to transfer

    :type context: parallels.plesk.source.plesk.global_context.PleskGlobalMigrationContext
    :rtype: list[parallels.plesk.source.plesk.server.PleskSourceServer]
    """
    for source_server_info in context.get_source_servers_info():
        if not isinstance(source_server_info.source, PleskSourceServer):
            continue
        if source_server_info.source.get_plesk_version() < (17, 0):
            continue
        if source_server_info.source.config.is_skip_extensions:
            continue
        yield source_server_info.source
