from typing import Optional

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.presentation.preprocessing.previous_messages_stack import PreviousMessagesStack
from analogai.deepthink.messaging.factories.pipeline_factory import MessagePipelineFactory
from analogai.deepthink.messaging.question_answering.factories.question_answering_service_factory import (
    QuestionAnsweringServiceFactory,
)
from analogai.deepthink.messaging.question_answering.question_routing_service import (
    QuestionRoutingService,
)
from analogai.deepthink.messaging.factories.command_factory import ChatCommandFactory
from analogai.deepthink.messaging.factories.chat_flow_factory import ChatFlowBuilder
from analogai.deepthink.messaging.factories.user_agent_resolver_factory import UserAgentResolverFactory
from analogai.deepthink.internal.processors.deepthinker_processor import DeepthinkPipelineProcessor
from analogai.deepthink.internal.processors.large_text_processor import SentenceBatcher
from analogai.deepthink.internal.processors.chat_synthesizer import ChatResponseSynthesizer
from analogai.deepthink.messaging.fact.services.fact_context_service import FactContextWindowService
from analogai.deepthink.messaging.fact.factories.fact_factory import FactServiceFactory
from analogai.deepthink.messaging.fact.factories.fact_domain_service_factory import FactDomainServiceFactory
from analogai.deepthink.messaging.fact.services.fact_context_assembler import FactContextBuilder
from analogai.deepthink.messaging.fact.services.fact_turn_recorder import FactTurnTracker
from analogai.deepthink.messaging.question_answering.enums.synthesized_text_type import (
    SynthesizedTextType,
)
from analogai.deepthink.messaging.fact.repositories.infrastructure.fact_repository import (
    InMemoryFactRepository,
    MongoFactRepository,
)


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

    def __init__(
        self,
        llm_gateway_instance: Optional[LlmGateway] = None,
        llm_provider: Optional[LlmProvider] = None,
        llm_model: Optional[str] = None,
    ):
        from analogai.deepthink.config import (
            MAX_SENTENCES_PER_INPUT,
            ENVIRONMENT,
        )
        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.startup.extensions_service_factory import ExtensionsServiceFactory
        from analogai.foundation.infrastructure.services.embedding_service import EmbeddingService

        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
        self._llm_gateway_type = llm_provider.gateway_type
        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:
            self._llm_gateway_instance.set_model(self._llm_model)
            LlmGatewayFactory.set_default_gateway_instance(self._llm_gateway_instance)
        else:
            LlmGatewayFactory.set_default_gateway_type(self._llm_gateway_type)

        base_domain_factory = DomainServiceFactory()
        extensions_factory = ExtensionsServiceFactory(base_domain_factory)
        conversation_service = extensions_factory.get_conversation_service()
        embedding_service = EmbeddingService()
        fact_repository = (
            MongoFactRepository()
            if ENVIRONMENT == "production"
            else InMemoryFactRepository()
        )
        fact_service = FactServiceFactory(
            conversation_service=conversation_service,
            embedding_service=embedding_service,
            repository=fact_repository,
        ).create()
        self._domain_service_factory = FactDomainServiceFactory(fact_service)
        fact_context_assembler = FactContextBuilder(fact_service)
        fact_context_service = FactContextWindowService(fact_service, window=5)
        pipeline_factory = MessagePipelineFactory(self._domain_service_factory)
        pipeline = pipeline_factory.create_message_pipeline(
            notion_enrichment_enabled=False,
            llm_gateway_instance=self._llm_gateway_instance,
        )
        qa_factory = QuestionAnsweringServiceFactory(self._domain_service_factory)
        question_answering_service = qa_factory.create(
            llm_gateway_instance=self._llm_gateway_instance,
            fact_context_assembler=fact_context_assembler,
            fact_context_service=fact_context_service,
            fact_service=fact_service,
        )
        router = QuestionRoutingService(question_answering_service)
        chat_synthesizer = ChatResponseSynthesizer()
        command_factory = ChatCommandFactory(
            pipeline=pipeline,
            question_answering_service=question_answering_service,
        )
        remember_command = command_factory.create_remember_command()
        search_command = command_factory.create_search_command()
        background_info_command = command_factory.create_background_info_command()
        chat_flow_factory = ChatFlowBuilder(
            input_router=router,
            remember_command=remember_command,
            search_command=search_command,
        )
        chat_flow = chat_flow_factory.create()
        user_agent_resolver_factory = UserAgentResolverFactory(
            default_user_id=self._DEFAULT_USER_ID,
            default_agent_id=self._DEFAULT_AGENT_ID,
        )
        user_agent_resolver = user_agent_resolver_factory.create()
        large_text_processor = SentenceBatcher(MAX_SENTENCES_PER_INPUT)
        turn_recorder = FactTurnTracker(fact_service)
        self._processor = DeepthinkPipelineProcessor(
            chat_flow=chat_flow,
            remember_command=remember_command,
            search_command=search_command,
            background_info_command=background_info_command,
            user_agent_resolver=user_agent_resolver,
            large_text_processor=large_text_processor,
            fact_service=fact_service,
            turn_recorder=turn_recorder,
            chat_synthesizer=chat_synthesizer,
        )

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

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

    def remember(
        self,
        input_text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> Optional[str]:
        return self._processor.remember(
            input_text=input_text,
            user_agent=user_agent,
            previous_messages_stack=previous_messages_stack,
        )

    def search(
        self,
        input_text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
        synthesized_text_type: Optional[SynthesizedTextType] = None,
    ) -> Optional[str]:
        return self._processor.search(
            input_text=input_text,
            user_agent=user_agent,
            previous_messages_stack=previous_messages_stack,
            synthesized_text_type=synthesized_text_type,
        )

    def generate_background_info(
        self,
        input_text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> str:
        return self._processor.generate_background_info(
            input_text=input_text,
            user_agent=user_agent,
            previous_messages_stack=previous_messages_stack,
        )

    def get_background_info(
        self,
        input_text: str,
        user_agent: Optional[UserAgent] = None,
        previous_messages_stack: Optional[PreviousMessagesStack] = None,
    ) -> str:
        return self._processor.get_background_info(
            input_text=input_text,
            user_agent=user_agent,
            previous_messages_stack=previous_messages_stack,
        )


DeepThinker = DeepthinkEngine
Thinker = DeepthinkEngine
