import uuid
from typing import Any

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

from app.auth.tenant import get_current_restaurant, get_tenant_session
from app.models.faq_entry import FaqEntry, FaqEntryCreate, FaqEntryRead, FaqEntryUpdate
from app.models.knowledge import KnowledgeDocument
from app.models.restaurant import Restaurant

router = APIRouter(prefix="/faq-entries", tags=["faq-entries"])


class FaqImportRequest(BaseModel):
    document_ids: list[str]


# ---------------------------------------------------------------------------
# Helper – RAG sync
# ---------------------------------------------------------------------------


async def _sync_faq_embedding(entry: FaqEntry, session: AsyncSession) -> None:
    """Create or update a KnowledgeDocument mirroring this FAQ entry for RAG retrieval."""
    from app.rag.embeddings import generate_embedding

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

    # Find existing knowledge doc for this FAQ entry
    result = await session.execute(
        select(KnowledgeDocument).where(
            KnowledgeDocument.restaurant_id == entry.restaurant_id,
            KnowledgeDocument.source == "faq",
            KnowledgeDocument.metadata_["faq_entry_id"].as_string() == entry.id,  # type: ignore[index]
        )
    )
    existing_doc = result.scalar_one_or_none()

    if not entry.is_active:
        # Remove from knowledge index if deactivated
        if existing_doc:
            await session.delete(existing_doc)
            await session.commit()
        return

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

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

    await session.commit()


# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------


@router.get("/", response_model=list[FaqEntryRead])
async def list_faq_entries(
    category: str | None = Query(default=None),
    is_active: bool | None = Query(default=None),
    limit: int = Query(default=50, ge=1, le=200),
    offset: int = Query(default=0, ge=0),
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> list[Any]:
    query = select(FaqEntry).where(FaqEntry.restaurant_id == restaurant.id)
    if category is not None:
        query = query.where(FaqEntry.category == category)
    if is_active is not None:
        query = query.where(FaqEntry.is_active == is_active)
    query = (
        query.order_by(
            FaqEntry.updated_at.desc().nulls_last(),  # type: ignore[union-attr]
            FaqEntry.created_at.desc(),  # type: ignore[attr-defined]
        )
        .offset(offset)
        .limit(limit)
    )
    result = await session.execute(query)
    return list(result.scalars().all())


@router.post("/", response_model=FaqEntryRead, status_code=status.HTTP_201_CREATED)
async def create_faq_entry(
    payload: FaqEntryCreate,
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> Any:
    question = (payload.question or "").strip()
    answer = (payload.answer or "").strip()
    if not question or not answer:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail="Question and answer must be non-empty.",
        )

    entry = FaqEntry(
        **payload.model_dump(),
        restaurant_id=restaurant.id,
    )
    session.add(entry)
    await session.commit()
    await session.refresh(entry)

    await _sync_faq_embedding(entry, session)

    return entry


@router.patch("/{entry_id}", response_model=FaqEntryRead)
async def update_faq_entry(
    entry_id: str,
    payload: FaqEntryUpdate,
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> Any:
    result = await session.execute(
        select(FaqEntry).where(
            FaqEntry.id == entry_id,
            FaqEntry.restaurant_id == restaurant.id,
        )
    )
    entry = result.scalar_one_or_none()
    if entry is None:
        raise HTTPException(status_code=404, detail="FAQ entry not found")

    updates = payload.model_dump(exclude_unset=True)

    # Validate non-empty question/answer if being updated
    if "question" in updates and not (updates["question"] or "").strip():
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail="Question must be non-empty.",
        )
    if "answer" in updates and not (updates["answer"] or "").strip():
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail="Answer must be non-empty.",
        )

    for key, value in updates.items():
        setattr(entry, key, value)

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

    await _sync_faq_embedding(entry, session)

    return entry


@router.delete("/{entry_id}", status_code=status.HTTP_200_OK)
async def delete_faq_entry(
    entry_id: str,
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> dict[str, str]:
    result = await session.execute(
        select(FaqEntry).where(
            FaqEntry.id == entry_id,
            FaqEntry.restaurant_id == restaurant.id,
        )
    )
    entry = result.scalar_one_or_none()
    if entry is None:
        raise HTTPException(status_code=404, detail="FAQ entry not found")

    # Delete associated KnowledgeDocument(s)
    kd_result = await session.execute(
        select(KnowledgeDocument).where(
            KnowledgeDocument.restaurant_id == entry.restaurant_id,
            KnowledgeDocument.source == "faq",
            KnowledgeDocument.metadata_["faq_entry_id"].as_string() == entry.id,  # type: ignore[index]
        )
    )
    for doc in kd_result.scalars().all():
        await session.delete(doc)

    await session.delete(entry)
    await session.commit()
    return {"status": "deleted"}


@router.post("/import")
async def bulk_import_from_knowledge(
    payload: FaqImportRequest,
    session: AsyncSession = Depends(get_tenant_session),
    restaurant: Restaurant = Depends(get_current_restaurant),
) -> dict[str, int]:
    imported = 0
    skipped = 0

    for doc_id in payload.document_ids:
        # Lookup knowledge document
        result = await session.execute(
            select(KnowledgeDocument).where(
                KnowledgeDocument.id == doc_id,
                KnowledgeDocument.restaurant_id == restaurant.id,
            )
        )
        doc = result.scalar_one_or_none()
        if doc is None:
            skipped += 1
            continue

        question = doc.content[:200]
        normalized = question.strip().lower()

        # Duplicate check
        dup_result = await session.execute(
            select(FaqEntry).where(
                FaqEntry.restaurant_id == restaurant.id,
                func.lower(FaqEntry.question) == normalized,
            )
        )
        is_dup = dup_result.scalars().all()

        if is_dup:
            skipped += 1
            continue

        entry = FaqEntry(
            restaurant_id=restaurant.id,
            question=question,
            answer=doc.content,
            category=doc.source,
            is_active=True,
        )
        session.add(entry)
        imported += 1

    await session.commit()
    return {"imported": imported, "skipped": skipped, "total": len(payload.document_ids)}
