from typing import TYPE_CHECKING

from analogai.deepthink.startup.factories.domain_service_factory import DomainServiceFactory
from analogai.deepthink.application.value_objects.llm_config import LlmConfig
from analogai.deepthink.application.value_objects.embedding_config import EmbeddingConfig
from analogai.deepthink.infrastructure.gateways.llm.gateway import LlmGateway
from analogai.deepthink.infrastructure.gateways.llm.enums.llm_provider import LlmProvider
from analogai.deepthink.deepthink_engine import DeepthinkEngine
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.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_context_service import FactContextWindowService
from analogai.deepthink.messaging.fact.services.fact_turn_recorder import FactTurnTracker
from analogai.deepthink.messaging.factories.pipeline_factory import MessagePipelineFactory
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.messaging.question_answering.factories.question_answering_service_factory import (
    QuestionAnsweringServiceFactory,
)
from analogai.deepthink.messaging.question_answering.question_routing_service import (
    QuestionRoutingService,
)
from analogai.foundation.infrastructure.services.embedding_service import EmbeddingService
from analogai.foundation.settings import config as infra_config
from analogai.deepthink.settings import config as app_config

if TYPE_CHECKING:
    from analogai.deepthink.application.usecases.learning.make_direct_statement.ports import MakeStatementOutputPort
    from analogai.deepthink.application.usecases.learning.learn_statement.learn_statement_usecase import MakeStatementUseCase
from analogai.deepthink.application.usecases.learning.make_direct_statement.states.common.response_builder import LearnerResponseBuilder
from analogai.deepthink.application.usecases.learning.make_direct_statement.states.context.learner_context import LearnerContext
from analogai.deepthink.application.usecases.learning.make_direct_statement.states.context.learner_context_handler import LearnerContextHandler
from analogai.deepthink.presentation.presenters.learning.make_direct_statement_presenter import MakeDirectStatementPresenter
from analogai.deepthink.presentation.controllers.learning_controller import LearningController
from analogai.deepthink.presentation.factories.intents_factory import IntentsFactory
from analogai.deepthink.presentation.factories.interpreters_factory import InterpretersFactory
from analogai.deepthink.presentation.intents.intents_processor import IntentsProcessor
from analogai.deepthink.presentation.interpreters.interpreter_pipeline import InterpreterPipeline
from analogai.deepthink.presentation.pipeline.pipeline import MessagePipeline
from analogai.deepthink.presentation.preprocessing.previous_messages_stack import PreviousMessagesStack


def _create_domain_services() -> DomainServiceFactory:
    return DomainServiceFactory()


def _create_extensions_factory(domain_service_factory: DomainServiceFactory):
    from analogai.deepthink.startup.extensions_service_factory import ExtensionsServiceFactory
    return ExtensionsServiceFactory(domain_service_factory)


