from analogai.foundation.common.enums.criterion import Criterion
from analogai.foundation.core.value_objects.relation import Relation
from analogai.foundation.common.dtos.hypothetical_statement_expression import HypotheticalStatementExpression
from analogai.foundation.common.utils.expressions_serializer import (
    serialize_hypothetical_statement_expression,
)
from analogai.foundation.common.logging.app_logger import app_logger
from analogai.deepthink.application.usecases.learning.make_hypothetical_statement.models import (
    MakeHypotheticalStatementResponse,
    CriterionData,
)
from analogai.deepthink.application.usecases.learning.make_hypothetical_statement.ports import (
    MakeHypotheticalStatementOutputPort,
)
from analogai.deepthink.presentation.presenters.presenter_response import PresenterResponse


class CriterionFieldStrategy:
    def applies(self, criteria: CriterionData) -> bool:
        raise NotImplementedError

    def apply(self, criteria: CriterionData) -> tuple[Criterion, object]:
        raise NotImplementedError


class _ValueFieldStrategy(CriterionFieldStrategy):
    def __init__(self, criterion: Criterion, attr_name: str) -> None:
        self._criterion = criterion
        self._attr_name = attr_name

    def applies(self, criteria: CriterionData) -> bool:
        return getattr(criteria, self._attr_name) is not None

    def apply(self, criteria: CriterionData) -> tuple[Criterion, object]:
        return self._criterion, getattr(criteria, self._attr_name)


class _RelationFieldStrategy(CriterionFieldStrategy):
    def applies(self, criteria: CriterionData) -> bool:
        return criteria.relation is not None

    def apply(self, criteria: CriterionData) -> tuple[Criterion, object]:
        relation_value = criteria.relation
        if isinstance(relation_value, str):
            relation_value = Relation.from_string(relation_value)
        return Criterion.Relation, relation_value


class _CertaintyFieldStrategy(CriterionFieldStrategy):
    def applies(self, criteria: CriterionData) -> bool:
        return criteria.certainty is not None

    def apply(self, criteria: CriterionData) -> tuple[Criterion, object]:
        return Criterion.Certainty, criteria.certainty.value


class _AttributeValuesFieldStrategy(CriterionFieldStrategy):
    def applies(self, criteria: CriterionData) -> bool:
        return bool(criteria.attribute_values)

    def apply(self, criteria: CriterionData) -> tuple[Criterion, object]:
        attribute_payload = [
            {
                "tag": value.tag,
                "value": value.value,
                "unit": value.unit,
                "min_value": value.min_value,
                "max_value": value.max_value,
            }
            for value in criteria.attribute_values
        ]
        return Criterion.AttributeValue, attribute_payload


_CRITERION_FIELD_STRATEGIES: list[CriterionFieldStrategy] = [
    _ValueFieldStrategy(Criterion.ComplementCaption, "complement_caption"),
    _ValueFieldStrategy(Criterion.SubjectCaption, "subject_caption"),
    _RelationFieldStrategy(),
    _ValueFieldStrategy(Criterion.Location, "location"),
    _ValueFieldStrategy(Criterion.Time, "time"),
    _ValueFieldStrategy(Criterion.FromTime, "from_time"),
    _ValueFieldStrategy(Criterion.ToTime, "to_time"),
    _ValueFieldStrategy(Criterion.Quantity, "quantity"),
    _ValueFieldStrategy(Criterion.DeonticModality, "deontic_modality"),
    _CertaintyFieldStrategy(),
    _AttributeValuesFieldStrategy(),
]

_CRITERION_BY_VALUE = {c.value: c for c in Criterion}


class MakeHypotheticalStatementPresenter(MakeHypotheticalStatementOutputPort):
    def output_create_statement_success(self, response: MakeHypotheticalStatementResponse):
        cause_dicts = [self._to_criterion_dict(cause.criteria) for cause in response.causes]
        effect_dict = self._to_criterion_dict(response.effect.criteria)
        hypothetical = HypotheticalStatementExpression(
            causes=cause_dicts,
            effect=effect_dict,
            validity=response.validity,
        )
        timezone = response.user_agent.timezone if response.user_agent else None
        message = serialize_hypothetical_statement_expression(hypothetical, timezone=timezone)
        return PresenterResponse(message=message)

    def _to_criterion_dict(self, criteria: CriterionData) -> dict:
        result = {}
        for strategy in _CRITERION_FIELD_STRATEGIES:
            if not strategy.applies(criteria):
                continue
            criterion_key, value = strategy.apply(criteria)
            result[criterion_key] = value
        return result

    def output_forbidden(self, response: MakeHypotheticalStatementResponse):
        app_logger.info("Outputting forbidden hypothetical statement")
        return self.output_create_statement_success(response)
