"""DMS search — API views.

Endpoints
---------
Search:
  GET  /dms/search/                           full-text + filtered document search

Saved searches:
  GET  /dms/search/saved/                     list own + shared saved searches
  POST /dms/search/saved/                     create a saved search
  GET  /dms/search/saved/<search_id>/         retrieve a saved search
  PUT  /dms/search/saved/<search_id>/         update (owner only)
  DELETE /dms/search/saved/<search_id>/       delete (owner only)
  POST /dms/search/saved/<search_id>/execute/ run a saved search
"""

from __future__ import annotations

from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response

from simorgh.apps.dms.common.exceptions import AssetNotFound
from simorgh.apps.dms.search import queries, services
from simorgh.apps.dms.search.iam_permissions import PERM_SEARCH_QUERY, PERM_SEARCH_SAVE
from simorgh.apps.dms.search.serializers import (
    DocumentSearchIndexSerializer,
    SavedSearchSerializer,
    SavedSearchWriteSerializer,
    SearchParamsSerializer,
)
from simorgh.apps.dms.search.services import SearchParams


# ---------------------------------------------------------------------------
# Shared helpers (same pattern as versioning/views.py)
# ---------------------------------------------------------------------------

def _tenant(request: Request):
    tenant = getattr(request, "tenant", None)
    if tenant is None:
        from rest_framework.exceptions import PermissionDenied
        raise PermissionDenied("Tenant required.")
    return tenant


def _org_node_id(request: Request) -> int:
    from simorgh.apps.memberships.models import Membership, MembershipStatus

    t = _tenant(request)
    m = (
        Membership.objects.filter(
            tenant=t, users=request.user, status=MembershipStatus.ACTIVE
        )
        .order_by("id")
        .first()
    )
    if m is not None:
        return m.organization_node_id
    if getattr(request.user, "is_superuser", False):
        from simorgh.apps.organizations.models import OrganizationNode

        root = OrganizationNode.objects.filter(tenant=t).order_by("id").first()
        if root is not None:
            return root.pk
    from rest_framework.exceptions import PermissionDenied
    raise PermissionDenied("No active membership for this tenant.")


def _require_perm(request: Request, codename: str) -> None:
    from rest_framework.exceptions import PermissionDenied
    from simorgh.core.context import current_request_context

    ctx = current_request_context()
    if ctx.is_superuser:
        return
    if codename not in (ctx.permissions or set()):
        raise PermissionDenied(f"Permission required: {codename}")


# ---------------------------------------------------------------------------
# Search
# ---------------------------------------------------------------------------

@api_view(["GET"])
@permission_classes([IsAuthenticated])
def document_search(request: Request) -> Response:
    """GET /dms/search/ — full-text + filtered document search.

    All search parameters are passed as query-string fields.  See
    ``SearchParamsSerializer`` for the full list of supported params.

    Returns a list of ``DocumentSearchIndex`` entries ordered by the
    requested sort field.  Pagination is the caller's responsibility —
    a ``?page=`` / ``?page_size=`` parameter pair can be added by a
    DRF pagination class without changing this view.
    """
    _require_perm(request, PERM_SEARCH_QUERY)
    tenant = _tenant(request)

    from django.core.cache import cache
    from simorgh.apps.dms.cache import search_results_key, SEARCH_TTL

    cache_key = search_results_key(tenant.pk, dict(request.query_params))
    cached = cache.get(cache_key)
    if cached is not None:
        return Response(cached)

    ser = SearchParamsSerializer(data=request.query_params)
    ser.is_valid(raise_exception=True)
    d = ser.validated_data

    params = SearchParams(
        q=d.get("q") or None,
        repository_id=str(d["repository_id"]) if d.get("repository_id") else None,
        folder_id=str(d["folder_id"]) if d.get("folder_id") else None,
        document_type=d.get("document_type") or None,
        status=d.get("status") or None,
        content_type=d.get("content_type") or None,
        indexed_after=d.get("indexed_after"),
        indexed_before=d.get("indexed_before"),
        tags=d.get("tags") or [],
        sort=d.get("sort") or "indexed_at",
        order=d.get("order") or "desc",
    )

    results = services.search_documents(tenant.pk, params)
    data = DocumentSearchIndexSerializer(results, many=True).data

    from django.core.cache import cache
    from simorgh.apps.dms.cache import search_results_key, SEARCH_TTL

    cache_key = search_results_key(tenant.pk, dict(request.query_params))
    cache.set(cache_key, data, timeout=SEARCH_TTL)
    return Response(data)


