from analogai.foundation.common.logging.app_logger import app_logger
from analogai.foundation.common.enums.certainty import Certainty
from analogai.deepthink.presentation.interpreters.interpreter import Interpreter
from analogai.deepthink.presentation.interpreters.interpreter_result import InterpreterResult
from analogai.deepthink.presentation.intent_type import IntentType
from analogai.deepthink.application.usecases.learning.make_conditional_statement.models import (
    CauseCriteria,
    MakeConditionalStatementRequest,
    CriterionData,
    CriterionValue,
)


class ConditionalStatementInterpreter(Interpreter):
    def interpret(self, parsed_message, user_agent, message_text):
        if parsed_message is None:
            return None
        if parsed_message.intent_type != IntentType.MAKING_CAUSAL_STATEMENT:
            return None
        
        data = parsed_message.data
        
        app_logger.debug(f"Causal interpreter - data.causes={data.causes}, data.effects={data.effects}")
        
        components = self._resolve_causal_components(data)
        if not components:
            return None

        cause_criteria_list, effect_criteria = components

        if not cause_criteria_list or not effect_criteria:
            return None
        
        probability = data.probability if data.probability is not None else 1.0
        
        request = MakeConditionalStatementRequest(
            user_agent=user_agent,
            probability=probability,
            causes=cause_criteria_list,
            effect=CauseCriteria(criteria=effect_criteria),
        )
        
        first_cause = cause_criteria_list[0].criteria
        app_logger.info(
            "Conditional statement interpreted: causes=%s, effect=%s %s %s in %s",
            len(cause_criteria_list),
            effect_criteria.subject_caption,
            effect_criteria.relation,
            effect_criteria.complement_caption,
            effect_criteria.location or ".",
        )
        
        return InterpreterResult(intent_type=IntentType.MAKING_CAUSAL_STATEMENT, request=request)

    def _resolve_causal_components(self, data):
        rules = (
            (self._has_causes_and_effects, self._from_causes_and_effects),
            (self._has_effects_only, self._from_effects_only),
            (self._always_true, self._from_data_only),
        )

        for predicate, builder in rules:
            if predicate(data):
                return builder(data)
        return None

    def _has_causes_and_effects(self, data) -> bool:
        return bool(data.causes and data.effects)

    def _has_effects_only(self, data) -> bool:
        return bool(data.effects)

    def _always_true(self, data) -> bool:
        return True

    def _from_causes_and_effects(self, data):
        effect_data = data.effects[0] if data.effects else None
        if not effect_data:
            return None
        cause_criteria_list = self._build_cause_criteria_list(data.causes)
        effect_criteria = self._build_criteria(effect_data)
        return cause_criteria_list, effect_criteria

    def _from_effects_only(self, data):
        effect_data = data.effects[0]
        cause_criteria_list = self._build_single_cause_list(data)
        effect_criteria = self._build_criteria(effect_data)
        return cause_criteria_list, effect_criteria

    def _from_data_only(self, data):
        cause_criteria_list = self._build_single_cause_list(data)
        effect_criteria = self._build_criteria(data, use_object_as_subject=True)
        return cause_criteria_list, effect_criteria

    def _build_cause_criteria_list(self, causes) -> list[CauseCriteria]:
        cause_criteria_list: list[CauseCriteria] = []
        for cause_data in causes or []:
            cause_criteria = self._build_criteria(cause_data)
            if cause_criteria:
                cause_criteria_list.append(CauseCriteria(criteria=cause_criteria))
        return cause_criteria_list

    def _build_single_cause_list(self, data) -> list[CauseCriteria]:
        cause_criteria = self._build_criteria(data)
        return [CauseCriteria(criteria=cause_criteria)] if cause_criteria else []
    
    def _extract_caption(self, notion_expression):
        if notion_expression is None:
            return None
        try:
            return notion_expression.caption
        except AttributeError:
            return str(notion_expression).strip()

    def _build_criteria(self, data, use_object_as_subject: bool = False) -> CriterionData | None:
        if data is None:
            return None

        subject_value = data.complement if use_object_as_subject else data.subject
        subject_caption = self._extract_caption(subject_value) or ""
        complement_caption = "" if use_object_as_subject else (self._extract_caption(data.complement) or "")

        relation = self._normalize_relation(data)

        location = data.location or ""
        time = data.time or ""
        from_time = data.from_time or ""
        to_time = data.to_time or ""
        quantity = data.quantity or ""
        deontic_modality = data.deontic_modality or ""
        certainty = data.certainty
        if certainty and not isinstance(certainty, Certainty):
            try:
                certainty = Certainty(str(certainty).lower())
            except ValueError:
                certainty = None
        attributes = data.attributes or []

        attribute_values = self._normalize_attributes(attributes)
        if not complement_caption and attribute_values:
            complement_caption = attribute_values[0].get("tag") or ""

        if not subject_caption:
            return None

        return CriterionData(
            subject_caption=subject_caption,
            complement_caption=complement_caption or subject_caption,
            relation=relation,
            location=location or None,
            time=time or None,
            from_time=from_time or None,
            to_time=to_time or None,
            quantity=quantity or None,
            deontic_modality=deontic_modality or None,
            certainty=certainty,
            attribute_values=attribute_values,
        )

    def _normalize_relation(self, data) -> str:
        relation = getattr(data, "relation", None) or "is"
        if not relation:
            relation = "is"

        if isinstance(relation, str):
            return relation

        for attr_name in ("value", "relation", "name"):
            if hasattr(relation, attr_name):
                attr_value = getattr(relation, attr_name)
                if isinstance(attr_value, str) and attr_value:
                    return attr_value
                if hasattr(attr_value, "value") and isinstance(attr_value.value, str):
                    return attr_value.value

        relationship_phrase = getattr(data, "relationship_phrase", None)
        if isinstance(relationship_phrase, str) and relationship_phrase:
            return relationship_phrase

        return str(relation)

    def _normalize_attributes(self, attributes) -> list[CriterionValue]:
        if not attributes:
            return []
        normalized: list[CriterionValue] = []
        for attr in attributes:
            tag = attr.tag
            values = attr.values or []
            value = values[0] if values else None
            unit = attr.unit
            min_value = attr.min_value
            max_value = attr.max_value
            if tag:
                normalized.append(
                    CriterionValue(
                        tag=tag,
                        value=value,
                        unit=unit,
                        min_value=min_value,
                        max_value=max_value,
                    )
                )
        return normalized
