"""Search and generation endpoints for Tale RAG service."""

import time

from fastapi import APIRouter, Depends, HTTPException, status
from loguru import logger

from ..auth import require_org_slug
from ..models import (
    GenerateRequest,
    GenerateResponse,
    QueryRequest,
    QueryResponse,
    SearchResult,
    UsageInfo,
)
from ..services.rag_service import rag_service

router = APIRouter(prefix="/api/v1", tags=["Search"])


@router.post("/search", response_model=QueryResponse)
async def search(
    request: QueryRequest,
    org_slug: str = Depends(require_org_slug),
):
    """Search the knowledge base using hybrid BM25 + vector search."""
    try:
        start_time = time.time()

        results, search_usage = await rag_service.search(
            org_slug,
            query=request.query,
            top_k=request.top_k,
            similarity_threshold=request.similarity_threshold,
            file_ids=request.file_ids,
            folder_path=request.effective_folder_path,
            metadata_filters=request.metadata_filters,
        )

        processing_time = (time.time() - start_time) * 1000

        search_results = [
            SearchResult(
                content=r.get("content", ""),
                score=r.get("score", 0.0),
                file_id=r.get("file_id"),
                filename=r.get("filename"),
                source_created_at=r.get("source_created_at"),
                source_modified_at=r.get("source_modified_at"),
                metadata=r.get("metadata") if request.include_metadata else None,
            )
            for r in results
        ]

        # `search_usage` is the per-call value returned alongside results
        # — no shared singleton, no cross-request mis-attribution.
        usage = None
        if search_usage:
            usage = UsageInfo(
                input_tokens=search_usage.prompt_tokens,
                total_tokens=search_usage.total_tokens,
                model=search_usage.model,
            )

        return QueryResponse(
            success=True,
            query=request.query,
            results=search_results,
            total_results=len(search_results),
            processing_time_ms=processing_time,
            usage=usage,
        )

    except Exception as e:
        logger.exception("Search failed")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Search failed. Please try again.",
        ) from e


@router.post("/generate", response_model=GenerateResponse)
async def generate(
    request: GenerateRequest,
    org_slug: str = Depends(require_org_slug),
):
    """Generate a response using RAG.

    Retrieves top 30 most relevant chunks, uses temperature 0.3
    for factual, consistent responses, and generates up to 2000 tokens.
    """
    try:
        result = await rag_service.generate(
            org_slug,
            query=request.query,
            file_ids=request.file_ids,
        )

        sources = [
            SearchResult(
                content=s.get("content", ""),
                score=s.get("score", 0.0),
                file_id=s.get("file_id"),
                filename=s.get("filename"),
                source_created_at=s.get("source_created_at"),
                source_modified_at=s.get("source_modified_at"),
                metadata=s.get("metadata"),
            )
            for s in result.get("sources", [])
        ]

        usage_data = result.get("usage")
        gen_usage = UsageInfo(**usage_data) if usage_data else None

        return GenerateResponse(
            success=result.get("success", False),
            query=request.query,
            response=result.get("response", ""),
            sources=sources,
            processing_time_ms=result.get("processing_time_ms", 0),
            usage=gen_usage,
        )

    except Exception as e:
        logger.exception("Generation failed")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to generate response. Please try again.",
        ) from e