def _create_learning_use_cases(domain_service_factory: DomainServiceFactory, learning_presenter=None):
    from analogai.deepthink.application.usecases.learning.learn_statement.learn_statement_usecase import MakeStatementUseCase
    from analogai.deepthink.application.usecases.learning.learn_statement.learn_statement_usecase import DirectStatementProvider
    from analogai.deepthink.application.usecases.learning.make_direct_statement.usecase import MakeDirectStatementUseCase
    from analogai.deepthink.application.usecases.learning.make_notion_statement.usecase import UpsertNotionStatementUseCase
    from analogai.deepthink.application.usecases.learning.make_conditional_statement.usecase import MakeConditionalStatementUseCase
    from analogai.deepthink.presentation.presenters.learning.make_conditional_statement_presenter import MakeConditionalStatementPresenter
    from analogai.deepthink.presentation.presenters.learning.make_notion_statement_presenter import MakeNotionStatementPresenter

    notion_service = domain_service_factory.get_notion_service()
    if learning_presenter is None:
        learning_presenter = MakeDirectStatementPresenter()
    direct_statement_service = domain_service_factory.get_direct_statement_service()

    class _DirectStatementProvider(DirectStatementProvider):
        def __init__(self):
            self._instance = None

        def get(self):
            if self._instance is None:
                context = LearnerContext()
                context_handler = LearnerContextHandler(context, notion_service)
                response_builder = LearnerResponseBuilder(context)
                self._instance = MakeDirectStatementUseCase(
                    statement_service=direct_statement_service,
                    notion_service=notion_service,
                    output_port=learning_presenter,
                    context_handler=context_handler,
                    response_builder=response_builder,
                )
            return self._instance
    fact_service = None
    if hasattr(domain_service_factory, "get_fact_service"):
        try:
            fact_service = domain_service_factory.get_fact_service()
        except Exception:
            fact_service = None
    make_notion_statement = UpsertNotionStatementUseCase(
        notion_service=notion_service,
        output_port=MakeNotionStatementPresenter(fact_service=fact_service),
    )
    make_statement = MakeStatementUseCase(
        direct_statement_provider=_DirectStatementProvider(),
        make_notion_statement=make_notion_statement,
    )
    causal_presenter = MakeConditionalStatementPresenter()
    make_conditional_statement = MakeConditionalStatementUseCase(
        domain_service_factory=domain_service_factory,
        output_port=causal_presenter,
    )
    return make_statement, make_conditional_statement




def _build_pipeline(
    domain_service_factory: DomainServiceFactory,
    notion_enrichment_enabled: bool | None = None,
    llm_gateway_instance=None,
) -> MessagePipeline:
    from analogai.deepthink.infrastructure.gateways.llm.llm_factory import LlmGatewayFactory

    learning_presenter = MakeDirectStatementPresenter()

    make_statement, make_conditional_statement = _create_learning_use_cases(
        domain_service_factory, learning_presenter=learning_presenter
    )
    if llm_gateway_instance is not None:
        LlmGatewayFactory.set_default_gateway_instance(llm_gateway_instance)

    # Automatic notion enrichment removed; use NotionEnrichmentSleeper instead.
    learning_controller = LearningController(
        learn_statement_usecase=make_statement,
        make_conditional_statement_usecase=make_conditional_statement,
    )
    intents_factory = IntentsFactory(
        domain_service_factory=domain_service_factory,
        learning_controller=learning_controller,
    )
    intents_processor = IntentsProcessor(intents_factory)
    interpreters_factory = InterpretersFactory()
    interpreters_processor = InterpreterPipeline(interpreters_factory)
    return MessagePipeline(
        interpreters=interpreters_processor,
        intents_processor=intents_processor,
    )


def create_pipeline(
    domain_service_factory=None,
    notion_enrichment_enabled: bool | None = None,
    llm_gateway_instance=None,
):
    if domain_service_factory is None:
        domain_service_factory = _create_domain_services()
    return _build_pipeline(
        domain_service_factory,
        notion_enrichment_enabled=notion_enrichment_enabled,
        llm_gateway_instance=llm_gateway_instance,
    )


def _resolve_llm_provider(value: str | LlmProvider | None) -> LlmProvider | None:
    if value is None:
        return None
    if isinstance(value, LlmProvider):
        return value
    normalized = str(value).strip().lower().replace("-", "_")
    legacy_map = {
        "claude": LlmProvider.ANTHROPIC,
        "azureopenai": LlmProvider.AZURE_OPENAI,
        "xai": LlmProvider.XAI,
    }
    if normalized in legacy_map:
        return legacy_map[normalized]
    return LlmProvider(normalized)


def _log_llm_model_choice(llm_model: str) -> None:
    if llm_model:
        print(f"LLM model selected: {llm_model}")
    else:
        print("LLM model selected: (empty)")


