"""DMS records — API views.

Endpoints
---------
Retention Policies:
  GET  /dms/retention-policies/                      list policies
  POST /dms/retention-policies/                      create policy

  GET    /dms/retention-policies/{policy_id}/        retrieve
  PUT    /dms/retention-policies/{policy_id}/        update
  DELETE /dms/retention-policies/{policy_id}/        soft-delete

Legal Holds:
  GET  /dms/documents/{doc_id}/legal-holds/           list holds
  POST /dms/documents/{doc_id}/legal-holds/           place hold

  GET  /dms/documents/{doc_id}/legal-holds/{hold_id}/          retrieve
  POST /dms/documents/{doc_id}/legal-holds/{hold_id}/release/  release hold

Archive Records:
  GET  /dms/documents/{doc_id}/archive-records/                list records
  POST /dms/documents/{doc_id}/archive-records/                create record

  GET  /dms/documents/{doc_id}/archive-records/{record_id}/    retrieve (read-only)
"""

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.records import queries, services
from simorgh.apps.dms.records.iam_permissions import (
    PERM_ARCHIVE_CREATE,
    PERM_ARCHIVE_VIEW,
    PERM_LEGALHOLD_PLACE,
    PERM_LEGALHOLD_RELEASE,
    PERM_LEGALHOLD_VIEW,
    PERM_RETENTION_MANAGE,
    PERM_RETENTION_VIEW,
)
from simorgh.apps.dms.records.serializers import (
    ArchiveRecordSerializer,
    ArchiveRecordWriteSerializer,
    LegalHoldSerializer,
    LegalHoldWriteSerializer,
    RetentionPolicySerializer,
    RetentionPolicyUpdateSerializer,
    RetentionPolicyWriteSerializer,
)
from simorgh.apps.dms.records.services import ArchiveError, LegalHoldError, RetentionPolicyError
from simorgh.apps.dms.common.exceptions import AssetNotFound


# ---------------------------------------------------------------------------
# Shared helpers (mirrors the collaboration views pattern)
# ---------------------------------------------------------------------------

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_document(request: Request, doc_id: str):
    from simorgh.apps.dms.documents.queries import get_document

    tenant = _tenant(request)
    return get_document(tenant.pk, doc_id)


# ---------------------------------------------------------------------------
# Retention Policy list / create
# ---------------------------------------------------------------------------

@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def retention_policy_list(request: Request) -> Response:
    """
    GET  — list retention policies.
    POST — create a new retention policy.
    """
    _require_perm(request, PERM_RETENTION_VIEW)
    tenant = _tenant(request)

    if request.method == "GET":
        active_only = request.query_params.get("active_only", "true").lower() != "false"
        qs = queries.list_retention_policies(tenant.pk, active_only=active_only)
        return Response(RetentionPolicySerializer(qs, many=True).data)

    # POST
    _require_perm(request, PERM_RETENTION_MANAGE)
    ser = RetentionPolicyWriteSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    d = ser.validated_data

    try:
        policy = services.create_retention_policy(
            tenant_id=tenant.pk,
            organization_node_id=_org_node_id(request),
            name=d["name"],
            description=d.get("description", ""),
            retention_period_days=d.get("retention_period_days", 0),
            action_on_expiry=d.get("action_on_expiry", "review"),
            is_active=d.get("is_active", True),
        )
    except RetentionPolicyError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(
        RetentionPolicySerializer(policy).data, status=status.HTTP_201_CREATED
    )


# ---------------------------------------------------------------------------
# Retention Policy detail
# ---------------------------------------------------------------------------

