"""DMS preview — API views.

Endpoints (all nested under a document version)
-----------------------------------------------
GET    /dms/documents/{doc_id}/versions/{vid}/previews/
    List all preview records for the version.

POST   /dms/documents/{doc_id}/versions/{vid}/previews/
    Request a new preview rendition (creates a PENDING record).

GET    /dms/documents/{doc_id}/versions/{vid}/previews/{preview_id}/
    Retrieve a specific preview record.

POST   /dms/documents/{doc_id}/versions/{vid}/previews/{preview_id}/processing/
    Converter callback: mark the preview as PROCESSING.

POST   /dms/documents/{doc_id}/versions/{vid}/previews/{preview_id}/complete/
    Converter callback: mark the preview as READY and attach the result file.

POST   /dms/documents/{doc_id}/versions/{vid}/previews/{preview_id}/fail/
    Converter callback: mark the preview as FAILED.

POST   /dms/documents/{doc_id}/versions/{vid}/previews/{preview_id}/unavailable/
    Mark the preview as UNAVAILABLE (format not supported).

DELETE /dms/documents/{doc_id}/versions/{vid}/previews/{preview_id}/
    Soft-delete a specific preview record.

POST   /dms/documents/{doc_id}/versions/{vid}/previews/invalidate/
    Soft-delete ALL previews for this version (e.g. after rollback).
"""

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.documents import queries as doc_queries
from simorgh.apps.dms.preview import queries, services
from simorgh.apps.dms.preview.permissions import PERM_PREVIEW_MANAGE, PERM_PREVIEW_VIEW
from simorgh.apps.dms.preview.serializers import (
    PreviewCompleteInputSerializer,
    PreviewFailInputSerializer,
    PreviewMarkProcessingInputSerializer,
    PreviewRecordSerializer,
    PreviewRequestInputSerializer,
)
from simorgh.apps.dms.preview.services import PreviewError


# ---------------------------------------------------------------------------
# Shared helpers (same pattern as documents/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}")


def _get_version(tenant_id: int, doc_id: str, version_id: str):
    """Resolve doc + version from URL params; return (doc, version) or raise."""
    try:
        doc = doc_queries.get_document(tenant_id, doc_id)
    except AssetNotFound as exc:
        raise exc
    try:
        version = doc_queries.get_version(doc, version_id)
    except AssetNotFound as exc:
        raise exc
    return doc, version


# ---------------------------------------------------------------------------
# Preview list / create
# ---------------------------------------------------------------------------

