import uuid
from collections import defaultdict
from typing import Any

import logfire
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import select

from app.auth.tenant import get_current_restaurant, get_tenant_session
from app.models.common_question import (
    CommonQuestion,
    CommonQuestionBulkUpdate,
    CommonQuestionCreateCustom,
    CommonQuestionGrouped,
    CommonQuestionRead,
    CommonQuestionUpdate,
)
from app.models.knowledge import KnowledgeDocument
from app.models.restaurant import Restaurant

router = APIRouter(prefix="/common-questions", tags=["common-questions"])

_CATEGORY_ORDER = [
    "General Info",
    "Menu & Dietary",
    "Facilities",
    "Policies",
    "Special Occasions",
    "Takeaway & Delivery",
    "Custom",
]


async def _sync_common_question_embedding(entry: CommonQuestion, session: AsyncSession) -> None:
    from app.rag.embeddings import generate_embedding

    text = f"Q: {entry.question_text}\nA: {entry.answer}"

    result = await session.execute(
        select(KnowledgeDocument).where(
            KnowledgeDocument.restaurant_id == entry.restaurant_id,
            KnowledgeDocument.source == "common-question",
            KnowledgeDocument.metadata_["common_question_id"].as_string() == entry.id,  # type: ignore[index]
        )
    )
    existing_doc = result.scalar_one_or_none()

    if not entry.is_answered:
        if existing_doc:
            await session.delete(existing_doc)
            await session.commit()
        return

    embedding = None
    try:
        embedding = await generate_embedding(text)
    except Exception as exc:
        logfire.error(
            "common_question_embedding_failed",
            error=str(exc),
            common_question_id=entry.id,
            retriable=True,
        )

    if existing_doc:
        existing_doc.content = text
        existing_doc.embedding = embedding
        existing_doc.metadata_ = {
            "common_question_id": entry.id,
            "question_key": entry.question_key,
            "source_type": "common-question",
        }
        session.add(existing_doc)
    else:
        doc = KnowledgeDocument(
            id=str(uuid.uuid4()),
            restaurant_id=entry.restaurant_id,
            content=text,
            source="common-question",
            embedding=embedding,
            metadata_={
                "common_question_id": entry.id,
                "question_key": entry.question_key,
                "source_type": "common-question",
            },
        )
        session.add(doc)

    await session.commit()


@router.get("/", response_model=list[CommonQuestionGrouped])
async def list_common_questions(
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> list[Any]:
    from app.services.common_questions import seed_common_questions

    await seed_common_questions(session, restaurant.id)

    result = await session.execute(
        select(CommonQuestion)
        .where(CommonQuestion.restaurant_id == restaurant.id)
        .order_by(CommonQuestion.category, CommonQuestion.created_at)  # type: ignore[arg-type]
    )
    questions = result.scalars().all()

    groups: dict[str, list[Any]] = defaultdict(list)
    for q in questions:
        cat = q.category or "Other"
        groups[cat].append(q)

    ordered: list[dict[str, Any]] = []
    for cat in _CATEGORY_ORDER:
        if cat in groups:
            ordered.append({"category": cat, "questions": groups.pop(cat)})
    for cat, qs in groups.items():
        ordered.append({"category": cat, "questions": qs})
    return ordered


@router.post("/", response_model=CommonQuestionRead, status_code=status.HTTP_201_CREATED)
async def create_common_question(
    payload: CommonQuestionCreateCustom,
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> Any:
    question = CommonQuestion(
        restaurant_id=restaurant.id,
        question_key=f"custom_{uuid.uuid4().hex[:8]}",
        question_text=payload.question_text,
        category=payload.category or "Custom",
    )
    session.add(question)
    await session.commit()
    await session.refresh(question)
    return question


@router.delete("/{question_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_common_question(
    question_id: str,
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> None:
    result = await session.execute(
        select(CommonQuestion).where(
            CommonQuestion.id == question_id,
            CommonQuestion.restaurant_id == restaurant.id,
        )
    )
    question = result.scalar_one_or_none()
    if question is None:
        raise HTTPException(status_code=404, detail="Common question not found")
    if not question.question_key.startswith("custom_"):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Cannot delete a predefined question",
        )
    # Clean up associated knowledge doc
    doc_result = await session.execute(
        select(KnowledgeDocument).where(
            KnowledgeDocument.restaurant_id == restaurant.id,
            KnowledgeDocument.source == "common-question",
            KnowledgeDocument.metadata_["common_question_id"].as_string() == question.id,  # type: ignore[index]
        )
    )
    doc = doc_result.scalar_one_or_none()
    if doc:
        await session.delete(doc)
    await session.delete(question)
    await session.commit()


@router.patch("/{question_id}", response_model=CommonQuestionRead)
async def update_common_question(
    question_id: str,
    payload: CommonQuestionUpdate,
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> Any:
    result = await session.execute(
        select(CommonQuestion).where(
            CommonQuestion.id == question_id,
            CommonQuestion.restaurant_id == restaurant.id,
        )
    )
    question = result.scalar_one_or_none()
    if question is None:
        raise HTTPException(status_code=404, detail="Common question not found")

    if payload.answer is not None:
        question.answer = payload.answer
        question.is_answered = bool(payload.answer.strip())

    session.add(question)
    await session.commit()
    await session.refresh(question)

    await _sync_common_question_embedding(question, session)

    return question


@router.post("/bulk-update", response_model=list[CommonQuestionRead])
async def bulk_update_common_questions(
    payload: CommonQuestionBulkUpdate,
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> list[Any]:
    if not payload.items:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail="No items provided",
        )

    ids = [item.id for item in payload.items]
    result = await session.execute(
        select(CommonQuestion).where(
            CommonQuestion.id.in_(ids),  # type: ignore[attr-defined]
            CommonQuestion.restaurant_id == restaurant.id,
        )
    )
    questions_by_id = {q.id: q for q in result.scalars().all()}

    missing = [item.id for item in payload.items if item.id not in questions_by_id]
    if missing:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail=f"Invalid question IDs: {', '.join(missing)}",
        )

    updated: list[Any] = []
    for item in payload.items:
        question = questions_by_id[item.id]
        question.answer = item.answer
        question.is_answered = bool(item.answer.strip())
        session.add(question)
        updated.append(question)

    await session.commit()
    for q in updated:
        await session.refresh(q)

    for q in updated:
        await _sync_common_question_embedding(q, session)

    return updated