@api_view(["GET", "PUT", "DELETE"])
@permission_classes([IsAuthenticated])
def retention_policy_detail(request: Request, policy_id: str) -> Response:
    """
    GET    — retrieve.
    PUT    — update (partial).
    DELETE — soft-delete.
    """
    _require_perm(request, PERM_RETENTION_VIEW)
    tenant = _tenant(request)

    try:
        policy = queries.get_retention_policy(tenant.pk, policy_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        return Response(RetentionPolicySerializer(policy).data)

    if request.method == "DELETE":
        _require_perm(request, PERM_RETENTION_MANAGE)
        services.delete_retention_policy(policy)
        return Response(status=status.HTTP_204_NO_CONTENT)

    # PUT
    _require_perm(request, PERM_RETENTION_MANAGE)
    ser = RetentionPolicyUpdateSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    d = ser.validated_data

    try:
        policy = services.update_retention_policy(
            policy,
            **{k: v for k, v in d.items()},
        )
    except RetentionPolicyError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(RetentionPolicySerializer(policy).data)


# ---------------------------------------------------------------------------
# Legal Hold list / place
# ---------------------------------------------------------------------------

@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def document_legal_holds(request: Request, doc_id: str) -> Response:
    """
    GET  — list legal holds for a document.
    POST — place a new legal hold.
    """
    _require_perm(request, PERM_LEGALHOLD_VIEW)

    try:
        document = _get_document(request, doc_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        active_only = request.query_params.get("active_only", "false").lower() == "true"
        qs = queries.list_legal_holds_for_document(document, active_only=active_only)
        return Response(LegalHoldSerializer(qs, many=True).data)

    # POST — place
    _require_perm(request, PERM_LEGALHOLD_PLACE)
    ser = LegalHoldWriteSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    d = ser.validated_data
    tenant = _tenant(request)

    try:
        hold = services.place_legal_hold(
            tenant_id=tenant.pk,
            organization_node_id=_org_node_id(request),
            document=document,
            name=d["name"],
            placed_by=request.user,
            notes=d.get("notes", ""),
        )
    except LegalHoldError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(LegalHoldSerializer(hold).data, status=status.HTTP_201_CREATED)


# ---------------------------------------------------------------------------
# Legal Hold detail
# ---------------------------------------------------------------------------

@api_view(["GET"])
@permission_classes([IsAuthenticated])
def legal_hold_detail(request: Request, doc_id: str, hold_id: str) -> Response:
    """GET — retrieve a legal hold."""
    _require_perm(request, PERM_LEGALHOLD_VIEW)
    tenant = _tenant(request)

    try:
        hold = queries.get_legal_hold(tenant.pk, hold_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    # Verify the hold belongs to the given document
    try:
        document = _get_document(request, doc_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if hold.document_id != document.pk:
        return Response(
            {"detail": "Legal hold does not belong to this document."},
            status=status.HTTP_404_NOT_FOUND,
        )

    return Response(LegalHoldSerializer(hold).data)


# ---------------------------------------------------------------------------
# Legal Hold release
# ---------------------------------------------------------------------------

@api_view(["POST"])
@permission_classes([IsAuthenticated])
def legal_hold_release(request: Request, doc_id: str, hold_id: str) -> Response:
    """POST — release an active legal hold."""
    _require_perm(request, PERM_LEGALHOLD_RELEASE)
    tenant = _tenant(request)

    try:
        hold = queries.get_legal_hold(tenant.pk, hold_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    try:
        document = _get_document(request, doc_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if hold.document_id != document.pk:
        return Response(
            {"detail": "Legal hold does not belong to this document."},
            status=status.HTTP_404_NOT_FOUND,
        )

    try:
        hold = services.release_legal_hold(hold, released_by=request.user)
    except LegalHoldError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(LegalHoldSerializer(hold).data)


# ---------------------------------------------------------------------------
# Archive Record list / create
# ---------------------------------------------------------------------------

@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def document_archive_records(request: Request, doc_id: str) -> Response:
    """
    GET  — list archive records for a document.
    POST — create a new archive record.
    """
    _require_perm(request, PERM_ARCHIVE_VIEW)

    try:
        document = _get_document(request, doc_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        qs = queries.list_archive_records_for_document(document)
        return Response(ArchiveRecordSerializer(qs, many=True).data)

    # POST
    _require_perm(request, PERM_ARCHIVE_CREATE)
    ser = ArchiveRecordWriteSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    d = ser.validated_data
    tenant = _tenant(request)

    # Resolve optional version
    version = None
    version_id = d.get("version_id")
    if version_id:
        from simorgh.apps.dms.documents.queries import get_version
        try:
            version = get_version(tenant.pk, str(version_id))
        except AssetNotFound as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    # Resolve optional retention policy
    retention_policy = None
    policy_id = d.get("retention_policy_id")
    if policy_id:
        try:
            retention_policy = queries.get_retention_policy(tenant.pk, str(policy_id))
        except AssetNotFound as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    try:
        record = services.create_archive_record(
            tenant_id=tenant.pk,
            organization_node_id=_org_node_id(request),
            document=document,
            archived_by=request.user,
            reason=d.get("reason", ""),
            version=version,
            retention_policy=retention_policy,
            is_permanent=d.get("is_permanent", False),
        )
    except ArchiveError as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(ArchiveRecordSerializer(record).data, status=status.HTTP_201_CREATED)


# ---------------------------------------------------------------------------
# Archive Record detail (read-only)
# ---------------------------------------------------------------------------

@api_view(["GET"])
@permission_classes([IsAuthenticated])
def archive_record_detail(request: Request, doc_id: str, record_id: str) -> Response:
    """GET — retrieve an archive record (read-only, immutable)."""
    _require_perm(request, PERM_ARCHIVE_VIEW)
    tenant = _tenant(request)

    try:
        record = queries.get_archive_record(tenant.pk, record_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    try:
        document = _get_document(request, doc_id)
    except AssetNotFound as exc:
        return Response({"detail": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if record.document_id != document.pk:
        return Response(
            {"detail": "Archive record does not belong to this document."},
            status=status.HTTP_404_NOT_FOUND,
        )

    return Response(ArchiveRecordSerializer(record).data)