@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def preview_list(request: Request, doc_id: str, version_id: str) -> Response:
    tenant = _tenant(request)
    try:
        _doc, version = _get_version(tenant.pk, doc_id, version_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        _require_perm(request, PERM_PREVIEW_VIEW)
        qs = queries.list_previews(version)
        return Response(PreviewRecordSerializer(qs, many=True).data)

    # POST — request a new preview
    _require_perm(request, PERM_PREVIEW_MANAGE)
    ser = PreviewRequestInputSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    d = ser.validated_data
    try:
        record = services.request_preview(
            document_version=version,
            preview_format=d["preview_format"],
            page_number=d.get("page_number"),
            width=d.get("width"),
            height=d.get("height"),
            provider=d.get("provider", ""),
            is_primary=d.get("is_primary", False),
            tenant_id=tenant.pk,
            organization_node_id=_org_node_id(request),
        )
    except PreviewError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
    return Response(PreviewRecordSerializer(record).data, status=status.HTTP_201_CREATED)


# ---------------------------------------------------------------------------
# Preview detail / delete
# ---------------------------------------------------------------------------

@api_view(["GET", "DELETE"])
@permission_classes([IsAuthenticated])
def preview_detail(request: Request, doc_id: str, version_id: str, preview_id: str) -> Response:
    tenant = _tenant(request)
    try:
        _doc, version = _get_version(tenant.pk, doc_id, version_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)
    try:
        record = queries.get_preview(version, preview_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        _require_perm(request, PERM_PREVIEW_VIEW)
        return Response(PreviewRecordSerializer(record).data)

    # DELETE
    _require_perm(request, PERM_PREVIEW_MANAGE)
    record.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)


# ---------------------------------------------------------------------------
# Converter callbacks
# ---------------------------------------------------------------------------

@api_view(["POST"])
@permission_classes([IsAuthenticated])
def preview_mark_processing(
    request: Request, doc_id: str, version_id: str, preview_id: str
) -> Response:
    """Converter callback: acknowledge the job (PENDING → PROCESSING)."""
    tenant = _tenant(request)
    try:
        _doc, version = _get_version(tenant.pk, doc_id, version_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)
    try:
        record = queries.get_preview(version, preview_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    _require_perm(request, PERM_PREVIEW_MANAGE)
    ser = PreviewMarkProcessingInputSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    try:
        record = services.mark_processing(
            record, provider_job_id=ser.validated_data.get("provider_job_id", "")
        )
    except PreviewError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
    return Response(PreviewRecordSerializer(record).data)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def preview_complete(
    request: Request, doc_id: str, version_id: str, preview_id: str
) -> Response:
    """Converter callback: deliver the rendition file (→ READY)."""
    tenant = _tenant(request)
    try:
        _doc, version = _get_version(tenant.pk, doc_id, version_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)
    try:
        record = queries.get_preview(version, preview_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    _require_perm(request, PERM_PREVIEW_MANAGE)
    ser = PreviewCompleteInputSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    d = ser.validated_data

    # Resolve the file asset produced by the converter.
    from simorgh.apps.storage.models import FileMetadata
    import uuid as _uuid
    try:
        file_asset = FileMetadata.objects.get(
            public_id=_uuid.UUID(str(d["file_asset_id"])),
            tenant_id=tenant.pk,
            is_deleted=False,
        )
    except FileMetadata.DoesNotExist:
        return Response({"detail": "File asset not found."}, status=status.HTTP_400_BAD_REQUEST)

    try:
        record = services.complete_preview(
            record,
            file_asset=file_asset,
            width=d.get("width"),
            height=d.get("height"),
        )
    except PreviewError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
    return Response(PreviewRecordSerializer(record).data)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def preview_fail(
    request: Request, doc_id: str, version_id: str, preview_id: str
) -> Response:
    """Converter callback: report a failure (→ FAILED)."""
    tenant = _tenant(request)
    try:
        _doc, version = _get_version(tenant.pk, doc_id, version_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)
    try:
        record = queries.get_preview(version, preview_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    _require_perm(request, PERM_PREVIEW_MANAGE)
    ser = PreviewFailInputSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    try:
        record = services.fail_preview(record, reason=ser.validated_data.get("reason", ""))
    except PreviewError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
    return Response(PreviewRecordSerializer(record).data)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def preview_unavailable(
    request: Request, doc_id: str, version_id: str, preview_id: str
) -> Response:
    """Mark a preview as UNAVAILABLE (format not supported for this file)."""
    tenant = _tenant(request)
    try:
        _doc, version = _get_version(tenant.pk, doc_id, version_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)
    try:
        record = queries.get_preview(version, preview_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    _require_perm(request, PERM_PREVIEW_MANAGE)
    ser = PreviewFailInputSerializer(data=request.data)  # same shape: optional reason
    ser.is_valid(raise_exception=True)
    try:
        record = services.mark_unavailable(record, reason=ser.validated_data.get("reason", ""))
    except PreviewError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
    return Response(PreviewRecordSerializer(record).data)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def preview_invalidate(request: Request, doc_id: str, version_id: str) -> Response:
    """Soft-delete ALL previews for a version (bulk invalidation)."""
    tenant = _tenant(request)
    try:
        _doc, version = _get_version(tenant.pk, doc_id, version_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    _require_perm(request, PERM_PREVIEW_MANAGE)
    count = services.invalidate_previews(version)
    return Response({"invalidated": count})
