import json
from collections import defaultdict

from ocyan.core.utils import identity
from ocyan.main.loading import get_ocyan_class, get_ocyan_classes

from .utils import REGISTERED_BLOCK_TYPES

# pylint: disable=unbalanced-tuple-unpacking
(
    convert_html,
    convert_carousel,
    convert_iconslider,
    convert_product,
    convert_googlemaps,
    convert_vat,
    convert_embed,
) = get_ocyan_classes(
    "roadrunner_bs5.convert.blocks",
    [
        "convert_html",
        "convert_carousel",
        "convert_iconslider",
        "convert_product",
        "convert_googlemaps",
        "convert_vat",
        "convert_embed",
    ],
)

convert_snippet = get_ocyan_class("roadrunner_bs5.convert.snippet", "convert_snippet")


class BlockMap:
    default_blockmap = {
        "html": convert_html,
        "carousel": convert_carousel,
        "IconSlider": convert_iconslider,
        "HighlightBlock": identity,
        "product": convert_product,
        "googlemaps": convert_googlemaps,
        "vatentry": convert_vat,
        "embed": convert_embed,
    }

    def __init__(self):
        self.block_map = defaultdict(lambda: convert_snippet, self.default_blockmap)

    def register(self, typ, mapper):
        self.block_map[typ] = mapper

    def has_replacement(self, typ):
        return typ in self.block_map

    def replacement(self, typ, blockdef):
        try:
            return self.block_map[typ](blockdef)
        except KeyError as e:
            raise ValueError(
                "Unknown block %s, did you register your custom blocks with rr?\n"
                "See https://git.mandelblog.com/mandel-plugins/ocyan.plugin.roadrunner_bs5/src/branch/master/README.rst\n"
                "Available blocks: %s\n"
                "Blockdef: %s"
                % (
                    typ,
                    json.dumps(list(REGISTERED_BLOCK_TYPES)),
                    json.dumps(blockdef, indent=2),
                )
            ) from e


blockmap = BlockMap()


def convert_blocks(obj):
    if isinstance(obj, list):
        return [convert_blocks(o) for o in obj]
    elif isinstance(obj, dict):
        if "type" in obj:
            if obj["type"] not in REGISTERED_BLOCK_TYPES or blockmap.has_replacement(
                obj["type"]
            ):
                return blockmap.replacement(obj["type"], obj)

        return {key: convert_blocks(value) for key, value in obj.items()}

    return obj
