class SolrSchemaReflectionError(Exception):
    pass


class SolrSchemaLanguageReflector(object):
    """
    Solr schema reflection that helps work with dynamic language-specific fields.
    """

    FALLBACK_FIELD = "faba"
    FALLBACK_FIELDS = {f"summary_{FALLBACK_FIELD}", f"text_{FALLBACK_FIELD}"}

    def __init__(self):
        self.reflected = False
        # specific languages indexed aside from the universal fallback field
        self.languages = []  # sorted

    def reflect_solr_schema(self, schema: dict):
        if not isinstance(fields := schema.get("fields"), list):
            raise SolrSchemaReflectionError("Field definitions are not found")
        context = self._create_collect_context()
        for field in fields:
            name_parts = field["name"].split("_")
            if len(name_parts) > 1 and name_parts[0] in ("summary", "text") and name_parts[1].islower():
                self._collect_language_field(field, name_parts, context)
        self._finalize_reflect(context)
        # ready to use
        self.reflected = True

    def _create_collect_context(self):
        return {"lang_fields": {}}

    def _collect_language_field(self, field, name_parts, context):
        lang_fields = context["lang_fields"]
        if name_parts[1] not in lang_fields:
            lang_fields[name_parts[1]] = []
        lang_fields[name_parts[1]].append(field["name"])

    def _finalize_reflect(self, context):
        # check if fallback fields exist
        fallback_fields = set(context["lang_fields"].get(self.FALLBACK_FIELD, []))
        if not fallback_fields.issuperset(self.FALLBACK_FIELDS):
            raise SolrSchemaReflectionError("Fallback fields are missing")
        if any(f"{f}_ex" in fallback_fields for f in self.FALLBACK_FIELDS):
            raise SolrSchemaReflectionError("Fallback fields may not have exact variants")
        languages_in_schema = []
        for lang, field_names in context["lang_fields"].items():
            if all(
                field_name in field_names
                for field_name in (f"summary_{lang}", f"summary_{lang}_ex", f"text_{lang}", f"text_{lang}_ex")
            ):
                languages_in_schema.append(lang)
        if self.FALLBACK_FIELD in languages_in_schema:  # pragma: no cover
            raise SolrSchemaReflectionError("Fallback fields must not be considered a language")
        self.languages = sorted(languages_in_schema)


class SolrSchemaHighlightReflector(SolrSchemaLanguageReflector):
    """
    Reflect highlightable fields in addition to the dynamic languages.
    """

    def __init__(self):
        SolrSchemaLanguageReflector.__init__(self)
        self.highlight_field_by_language = dict()

    def _create_collect_context(self):
        d = SolrSchemaLanguageReflector._create_collect_context(self)
        d["highlight_compatibles"] = {}
        return d

    def _collect_language_field(self, field, name_parts, context):
        SolrSchemaLanguageReflector._collect_language_field(self, field, name_parts, context)
        if field.get("storeOffsetsWithPositions") and field.get("stored"):
            highlight_compatibles = context["highlight_compatibles"]
            if name_parts[1] in highlight_compatibles:
                raise SolrSchemaReflectionError("Multiple highlight fields for the same language is not necessary")
            highlight_compatibles[name_parts[1]] = field["name"]

    def _finalize_reflect(self, context):
        SolrSchemaLanguageReflector._finalize_reflect(self, context)
        highlight_compatibles = context["highlight_compatibles"]
        if self.FALLBACK_FIELD not in highlight_compatibles:
            raise SolrSchemaReflectionError("Fallback has no highlight field")
        highlight_field_by_language = {self.FALLBACK_FIELD: highlight_compatibles[self.FALLBACK_FIELD]}
        for lang in self.languages:
            if lang not in highlight_compatibles:
                raise SolrSchemaReflectionError("Language has no highlight field")
            highlight_field_by_language[lang] = highlight_compatibles[lang]
        self.highlight_field_by_language = highlight_field_by_language
