from __future__ import annotations

import json
from typing import Iterable, Protocol, Union


class ModifierSerializer(Protocol):
    def supports(self, modifier) -> bool:
        raise NotImplementedError

    def serialize(self, modifier) -> str | None:
        raise NotImplementedError


class _ModelDumpSerializer:
    def supports(self, modifier) -> bool:
        return callable(getattr(modifier, "model_dump", None))

    def serialize(self, modifier) -> str | None:
        modifier_data = modifier.model_dump()
        return _serialize_modifier_data(modifier_data)


class _DictSerializer:
    def supports(self, modifier) -> bool:
        return callable(getattr(modifier, "dict", None))

    def serialize(self, modifier) -> str | None:
        modifier_data = modifier.dict()
        return _serialize_modifier_data(modifier_data)


class _AttrsSerializer:
    def supports(self, modifier) -> bool:
        return hasattr(modifier, "__dict__")

    def serialize(self, modifier) -> str | None:
        modifier_data = modifier.__dict__
        return _serialize_modifier_data(modifier_data)


class _FallbackSerializer:
    def supports(self, modifier) -> bool:
        return True

    def serialize(self, modifier) -> str | None:
        return str(modifier)


def _serialize_modifier_data(modifier_data) -> str | None:
    if isinstance(modifier_data, dict):
        return json.dumps(modifier_data, sort_keys=True, default=str)
    if modifier_data is None:
        return None
    return str(modifier_data)


_DEFAULT_MODIFIER_SERIALIZERS: list[ModifierSerializer] = [
    _ModelDumpSerializer(),
    _DictSerializer(),
    _AttrsSerializer(),
    _FallbackSerializer(),
]


from analogai.deepthink.presentation.presenters.utils.plural_utils import get_plural_relationship
from analogai.deepthink.presentation.presenters.utils.list_format_utils import (
    format_items_and,
    format_items_and_capitalized,
)


def _normalize_relation_value(relation: Union[str, "Relation", "RelationshipType"]) -> "Relation":
    from analogai.foundation.common.enums import RelationshipType
    from analogai.foundation.core.value_objects.relation import Relation

    if isinstance(relation, Relation):
        return relation
    if isinstance(relation, RelationshipType):
        return Relation.from_type(relation)
    return Relation.from_string(str(relation))


class NotionExpressionBuilder:
    def build(self, notion_or_caption) -> "NotionExpression":
        from analogai.foundation.common.dtos.notion_expression import NotionExpression

        if isinstance(notion_or_caption, NotionExpression):
            return notion_or_caption
        return NotionExpression(notion_or_caption)


def format_single_belief_statement(
    subject_notion_caption: Union[str, "NotionExpression"],
    complement_notion_caption: Union[str, "NotionExpression"],
    relation: Union[str, "Relation", "RelationshipType"],
    probability: float = 1.0,
    modifier=None,
    timezone=None,
) -> str:
    from analogai.foundation.common.dtos.belief_expression import BeliefExpression
    from analogai.foundation.common.dtos.notion_expression import NotionExpression
    from analogai.foundation.common.utils.expressions_serializer import serialize_belief_expression

    normalized_relation = _normalize_relation_value(relation)
    expression_builder = NotionExpressionBuilder()
    subject_expr = expression_builder.build(subject_notion_caption)
    complement_expr = expression_builder.build(complement_notion_caption)
    belief_expression = BeliefExpression(
        subject_notion_expression=subject_expr,
        complement_notion_expression=complement_expr,
        relation=normalized_relation,
        probability=probability,
        validity=1.0,
        modifier=modifier,
    )
    return serialize_belief_expression(belief_expression, timezone=timezone)


def serialize_modifier_signature(
    modifier,
    serializers: Iterable[ModifierSerializer] | None = None,
) -> str | None:
    if modifier is None:
        return None
    for serializer in serializers or _DEFAULT_MODIFIER_SERIALIZERS:
        if not serializer.supports(modifier):
            continue
        serialized_value = serializer.serialize(modifier)
        if serialized_value:
            return serialized_value
    return None


def format_grouped_statement(
    common_notion: str,
    relation: str,
    other_notions: list[str],
    is_subject_grouped: bool,
    modifier=None,
    timezone=None,
) -> str:
    from analogai.foundation.common.utils.statement_serializers import serialize_statement

    if not other_notions:
        unique_notions = []
    else:
        seen = set()
        unique = []
        for item in other_notions:
            if item in seen:
                continue
            seen.add(item)
            unique.append(item)
        unique_notions = unique

    if len(unique_notions) == 1:
        if is_subject_grouped:
            return serialize_statement(
                subject_notion_caption=common_notion,
                complement_notion_caption=unique_notions[0],
                relation=relation,
                probability=1.0,
                modifier=modifier,
                timezone=timezone,
            )
        return serialize_statement(
            subject_notion_caption=unique_notions[0],
            complement_notion_caption=common_notion,
            relation=relation,
            probability=1.0,
            modifier=modifier,
            timezone=timezone,
        )

    combined = format_items_and(unique_notions)
    plural_relationship = get_plural_relationship(relation)

    if is_subject_grouped:
        return f"{common_notion.capitalize()} {plural_relationship} {combined}"
    sources_combined = format_items_and_capitalized(unique_notions)
    return f"{sources_combined} {plural_relationship} {common_notion.capitalize()}"