# ---------------------------------------------------------------------------
# Saved searches — list / create
# ---------------------------------------------------------------------------

@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def saved_search_list(request: Request) -> Response:
    """
    GET  /dms/search/saved/  — list own + shared saved searches
    POST /dms/search/saved/  — create a new saved search
    """
    _require_perm(request, PERM_SEARCH_SAVE)
    tenant = _tenant(request)

    if request.method == "GET":
        qs = queries.list_saved_searches(tenant.pk, request.user)
        return Response(SavedSearchSerializer(qs, many=True).data)

    # POST — create
    ser = SavedSearchWriteSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    d = ser.validated_data

    if not d.get("name"):
        return Response(
            {"error": "name is required when creating a saved search."},
            status=status.HTTP_400_BAD_REQUEST,
        )

    org_node_id = _org_node_id(request)
    ss = services.create_saved_search(
        tenant_id=tenant.pk,
        organization_node_id=org_node_id,
        owner=request.user,
        name=d["name"],
        description=d.get("description", ""),
        query_params=d.get("query_params", {}),
        is_shared=d.get("is_shared", False),
    )
    return Response(SavedSearchSerializer(ss).data, status=status.HTTP_201_CREATED)


# ---------------------------------------------------------------------------
# Saved searches — detail / update / delete
# ---------------------------------------------------------------------------

@api_view(["GET", "PUT", "DELETE"])
@permission_classes([IsAuthenticated])
def saved_search_detail(request: Request, search_id: str) -> Response:
    """
    GET    /dms/search/saved/<search_id>/  — retrieve (owner or shared)
    PUT    /dms/search/saved/<search_id>/  — update (owner only)
    DELETE /dms/search/saved/<search_id>/  — delete (owner only)
    """
    _require_perm(request, PERM_SEARCH_SAVE)
    tenant = _tenant(request)

    try:
        ss = queries.get_saved_search(tenant.pk, search_id, user=request.user)
    except AssetNotFound as exc:
        return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        return Response(SavedSearchSerializer(ss).data)

    # Write operations — owner or superuser only
    is_owner = ss.owner_id == request.user.pk
    is_super = getattr(request.user, "is_superuser", False)
    if not is_owner and not is_super:
        from rest_framework.exceptions import PermissionDenied
        raise PermissionDenied("Only the owner can modify this saved search.")

    if request.method == "DELETE":
        services.delete_saved_search(ss)
        return Response(status=status.HTTP_204_NO_CONTENT)

    # PUT — partial update
    ser = SavedSearchWriteSerializer(data=request.data, partial=True)
    ser.is_valid(raise_exception=True)
    d = ser.validated_data

    ss = services.update_saved_search(
        ss,
        name=d.get("name"),
        description=d.get("description"),
        query_params=d.get("query_params"),
        is_shared=d.get("is_shared"),
    )
    return Response(SavedSearchSerializer(ss).data)


# ---------------------------------------------------------------------------
# Saved searches — execute
# ---------------------------------------------------------------------------

@api_view(["POST"])
@permission_classes([IsAuthenticated])
def saved_search_execute(request: Request, search_id: str) -> Response:
    """POST /dms/search/saved/<search_id>/execute/ — run a saved search.

    Returns the same shape as ``document_search`` (list of index entries).
    Increments the saved search's ``use_count`` and updates ``last_used_at``.
    """
    _require_perm(request, PERM_SEARCH_QUERY)
    tenant = _tenant(request)

    try:
        ss = queries.get_saved_search(tenant.pk, search_id, user=request.user)
    except AssetNotFound as exc:
        return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    results = services.execute_saved_search(ss, tenant.pk)
    return Response(DocumentSearchIndexSerializer(results, many=True).data)
