from __future__ import annotations

from dataclasses import dataclass
from typing import Callable

from backend.config import get_default_llm_id
from backend.models.events import EventKind
from backend.services.conversation_text_fallback_service import generate_missing_conversation_text_fallbacks
from backend.services.derived_documents_service import get_structured_documents
from backend.services.helpers.multimodal_attachment_handling import is_text_fallback_generation_required
from backend.services.helpers.non_multimodal_attachment_routing import (
    prepare_attachment_routing,
    resolve_multimodal_capability,
    target_is_multimodal,
)
from backend.utils.llm_utils import normalize_llm_id
from backend.utils.logging_utils import get_logger

logger = get_logger(__name__)


@dataclass
class NonMultimodalTurnPreparationResult:
    history: list[dict]
    filtered_sel_agents: list[dict]
    history_by_target: dict[str, list[dict]]
    event_payloads: list[dict]
    blocking_error: str | None


def prepare_turn_for_non_multimodal(
    *,
    store,
    project,
    user_agents_by_id: dict,
    conv_id: str,
    user_login: str,
    agent_ids: list[str],
    conv_messages,
    sel_agents: list[dict],
    agents_enabled: bool,
    build_history: Callable[..., list[dict]],
    emit_event_payload: Callable[[dict], None] | None = None,
) -> NonMultimodalTurnPreparationResult:
    """Prepare history and target routing for non-multimodal compatibility before streaming."""
    history = _build_history_with_documents(
        store=store,
        conv_id=conv_id,
        user_login=user_login,
        agent_ids=agent_ids,
        conv_messages=conv_messages,
        agents_enabled=agents_enabled,
        build_history=build_history,
    )

    fallback_required = is_text_fallback_generation_required(
        sel_agents=sel_agents,
        history=history,
        base_llm_id=normalize_llm_id(get_default_llm_id()),
        is_base_llm_multimodal=lambda: resolve_multimodal_capability(project, normalize_llm_id(get_default_llm_id())),
        is_target_multimodal=lambda target_id: target_is_multimodal(
            target_id=target_id,
            project=project,
            user_agents_by_id=user_agents_by_id,
        ),
    )

    if fallback_required:
        if emit_event_payload is not None:
            emit_event_payload({"eventKind": EventKind.DOCUMENT_TEXT_EXTRACTION, "eventData": {}})
        history = _generate_text_fallback_and_rebuild_history(
            store=store,
            conv_id=conv_id,
            user_login=user_login,
            agent_ids=agent_ids,
            conv_messages=conv_messages,
            agents_enabled=agents_enabled,
            build_history=build_history,
        )

    filter_result = prepare_attachment_routing(
        sel_agents=sel_agents,
        history=history,
        mode_agents=agents_enabled,
        project=project,
        user_agents_by_id=user_agents_by_id,
        get_default_llm_id=get_default_llm_id,
    )

    return NonMultimodalTurnPreparationResult(
        history=history,
        filtered_sel_agents=filter_result.filtered_targets or [],
        history_by_target=filter_result.history_by_target,
        event_payloads=filter_result.events,
        blocking_error=filter_result.blocking_error,
    )


def _build_history_with_documents(
    *,
    store,
    conv_id: str,
    user_login: str,
    agent_ids: list[str],
    conv_messages,
    agents_enabled: bool,
    build_history: Callable[..., list[dict]],
    log_context: str = "",
) -> list[dict]:
    documents_map: dict[str, dict] = {}
    try:
        documents_map = get_structured_documents(store, conv_id, user_login)
    except Exception:
        if log_context:
            logger.exception("Failed to build structured prompt %s for conversation %s", log_context, conv_id)
        else:
            logger.exception("Failed to build structured prompt for conversation %s", conv_id)

    return build_history(
        agent_ids,
        conv_messages,
        documents_map=documents_map,
        agents_enabled=agents_enabled,
    )


def _generate_text_fallback_and_rebuild_history(
    *,
    store,
    conv_id: str,
    user_login: str,
    agent_ids: list[str],
    conv_messages,
    agents_enabled: bool,
    build_history: Callable[..., list[dict]],
) -> list[dict]:
    fallback_generated = generate_missing_conversation_text_fallbacks(
        store=store,
        conv_id=conv_id,
    )
    if fallback_generated:
        logger.info("Text fallback generation completed for conversation %s", conv_id)
    else:
        logger.debug(
            "Text fallback generation was required but no missing fallback was found for conversation %s",
            conv_id,
        )

    return _build_history_with_documents(
        store=store,
        conv_id=conv_id,
        user_login=user_login,
        agent_ids=agent_ids,
        conv_messages=conv_messages,
        agents_enabled=agents_enabled,
        build_history=build_history,
        log_context="after text fallback generation",
    )
