from typing import Optional, Any, Callable, cast

from analogai.foundation.common.enums import RelationshipType
from analogai.foundation.common.dtos.notion_expression import NotionExpression
from analogai.foundation.common.dtos.belief_expression import Relation
from analogai.foundation.core.value_objects.modifier import Modifier
from analogai.foundation.core.value_objects.relation import Relation as CoreRelation

from analogai.deepthink.application.usecases.learning.learn_statement.models import LearnStatementRequest, StatementType
from analogai.deepthink.application.usecases.learning.make_notion_statement.models import MakeNotionStatementRequest
from analogai.deepthink.application.usecases.learning.learn_statement.ports import LearnStatementInputPort
from analogai.deepthink.application.usecases.learning.make_direct_statement.models import MakeDirectStatementRequest


class MakeStatementUseCase(LearnStatementInputPort):
    def __init__(self, make_direct_statement: Any, make_notion_statement: Any | None = None):
        self._make_direct_statement = make_direct_statement
        self._make_notion_statement = make_notion_statement
        self._handlers = self._build_handler_registry()

    def _build_handler_registry(self) -> dict[StatementType, Callable[[LearnStatementRequest], Any]]:
        return {
            StatementType.SIMPLE: self._execute_simple,
            StatementType.ATTRIBUTE_QUANTITY: self._execute_attribute_as_attribute,
            StatementType.NOTION_ATTRIBUTE: self._execute_notion_statement,
        }

    def execute(self, request: LearnStatementRequest):
        handler = self._handlers.get(request.statement_type)
        if handler is None:
            return None
        return handler(request)

    def execute_simple(self, request: MakeDirectStatementRequest):
        return self._make_direct_statement.execute(request)

    def _execute_simple(self, request: LearnStatementRequest):
        if not request.relation:
            return None
        simple_req = MakeDirectStatementRequest(
            user_agent=request.user_agent,
            subject_notion_expression=request.subject,
            complement_notion_expression=cast(NotionExpression, request.complement),
            relation=cast(CoreRelation, request.relation),
            probability=request.probability if request.probability is not None else 1.0,
            modifier=request.modifier,
            quantity=request.quantity,
        )
        return self._make_direct_statement.execute(simple_req)

    def _execute_notion_statement(self, request: LearnStatementRequest):
        if self._make_notion_statement is None:
            return None
        if not request.subject:
            return None
        notion_req = MakeNotionStatementRequest(
            user_agent=request.user_agent,
            notion_expression=request.subject,
        )
        return self._make_notion_statement.execute(notion_req)

    def _execute_attribute_as_attribute(self, request: LearnStatementRequest):
        if not request.subject:
            return None
        
        if not request.attributes:
            return None
        
        attr = request.attributes[0]
        tag = attr.tag
        value = attr.values[0] if attr.values else 0.0
        unit = attr.unit or ""

        base_modifier = request.modifier or Modifier()
        modifier = Modifier(
            location=base_modifier.location,
            deontic_modality=base_modifier.deontic_modality,
            from_time=base_modifier.from_time,
            to_time=base_modifier.to_time,
            attributes=base_modifier.attributes,
            quantity=base_modifier.quantity,
        )
        simple_req = MakeDirectStatementRequest(
            user_agent=request.user_agent,
            subject_notion_expression=request.subject,
            complement_notion_expression=NotionExpression(caption=tag),
            relation=Relation.from_type(RelationshipType.HAS_A),
            probability=1.0,
            modifier=modifier,
        )
        return self._make_direct_statement.execute(simple_req)

