"""API routes for the Unified Search plugin."""

from __future__ import annotations

import logging
import uuid

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.db import get_db
from app.core.jobs import enqueue_job
from app.core.permissions import resolve_permissions, filter_fields_by_permission
from app.deps import get_current_user, require_permission
from app.plugins.builtins.unified_search.provider_registry import get_search_registry
from app.plugins.builtins.unified_search.query_understanding import (
    llm_aggregate_results,
    llm_analyze_query,
)
from app.plugins.builtins.unified_search.schemas import (
    ProviderResponse,
    ReindexRequest,
    SearchRequest,
    SearchResponse,
    SearchResult,
    SimilarRequest,
    SimilarResponse,
    SuggestRequest,
    SuggestResponse,
)
from app.plugins.builtins.unified_search.search_engine import (
    autocomplete,
    find_similar_all_types,
    hybrid_search,
)

logger = logging.getLogger(__name__)

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


# ─── Search ───

@router.post("", dependencies=[Depends(require_permission("search:read"))])
async def search(
    req: SearchRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> SearchResponse:
    """Perform hybrid search with KI query understanding."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    user_id = uuid.UUID(current_user["user_id"])

    # KI query understanding
    query_analysis = await llm_analyze_query(req.query, db=db, tenant_id=tenant_id)

    # Hybrid search
    results = await hybrid_search(
        db=db,
        query_analysis=query_analysis,
        tenant_id=tenant_id,
        entity_types=req.entity_types,
        limit=req.limit,
    )

    # Resolve user permissions for field-level RBAC
    resolved_perms = await resolve_permissions(db, user_id, tenant_id)

    # Map entity_type to module name for field-level permissions
    _ENTITY_TO_MODULE = {
        "contact": "contacts",
        "mail": "mail",
        "file": "dms",
        "event": "calendar",
    }

    # KI result aggregation
    aggregation = await llm_aggregate_results(results, req.query, db=db, tenant_id=tenant_id)

    search_results = [
        SearchResult(
            entity_type=r.get("entity_type", ""),
            entity_id=r.get("entity_id", ""),
            title=r.get("title", ""),
            snippet=r.get("snippet", ""),
            score=r.get("score", 0.0),
            data=filter_fields_by_permission(
                r.get("data", {}),
                resolved_perms,
                _ENTITY_TO_MODULE.get(r.get("entity_type", ""), r.get("entity_type", "")),
            ),
        )
        for r in results
    ]

    return SearchResponse(
        query=req.query,
        normalized_query=query_analysis.get("normalized_query", req.query),
        results=search_results,
        facets=aggregation.get("facets", {}),
        summary=aggregation.get("summary", f"{len(results)} Ergebnisse"),
        suggestions=aggregation.get("suggestions", []),
    )


# ─── Suggest / Autocomplete ───

@router.get("/suggest", dependencies=[Depends(require_permission("search:read"))])
async def suggest(
    q: str = Query(..., min_length=1, max_length=200),
    limit: int = Query(default=10, ge=1, le=50),
    current_user: dict = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> SuggestResponse:
    """Autocomplete suggestions using FTS prefix search."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    suggestions = await autocomplete(db, q, tenant_id, limit)
    return SuggestResponse(suggestions=suggestions)


# ─── Similar ───

@router.post("/similar", dependencies=[Depends(require_permission("search:read"))])
async def find_similar(
    req: SimilarRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> SimilarResponse:
    """Find similar entities across all types based on embedding."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        entity_id = uuid.UUID(req.entity_id)
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid entity_id")

    similar = await find_similar_all_types(
        db=db,
        entity_type=req.entity_type,
        entity_id=entity_id,
        tenant_id=tenant_id,
        limit=req.limit,
    )

    result_dict: dict[str, list[SearchResult]] = {}
    for etype, items in similar.items():
        result_dict[etype] = [
            SearchResult(
                entity_type=r.get("entity_type", etype),
                entity_id=r.get("entity_id", ""),
                title=r.get("title", ""),
                snippet=r.get("snippet", ""),
                score=r.get("score", 0.0),
                data=r.get("data", {}),
            )
            for r in items
        ]

    return SimilarResponse(similar=result_dict)


# ─── Reindex ───

@router.post("/reindex", dependencies=[Depends(require_permission("search:admin"))])
async def reindex(
    req: ReindexRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> dict:
    """Trigger reindexing of specified entity types."""
    entity_types = req.entity_types
    if not entity_types:
        entity_types = get_search_registry().get_entity_types()

    job_ids: list[str] = []
    for etype in entity_types:
        job_id = await enqueue_job("reindex", etype)
        if job_id:
            job_ids.append(job_id)

    return {
        "status": "ok",
        "message": f"Reindexing {len(entity_types)} entity types",
        "entity_types": entity_types,
        "job_ids": job_ids,
    }


# ─── Providers ───

@router.get("/providers", dependencies=[Depends(require_permission("search:read"))])
async def list_providers(
    current_user: dict = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> list[ProviderResponse]:
    """List all active search providers."""
    registry = get_search_registry()
    providers = registry.get_all()
    return [
        ProviderResponse(
            entity_type=p.entity_type,
            plugin_name="unified_search",
            is_active=True,
        )
        for p in providers
    ]


@router.post("/providers/{entity_type}/toggle", dependencies=[Depends(require_permission("search:admin"))])
async def toggle_provider(
    entity_type: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> dict:
    """Toggle a search provider on/off."""
    registry = get_search_registry()
    provider = registry.get(entity_type)
    if provider is None:
        raise HTTPException(status_code=404, detail=f"Provider not found: {entity_type}")

    # Toggle in DB
    tenant_id = uuid.UUID(current_user["tenant_id"])
    result = await db.execute(
        text(
            """
            SELECT is_active FROM unified_search_providers
            WHERE tenant_id = :tid AND entity_type = :et
            """
        ),
        {"tid": tenant_id, "et": entity_type},
    )
    row = result.mappings().first()

    if row:
        new_status = not row["is_active"]
        await db.execute(
            text(
                """
                UPDATE unified_search_providers SET is_active = :active
                WHERE tenant_id = :tid AND entity_type = :et
                """
            ),
            {"active": new_status, "tid": tenant_id, "et": entity_type},
        )
    else:
        new_status = False
        await db.execute(
            text(
                """
                INSERT INTO unified_search_providers (tenant_id, entity_type, plugin_name, is_active)
                VALUES (:tid, :et, 'unified_search', false)
                """
            ),
            {"tid": tenant_id, "et": entity_type},
        )

    await db.commit()

    if not new_status:
        registry.unregister(entity_type)
    else:
        # Re-register by clearing and re-running auto_register
        from app.plugins.builtins.unified_search.provider_registry import auto_register_providers
        await auto_register_providers(db)

    return {
        "entity_type": entity_type,
        "is_active": new_status,
    }


# ─── Stats ───

@router.get("/stats", dependencies=[Depends(require_permission("search:read"))])
async def search_stats(
    current_user: dict = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> dict:
    """Get search index statistics."""
    tenant_id = uuid.UUID(current_user["tenant_id"])

    stats: dict = {}
    tables = {
        "contacts": "embedding",
        "contacts": "embedding",
        "mails": "embedding",
        "files": "embedding",
        "calendar_entries": "embedding",
    }

    for table, col in tables.items():
        try:
            total_result = await db.execute(
                text(f"SELECT count(*) AS cnt FROM {table} WHERE tenant_id = :tid AND deleted_at IS NULL"),
                {"tid": tenant_id},
            )
            total = total_result.scalar() or 0

            indexed_result = await db.execute(
                text(f"SELECT count(*) AS cnt FROM {table} WHERE tenant_id = :tid AND deleted_at IS NULL AND {col} IS NOT NULL"),
                {"tid": tenant_id},
            )
            indexed = indexed_result.scalar() or 0

            stats[table] = {
                "total": total,
                "indexed": indexed,
                "pending": total - indexed,
            }
        except Exception:
            logger.exception("Stats query failed for %s", table)
            stats[table] = {"total": 0, "indexed": 0, "pending": 0}

    # Last index log entries
    try:
        log_result = await db.execute(
            text(
                """
                SELECT entity_type, action, status, created_at
                FROM unified_search_index_log
                WHERE tenant_id = :tid
                ORDER BY created_at DESC
                LIMIT 10
                """
            ),
            {"tid": tenant_id},
        )
        recent_logs = [dict(r) for r in log_result.mappings().all()]
    except Exception:
        recent_logs = []

    stats["recent_logs"] = recent_logs
    return stats
