from __future__ import annotations

from backend.utils.logging_utils import get_logger

from . import internal_utils, prompts

logger = get_logger(__name__)


def generate_document_yaml(
    project,
    llm_id: str,
    user_prompt: str,
    *,
    user_message: dict[str, object] | None = None,
    conversation_history: list[dict[str, object]] | None = None,
) -> str:
    return _run_llm_phase(
        project=project,
        llm_id=llm_id,
        phase="document",
        messages=[
            ("system", prompts.build_docx_system_prompt()),
            *(conversation_history or []),
            _build_user_message(
                text=prompts.build_docx_document_user_prompt(user_prompt),
                source_message=user_message,
            ),
        ],
    )


def revise_document_yaml(
    *,
    project,
    llm_id: str,
    source_document_yaml: str,
    revision_instructions: str,
    user_message: dict[str, object] | None = None,
    conversation_history: list[dict[str, object]] | None = None,
) -> str:
    return _run_llm_phase(
        project=project,
        llm_id=llm_id,
        phase="revision",
        messages=[
            ("system", prompts.build_docx_revision_system_prompt()),
            *(conversation_history or []),
            _build_user_message(
                text=prompts.build_docx_revision_user_prompt(
                    source_document_yaml=source_document_yaml,
                    revision_instructions=revision_instructions,
                ),
                source_message=user_message,
            ),
        ],
    )


def repair_document_yaml(
    *,
    project,
    llm_id: str,
    user_prompt: str,
    user_message: dict[str, object] | None,
    current_document_yaml: str,
    validation_error: str,
    quality_feedback: str | None,
    attempt: int,
    max_attempts: int,
) -> str:
    logger.warning(
        "DOCX document repair requested for attempt %s/%s: %s",
        attempt - 1,
        max_attempts,
        validation_error,
    )
    return _run_llm_phase(
        project=project,
        llm_id=llm_id,
        phase="repair",
        messages=[
            ("system", prompts.build_docx_system_prompt()),
            _build_user_message(
                text=prompts.build_docx_repair_prompt(
                    user_prompt=user_prompt,
                    current_document_yaml=current_document_yaml,
                    validation_error=validation_error,
                    quality_feedback=quality_feedback,
                    attempt=attempt,
                    max_attempts=max_attempts,
                ),
                source_message=user_message,
            ),
        ],
    )


def repair_revision_document_yaml(
    *,
    project,
    llm_id: str,
    source_document_yaml: str,
    revision_instructions: str,
    user_message: dict[str, object] | None,
    current_document_yaml: str,
    validation_error: str,
    quality_feedback: str | None,
    attempt: int,
    max_attempts: int,
) -> str:
    logger.warning(
        "DOCX revision repair requested for attempt %s/%s: %s",
        attempt - 1,
        max_attempts,
        validation_error,
    )
    return _run_llm_phase(
        project=project,
        llm_id=llm_id,
        phase="revision_repair",
        messages=[
            ("system", prompts.build_docx_revision_system_prompt()),
            _build_user_message(
                text=prompts.build_docx_revision_repair_prompt(
                    source_document_yaml=source_document_yaml,
                    revision_instructions=revision_instructions,
                    current_document_yaml=current_document_yaml,
                    validation_error=validation_error,
                    quality_feedback=quality_feedback,
                    attempt=attempt,
                    max_attempts=max_attempts,
                ),
                source_message=user_message,
            ),
        ],
    )


def repair_revision_syntax_only(
    *,
    project,
    llm_id: str,
    source_document_yaml: str,
    revision_instructions: str,
    user_message: dict[str, object] | None,
    current_document_yaml: str,
    syntax_error: str,
    attempt: int,
    max_attempts: int,
) -> str:
    logger.warning(
        "DOCX revision syntax repair requested for attempt %s/%s: %s",
        attempt - 1,
        max_attempts,
        syntax_error,
    )
    return _run_llm_phase(
        project=project,
        llm_id=llm_id,
        phase="revision_syntax_repair",
        messages=[
            ("system", prompts.build_docx_revision_system_prompt()),
            _build_user_message(
                text=prompts.build_docx_revision_syntax_repair_prompt(
                    source_document_yaml=source_document_yaml,
                    revision_instructions=revision_instructions,
                    current_document_yaml=current_document_yaml,
                    syntax_error=syntax_error,
                    attempt=attempt,
                    max_attempts=max_attempts,
                ),
                source_message=user_message,
            ),
        ],
    )


def _run_llm_phase(
    *,
    project,
    llm_id: str,
    phase: str,
    messages: list[dict[str, object] | tuple[str, str]],
) -> str:
    normalized_messages = [_normalize_llm_message(message) for message in messages]
    logger.info(
        "DOCX_GEN %s_request llm_id=%s message_count=%s roles=%s",
        phase,
        llm_id,
        len(normalized_messages),
        ",".join(str(message.get("role") or "") for message in normalized_messages),
    )
    response = internal_utils.execute_llm_completion(project, llm_id, normalized_messages)
    response_text = getattr(response, "text", None)
    logger.info(
        "DOCX_GEN %s_response success=%s text_chars=%s",
        phase,
        getattr(response, "success", None),
        len(response_text) if isinstance(response_text, str) else 0,
    )
    return internal_utils.extract_llm_text(response)


def _build_user_message(*, text: str, source_message: dict[str, object] | None) -> dict[str, object]:
    if not source_message:
        return {"role": "user", "content": text}
    parts = source_message.get("parts")
    if not isinstance(parts, list):
        return {"role": "user", "content": text}
    source_text = ""
    first_part = parts[0] if parts else None
    if isinstance(first_part, dict) and first_part.get("type") == "TEXT" and isinstance(first_part.get("text"), str):
        source_text = first_part["text"].strip()
    combined_text = text if not source_text else f"{text}\n\n{source_text}"
    return {
        "role": "user",
        "parts": [{"type": "TEXT", "text": combined_text}, *parts[1:]],
    }


def _normalize_llm_message(message: dict[str, object] | tuple[str, str]) -> dict[str, object]:
    if isinstance(message, dict):
        return dict(message)
    if isinstance(message, tuple) and len(message) == 2:
        role, content = message
        return {"role": role, "content": content}
    raise TypeError(f"Unsupported LLM message shape: {type(message)!r}")
