from typing import List, Optional, Union

from analogai.foundation.modules.notion.notion import Notion
from analogai.foundation.modules.direct_statement.direct_statement import DirectStatement
from analogai.foundation.modules.direct_statement.direct_statement_repository import StatementRepository
from analogai.foundation.core.observables.statement_observable_service import StatementObservableService
from analogai.foundation.core.value_objects.modifier import Modifier
from analogai.foundation.core.value_objects.relation import Relation
from analogai.foundation.modules.direct_statement.statement_service_base import StatementServiceBase


class StatementService(StatementServiceBase, StatementObservableService):
    def __init__(self, statement_repository: StatementRepository):
        super().__init__()
        self._statement_repository = statement_repository

    def create(
        self,
        user_id: str,
        agent_id: str,
        subject_notion: Notion,
        relation: Relation,
        complement_notion: Notion,
        probability: Union[float, int] = 1.0,
        validity: Union[float, int] = 1.0,
        modifier: Optional[Modifier] = None,
        quantity: Optional[float] = None,
        id: Optional[str] = None,
    ) -> DirectStatement:
        if not isinstance(relation, Relation):
            raise TypeError("relation must be a Relation instance")
        relation_obj = relation
        if modifier is None and quantity is not None:
            modifier = Modifier(quantity=quantity)
        elif modifier is not None and quantity is not None:
            modifier.quantity = quantity

        existing = self._statement_repository.find_direct_statement(
            user_id, agent_id, subject_notion, relation_obj, complement_notion, modifier
        )
        if existing:
            self.notify_statement_created(existing)
            return existing
        statement = DirectStatement(
            user_id=user_id,
            agent_id=agent_id,
            subject_notion=subject_notion,
            relation=relation_obj,
            complement_notion=complement_notion,
            probability=probability,
            validity=validity,
            modifier=modifier,
            id=id,
        )
        created = self._statement_repository.create(statement)
        self.notify_statement_created(created)
        return created

    def update(self, statement: DirectStatement) -> DirectStatement:
        updated = self._statement_repository.update(statement)
        self.notify_statement_updated(updated)
        return updated

    def delete(self, statement: DirectStatement) -> None:
        self._statement_repository.delete(statement)

        def find_by_captions(
            self,
            agent_id: str,
            captions: List[str],
            max_depth: int = 1,
        ) -> List[DirectStatement]:
            repository = self._statement_repository
            finder = getattr(repository, "find_by_captions", None)
            if not callable(finder):
                return []
            return finder(agent_id, captions, max_depth=max_depth)

    def find_by_id(self, statement_id: str) -> Optional[DirectStatement]:
        return self._statement_repository.find_by_id(statement_id)

    def find(
        self,
        subject_notion: Notion,
        complement_notion: Notion,
        relation: Relation,
        modifier: Optional[Modifier] = None,
    ) -> Optional[DirectStatement]:
        return self._statement_repository.find(
            subject_notion, complement_notion, relation, modifier
        )

    def find_by_agent_id(
        self,
        agent_id: str,
        limit: int = 0,
        offset: int = 0,
    ) -> List[DirectStatement]:
        return self._statement_repository.find_by_agent_id(
            agent_id,
            limit=limit,
            offset=offset,
        )

    def find_by_subject(self, subject_notion: Notion) -> List[DirectStatement]:
        return self._statement_repository.find_by_subject(subject_notion)

    def find_by_subject_and_relation(
        self,
        subject_notion: Notion,
        relation: Relation,
        modifier: Optional[Modifier] = None,
    ) -> List[DirectStatement]:
        return self._statement_repository.find_by_subject_and_relation(
            subject_notion, relation, modifier
        )

    def find_by_object(self, complement_notion: Notion) -> List[DirectStatement]:
        return self._statement_repository.find_by_object(complement_notion)

    def find_by_object_and_relation(
        self,
        complement_notion: Notion,
        relation: Relation,
        modifier: Optional[Modifier] = None,
    ) -> List[DirectStatement]:
        return self._statement_repository.find_by_object_and_relation(
            complement_notion, relation, modifier
        )

    def search_by_subject_notion_and_relation(
        self,
        subject_notion: Notion,
        relation: Relation,
        modifier: Optional[Modifier] = None,
    ) -> List[DirectStatement]:
        return self.find_by_subject_and_relation(
            subject_notion, relation, modifier
        )

    def search_by_complement_notion_and_relation(
        self,
        complement_notion: Notion,
        relation: Relation,
        modifier: Optional[Modifier] = None,
    ) -> List[DirectStatement]:
        return self.find_by_object_and_relation(
            complement_notion, relation, modifier
        )

    # def has_statement(
    #     self, subject_notion: Notion, relation: Relation, complement_notion: Notion
    # ) -> bool:
    #     statements = self.find_by_subject_and_relation(
    #         subject_notion, relation
    #     )
    #     for statement in statements:
    #         if statement.complement_notion.id == complement_notion.id:
    #             return True
    #     return False

    def update_probability(self, statement: DirectStatement, probability: Union[float, int]) -> DirectStatement:
        statement.probability = probability
        return self.update(statement)

    def update_validity(self, statement: DirectStatement, validity: Union[float, int]) -> DirectStatement:
        statement.validity = validity
        return self.update(statement)