from __future__ import annotations

from abc import ABC, abstractmethod
from datetime import datetime, timezone
from typing import Optional

from analogai.deepthink.application.value_objects.user_agent import UserAgent
from analogai.deepthink.messaging.question_answering.question_answering_service import (
    QuestionAnsweringService,
)
from analogai.deepthink.presentation.pipeline.pipeline import MessagePipeline
from analogai.deepthink.presentation.preprocessing.previous_messages_stack import PreviousMessagesStack
from analogai.deepthink.messaging.question_answering.enums.synthesized_text_type import (
    SynthesizedTextType,
)
from analogai.deepthink.application.value_objects.search_result import SearchResult
from analogai.deepthink.messaging.fact.value_objects.source import FactSource, SourceType
from analogai.foundation.modules.fact.source.fact_source import FileSource, OtherSource, UrlSource
from analogai.foundation.modules.fact.source.services.fact_source_service import FactSourceService
from analogai.foundation.modules.fact.subscribers.fact_observer_registry import FactObserverRegistry


class MessageCommand(ABC):
    @abstractmethod
    def execute(
        self,
        text: str,
        user_agent: UserAgent,
        stack: PreviousMessagesStack,
        synthesized_text_type: Optional[SynthesizedTextType] = None,
        source: FactSource | str | None = None,
    ) -> Optional[str]:
        raise NotImplementedError


class RememberCommand(MessageCommand):
    def __init__(
        self,
        pipeline: MessagePipeline,
        observer_registry: Optional[FactObserverRegistry] = None,
        source_service: Optional[FactSourceService] = None,
    ):
        self._pipeline = pipeline
        self._observer_registry = observer_registry
        self._source_service = source_service

    def execute(
        self,
        text: str,
        user_agent: UserAgent,
        stack: PreviousMessagesStack,
        synthesized_text_type: Optional[SynthesizedTextType] = None,
        source: FactSource | str | None = None,
    ) -> Optional[str]:
        resolved_source = _resolve_source_for_registry(
            source,
            user_agent=user_agent,
            source_service=self._source_service,
        )
        stack.add_user(text)
        result = None
        if self._observer_registry is None:
            result = self._pipeline.execute(
                raw_message=text,
                user_agent=user_agent,
                previous_messages_stack=stack,
            )
        else:
            with self._observer_registry.use_current_sentence(
                text=text,
                role="user",
                user_name=user_agent.user_id,
                timestamp=datetime.now(timezone.utc),
                source=resolved_source,
            ):
                result = self._pipeline.execute(
                    raw_message=text,
                    user_agent=user_agent,
                    previous_messages_stack=stack,
                )
        if result is None:
            message_text = None
        else:
            try:
                message_text = result.message_text
            except AttributeError:
                message_text = None
        if message_text:
            stack.add_agent(message_text)
        return message_text


class SearchCommand(MessageCommand):
    def __init__(self, question_answering_service: QuestionAnsweringService):
        self._question_answering_service = question_answering_service

    def execute(
        self,
        text: str,
        user_agent: UserAgent,
        stack: PreviousMessagesStack,
        synthesized_text_type: Optional[SynthesizedTextType] = None,
        source: FactSource | str | None = None,
    ) -> Optional[SearchResult]:
        stack.add_user(text)
        answer = self._question_answering_service.answer_question(
            question_text=text,
            user_agent=user_agent,
            synthesized_text_type=synthesized_text_type,
            previous_messages_stack=stack,
        )
        if answer and answer.text:
            stack.add_agent(answer.text)
        return answer


class GenerateBackgroundInfoCommand:
    def __init__(self, question_answering_service: QuestionAnsweringService):
        self._question_answering_service = question_answering_service

    def execute(self, text: str, user_agent: UserAgent) -> str:
        return self._question_answering_service.build_question_background_info(
            question_text=text,
            user_agent=user_agent,
        )


__all__ = [
    "GenerateBackgroundInfoCommand",
    "MessageCommand",
    "RememberCommand",
    "SearchCommand",
]


def _resolve_source_for_registry(
    source: FactSource | str | None,
    *,
    user_agent: UserAgent,
    source_service: Optional[FactSourceService],
) -> FactSource | str:
    agent_id = user_agent.agent_id
    if source is None:
        resolved = OtherSource(user_agent.user_id, agent_id=agent_id)
    elif isinstance(source, FactSource):
        if getattr(source, "agent_id", None) == agent_id or not agent_id:
            resolved = source
        else:
            resolved = _clone_source_with_agent(source, agent_id)
    else:
        raw = str(source).strip()
        if raw.startswith("http://") or raw.startswith("https://"):
            resolved = UrlSource(raw, agent_id=agent_id)
        elif ":\\" in raw or "/" in raw or "\\" in raw:
            resolved = FileSource(raw, agent_id=agent_id)
        else:
            resolved = OtherSource(raw or user_agent.user_id, agent_id=agent_id)
    if source_service is not None:
        try:
            source_service.store_source(resolved)
        except Exception:
            pass
    return resolved


def _clone_source_with_agent(source: FactSource, agent_id: str | None) -> FactSource:
    if isinstance(source, UrlSource):
        return UrlSource(source.value, agent_id=agent_id)
    if isinstance(source, FileSource):
        return FileSource(source.value, agent_id=agent_id)
    if isinstance(source, OtherSource):
        return OtherSource(source.value, agent_id=agent_id)
    return FactSource(source.type, source.value, agent_id=agent_id)
