from typing import Optional, Dict, Tuple

from analogai.deepthink.application.value_objects.user_agent import UserAgent
from analogai.deepthink.infrastructure.gateways.llm.gateway import LlmGateway
from analogai.deepthink.infrastructure.gateways.llm.enums.llm_provider import LlmProvider
from analogai.deepthink.infrastructure.gateways.llm.enums.llm_gateway_type import LlmGatewayType
# from analogai.deepthink.presentation.user_message import UserMessage
from analogai.deepthink.presentation.preprocessing.preprocessor import preprocess_text
from analogai.deepthink.presentation.preprocessing.previous_messages_stack import PreviousMessagesStack


class Deepthinker:
    _DEFAULT_USER_ID = "default-user"
    _DEFAULT_AGENT_ID = "default-agent"

    def __init__(
        self,
        notion_enrichment_enabled: Optional[bool] = None,
        llm_gateway_instance: Optional[LlmGateway] = None,
        similarity_cutoff: Optional[float] = None,
        llm_provider: Optional[LlmProvider] = None,
        llm_model: Optional[str] = None,
    ):
        from analogai.deepthink.startup.bootstrap import create_pipeline
        from analogai.deepthink.config import (
            NOTION_ENRICHMENT_ENABLED,
            SIMILARITY_CUTOFF,
        )
        from analogai.deepthink.infrastructure.gateways.llm.llm_gateway_provider_factory import (
            LlmGatewayProviderFactory,
        )
        from analogai.deepthink.infrastructure.gateways.llm.llm_factory import LlmGatewayFactory
        from analogai.deepthink.startup.factories.domain_service_factory import DomainServiceFactory
        from analogai.deepthink.features.question_answering.factory import create_question_answering_service
        from analogai.deepthink.features.question_answering.question_router import QuestionRouter

        self._notion_enrichment_enabled = (
            NOTION_ENRICHMENT_ENABLED
            if notion_enrichment_enabled is None
            else notion_enrichment_enabled
        )
        self._similarity_cutoff = (
            SIMILARITY_CUTOFF if similarity_cutoff is None else similarity_cutoff
        )
        if not llm_model:
            raise ValueError("llm_model must be provided; defaults are disabled")
        if llm_provider is None:
            raise ValueError("llm_provider must be provided; defaults are disabled")

        self._llm_model = llm_model
        provider_map = {
            LlmProvider.ANTHROPIC: LlmGatewayType.ANTHROPIC,
            LlmProvider.OPENAI: LlmGatewayType.OPENAI,
            LlmProvider.AZURE: LlmGatewayType.AZURE,
            LlmProvider.AZURE_OPENAI: LlmGatewayType.AZURE_OPENAI,
            LlmProvider.XAI: LlmGatewayType.GROK,
            LlmProvider.BEDROCK: LlmGatewayType.BEDROCK,
        }
        self._llm_gateway_type = provider_map.get(llm_provider)
        if self._llm_gateway_type is None:
            raise ValueError(f"Unsupported llm_provider: {llm_provider}")
        self._llm_gateway_instance = llm_gateway_instance
        LlmGatewayProviderFactory.set_default_gateway_type(self._llm_gateway_type)
        LlmGatewayProviderFactory.set_default_model(self._llm_model)
        if self._llm_gateway_instance is not None:
            if hasattr(self._llm_gateway_instance, "set_model"):
                try:
                    self._llm_gateway_instance.set_model(self._llm_model)
                except Exception:
                    pass
            LlmGatewayFactory.set_default_gateway_instance(self._llm_gateway_instance)
        else:
            LlmGatewayFactory.set_default_gateway_type(self._llm_gateway_type)

        self._domain_service_factory = DomainServiceFactory()
        self._pipeline = create_pipeline(
            domain_service_factory=self._domain_service_factory,
            notion_enrichment_enabled=self._notion_enrichment_enabled,
            llm_gateway_instance=self._llm_gateway_instance,
        )
        self._question_answering_service = create_question_answering_service(
            self._domain_service_factory,
            llm_gateway_instance=self._llm_gateway_instance,
            similarity_cutoff=self._similarity_cutoff,
        )
        self._router = QuestionRouter(self._question_answering_service)
        self._internal_stacks: Dict[Tuple[str, str], PreviousMessagesStack] = {}

    def deepthink(
        self,
        text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> Optional[str]:
        return self.chat(
            text=text,
            user_agent=user_agent,
            previous_messages_stack=previous_messages_stack,
        )

    def chat(
        self,
        text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> Optional[str]:
        resolved_user_agent = self._resolve_user_agent(user_agent)
        stack = self._resolve_prev_message_stack(previous_messages_stack, resolved_user_agent)
        if self._is_question(text, stack):
            answer = self.search(
                text=text,
                user_agent=resolved_user_agent,
                previous_messages_stack=stack,
            )
            return answer if answer else "I don't know"
        return self.remember(
            text=text,
            user_agent=resolved_user_agent,
            previous_messages_stack=stack,
        )

    def _is_question(
        self,
        text: str,
        stack: PreviousMessagesStack,
    ) -> bool:
        if self._router.is_question(text):
            return True
        if "?" in text:
            return True
        return False

    def remember(
        self,
        text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> Optional[str]:
        if text:
            text = text.replace('"', "")
        resolved_user_agent = self._resolve_user_agent(user_agent)
        stack = self._resolve_prev_message_stack(previous_messages_stack, resolved_user_agent)
        stack.add_user(text)
        result = self._handle_statement(text, resolved_user_agent, stack)
        if result:
            stack.add_agent(result)
        return result

    def search(
        self,
        text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> Optional[str]:
        resolved_user_agent = self._resolve_user_agent(user_agent)
        stack = self._resolve_prev_message_stack(previous_messages_stack, resolved_user_agent)
        stack.add_user(text)
        answer = self._question_answering_service.search(
            question_text=text,
            user_agent=resolved_user_agent,
        )
        if answer:
            stack.add_agent(answer)
        return answer

    def answer(
        self,
        text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> Optional[str]:
        return self.search(
            text=text,
            user_agent=user_agent,
            previous_messages_stack=previous_messages_stack,
        )

    def generate_context(
        self,
        text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> str:
        resolved_user_agent = self._resolve_user_agent(user_agent)
        return self._question_answering_service.generate_context(
            question_text=text,
            user_agent=resolved_user_agent,
        )

    def get_context(
        self,
        text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> str:
        return self.generate_context(
            text=text,
            user_agent=user_agent,
            previous_messages_stack=previous_messages_stack,
        )

    def enrich_notions(self, notion: object) -> None:
        if not self._notion_enrichment_enabled:
            return
        from analogai.deepthink.features.notion_enrichment.registry import get_notion_enrichment_observer

        observer = get_notion_enrichment_observer()
        if observer is None:
            return
        observer.on_notion_created(notion)

    def _resolve_prev_message_stack(
        self,
        stack: Optional[PreviousMessagesStack],
        user_agent: UserAgent,
    ) -> PreviousMessagesStack:
        if stack is not None:
            return stack
        key = (user_agent.user_id, user_agent.agent_id)
        if key not in self._internal_stacks:
            self._internal_stacks[key] = PreviousMessagesStack()
        return self._internal_stacks[key]

    def _resolve_user_agent(
        self,
        user_agent: Optional[UserAgent],
    ) -> UserAgent:
        if user_agent is not None:
            return user_agent
        return UserAgent(
            user_id=self._DEFAULT_USER_ID,
            agent_id=self._DEFAULT_AGENT_ID,
            user_authority=1.0,
        )

    def _handle_statement(
        self,
        text: str,
        user_agent: UserAgent,
        stack: PreviousMessagesStack,
    ) -> Optional[str]:
        result = self._pipeline.execute(
            raw_message=text,
            user_agent=user_agent,
            previous_messages_stack=stack,
        )
        return result.message_text if result else None


DeepThinker = Deepthinker
Thinker = Deepthinker