def create_deepthink_engine(
    llm_gateway_instance: LlmGateway | None = None,
    llm_provider: LlmProvider | None = None,
    llm_model: str | None = None,
    llm_config: LlmConfig | None = None,
    embedding_config: EmbeddingConfig | None = None,
    domain_service_factory: DomainServiceFactory | None = None,
    conversation_service: object | None = None,
    embedding_service: EmbeddingService | None = None,
    fact_service: object | None = None,
    pipeline: object | None = None,
    question_answering_service: object | None = None,
    chat_synthesizer: ChatResponseSynthesizer | None = None,
):
    from analogai.deepthink.infrastructure.gateways.llm.llm_gateway_provider_factory import (
        LlmGatewayProviderFactory,
    )
    from analogai.deepthink.infrastructure.gateways.llm.llm_factory import LlmGatewayFactory

    resolved_gateway_instance = (
        llm_config.gateway_instance if llm_config else None
    ) or llm_gateway_instance
    resolved_provider = (
        llm_config.provider if llm_config else None
    ) or llm_provider
    resolved_model = (llm_config.model if llm_config else None) or llm_model

    if resolved_provider is None:
        resolved_provider = _resolve_llm_provider(
            app_config.get("llm.deepthink.provider")
        )
    if not resolved_model:
        resolved_model = app_config.get("llm.deepthink.model")

    if not resolved_model:
        raise ValueError("llm_model must be provided")
    if resolved_provider is None:
        raise ValueError("llm_provider must be provided")

    _log_llm_model_choice(resolved_model)
    LlmGatewayProviderFactory.set_default_gateway_type(resolved_provider)
    LlmGatewayProviderFactory.set_default_model(resolved_model)
    if resolved_gateway_instance is not None:
        resolved_gateway_instance.set_model(resolved_model)
        LlmGatewayFactory.set_default_gateway_instance(resolved_gateway_instance)
    else:
        LlmGatewayFactory.set_default_gateway_type(resolved_provider)

    if domain_service_factory is None:
        domain_service_factory = _create_domain_services()

    if conversation_service is None:
        extensions_factory = _create_extensions_factory(domain_service_factory)
        conversation_service = extensions_factory.get_conversation_service()

    cleanup_callbacks = []
    if embedding_service is None:
        if embedding_config and embedding_config.provider_instance is not None:
            embedding_service = embedding_config.provider_instance
        else:
            overrides = embedding_config.to_overrides() if embedding_config else {}
            if overrides:
                override_ctx = infra_config.override(**overrides)
                override_ctx.__enter__()
                cleanup_callbacks.append(lambda: override_ctx.__exit__(None, None, None))
            embedding_service = EmbeddingService()

    if fact_service is None:
        fact_service = FactServiceFactory(
            embedding_service=embedding_service,
        ).create()

    fact_domain_factory = FactDomainServiceFactory(fact_service)
    fact_context_assembler = FactContextBuilder(fact_service)
    fact_context_service = FactContextWindowService(fact_service, window=5)

    if pipeline is None:
        pipeline_factory = MessagePipelineFactory(fact_domain_factory)
        pipeline = pipeline_factory.create_message_pipeline(
            notion_enrichment_enabled=app_config.get("app.features.notion_enrichment", False),
            llm_gateway_instance=resolved_gateway_instance,
        )

    if question_answering_service is None:
        qa_factory = QuestionAnsweringServiceFactory(fact_domain_factory)
        question_answering_service = qa_factory.create(
            llm_gateway_instance=resolved_gateway_instance,
            fact_context_assembler=fact_context_assembler,
            fact_context_service=fact_context_service,
            fact_service=fact_service,
        )

    router = QuestionRoutingService(question_answering_service)
    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="default-user",
        default_agent_id="default-agent",
    )
    user_agent_resolver = user_agent_resolver_factory.create()
    max_sentences_per_input = max(
        1,
        app_config.get("limits.max_sentences_per_input", 3),
    )
    large_text_processor = SentenceBatcher(max_sentences_per_input)
    turn_recorder = FactTurnTracker(fact_service)
    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,
        conversation_service=conversation_service,
        chat_synthesizer=chat_synthesizer or ChatResponseSynthesizer(),
    )

    def _cleanup() -> None:
        for callback in reversed(cleanup_callbacks):
            callback()

    return DeepthinkEngine(processor=processor, on_close=_cleanup if cleanup_callbacks else None)

