"""DMS collaboration — API views.

Endpoints
---------
Comments:
  GET  /dms/documents/{doc_id}/comments/                     list top-level comments
  POST /dms/documents/{doc_id}/comments/                     create a comment/reply

  GET    /dms/documents/{doc_id}/comments/{comment_id}/      retrieve comment
  PUT    /dms/documents/{doc_id}/comments/{comment_id}/      update comment body (author only)
  DELETE /dms/documents/{doc_id}/comments/{comment_id}/      delete comment

  POST /dms/documents/{doc_id}/comments/{comment_id}/resolve/   toggle resolved flag

Annotations:
  GET  /dms/documents/{doc_id}/versions/{ver_id}/annotations/             list annotations
  POST /dms/documents/{doc_id}/versions/{ver_id}/annotations/             create annotation

  GET    /dms/documents/{doc_id}/versions/{ver_id}/annotations/{ann_id}/  retrieve
  PUT    /dms/documents/{doc_id}/versions/{ver_id}/annotations/{ann_id}/  update
  DELETE /dms/documents/{doc_id}/versions/{ver_id}/annotations/{ann_id}/  delete
"""

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.collaboration import queries, services
from simorgh.apps.dms.collaboration.iam_permissions import (
    PERM_ANNOTATION_CREATE,
    PERM_ANNOTATION_DELETE_ANY,
    PERM_ANNOTATION_DELETE_OWN,
    PERM_ANNOTATION_VIEW,
    PERM_COMMENT_CREATE,
    PERM_COMMENT_DELETE_ANY,
    PERM_COMMENT_DELETE_OWN,
    PERM_COMMENT_RESOLVE,
    PERM_COMMENT_VIEW,
)
from simorgh.apps.dms.collaboration.serializers import (
    AnnotationSerializer,
    AnnotationUpdateSerializer,
    AnnotationWriteSerializer,
    CommentSerializer,
    CommentUpdateSerializer,
    CommentWriteSerializer,
)
from simorgh.apps.dms.collaboration.services import (
    AnnotationError,
    CollaborationError,
    CommentError,
)
from simorgh.apps.dms.common.exceptions import AssetNotFound


# ---------------------------------------------------------------------------
# Shared helpers
# ---------------------------------------------------------------------------

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)


def _get_version(request: Request, ver_id: str):
    from simorgh.apps.dms.documents.queries import get_version

    tenant = _tenant(request)
    return get_version(tenant.pk, ver_id)


def _is_owner(request: Request, obj) -> bool:
    """Return True if the request user authored the given object."""
    author = getattr(obj, "author", None)
    if author is None:
        return False
    return author.pk == request.user.pk


def _has_perm(request: Request, codename: str) -> bool:
    """Return True if the user has the given permission (or is superuser)."""
    from simorgh.core.context import current_request_context

    ctx = current_request_context()
    if ctx.is_superuser:
        return True
    return codename in (ctx.permissions or set())


# ---------------------------------------------------------------------------
# Comment list / create
# ---------------------------------------------------------------------------

@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def document_comments(request: Request, doc_id: str) -> Response:
    """
    GET  — list comments for the document.
    POST — create a new comment or reply.
    """
    _require_perm(request, PERM_COMMENT_VIEW)

    try:
        doc = _get_document(request, doc_id)
    except AssetNotFound as exc:
        return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        qs = queries.list_comments_for_document(doc, top_level_only=True)
        return Response(CommentSerializer(qs, many=True).data)

    # POST
    _require_perm(request, PERM_COMMENT_CREATE)
    ser = CommentWriteSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)

    tenant = _tenant(request)
    org_node_id = _org_node_id(request)
    data = ser.validated_data

    # Resolve optional version
    version = None
    if data.get("version_id"):
        try:
            version = _get_version(request, str(data["version_id"]))
        except AssetNotFound as exc:
            return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    # Resolve optional parent
    parent = None
    if data.get("parent_id"):
        try:
            parent = queries.get_comment(tenant.pk, str(data["parent_id"]))
        except AssetNotFound as exc:
            return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    try:
        comment = services.create_comment(
            tenant_id=tenant.pk,
            organization_node_id=org_node_id,
            document=doc,
            author=request.user,
            body=data["body"],
            version=version,
            parent=parent,
        )
    except (CommentError, CollaborationError) as exc:
        return Response({"error": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    if data.get("mention_user_ids"):
        services.sync_mentions(comment, data["mention_user_ids"])

    return Response(CommentSerializer(comment).data, status=status.HTTP_201_CREATED)


# ---------------------------------------------------------------------------
# Comment detail (retrieve / update / delete)
# ---------------------------------------------------------------------------

@api_view(["GET", "PUT", "DELETE"])
@permission_classes([IsAuthenticated])
def comment_detail(request: Request, doc_id: str, comment_id: str) -> Response:
    """
    GET    — retrieve a comment.
    PUT    — update body (author only).
    DELETE — soft-delete comment.
    """
    _require_perm(request, PERM_COMMENT_VIEW)

    try:
        _get_document(request, doc_id)
        tenant = _tenant(request)
        comment = queries.get_comment(tenant.pk, comment_id)
    except AssetNotFound as exc:
        return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        return Response(CommentSerializer(comment).data)

    if request.method == "PUT":
        # Only the author may edit
        if not _is_owner(request, comment) and not _has_perm(request, PERM_COMMENT_DELETE_ANY):
            return Response(
                {"error": "Only the author may edit a comment."},
                status=status.HTTP_403_FORBIDDEN,
            )
        ser = CommentUpdateSerializer(data=request.data)
        if not ser.is_valid():
            return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)
        data = ser.validated_data
        try:
            comment = services.update_comment(comment, body=data["body"])
        except CommentError as exc:
            return Response({"error": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        if "mention_user_ids" in data:
            services.sync_mentions(comment, data["mention_user_ids"])
        return Response(CommentSerializer(comment).data)

    # DELETE
    can_delete_own = _is_owner(request, comment) and _has_perm(request, PERM_COMMENT_DELETE_OWN)
    can_delete_any = _has_perm(request, PERM_COMMENT_DELETE_ANY)
    if not can_delete_own and not can_delete_any:
        return Response(
            {"error": "Permission required to delete this comment."},
            status=status.HTTP_403_FORBIDDEN,
        )
    services.delete_comment(comment)
    return Response(status=status.HTTP_204_NO_CONTENT)


# ---------------------------------------------------------------------------
# Comment resolve toggle
# ---------------------------------------------------------------------------

@api_view(["POST"])
@permission_classes([IsAuthenticated])
def comment_resolve(request: Request, doc_id: str, comment_id: str) -> Response:
    """POST — toggle the resolved state of a comment thread."""
    _require_perm(request, PERM_COMMENT_RESOLVE)

    try:
        _get_document(request, doc_id)
        tenant = _tenant(request)
        comment = queries.get_comment(tenant.pk, comment_id)
    except AssetNotFound as exc:
        return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    try:
        if comment.is_resolved:
            comment = services.unresolve_comment(comment, resolving_user=request.user)
        else:
            comment = services.resolve_comment(comment, resolving_user=request.user)
    except CommentError as exc:
        return Response({"error": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(CommentSerializer(comment).data)


# ---------------------------------------------------------------------------
# Annotation list / create
# ---------------------------------------------------------------------------

@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def version_annotations(request: Request, doc_id: str, ver_id: str) -> Response:
    """
    GET  — list annotations for a document version.
    POST — create an annotation.
    """
    _require_perm(request, PERM_ANNOTATION_VIEW)

    try:
        doc = _get_document(request, doc_id)
        version = _get_version(request, ver_id)
    except AssetNotFound as exc:
        return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        page_number = request.query_params.get("page")
        page_int: int | None = None
        if page_number is not None:
            try:
                page_int = int(page_number)
            except ValueError:
                return Response(
                    {"error": "page must be an integer."},
                    status=status.HTTP_400_BAD_REQUEST,
                )
        qs = queries.list_annotations_for_version(version, page_number=page_int)
        return Response(AnnotationSerializer(qs, many=True).data)

    # POST
    _require_perm(request, PERM_ANNOTATION_CREATE)
    ser = AnnotationWriteSerializer(data=request.data)
    if not ser.is_valid():
        return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)

    data = ser.validated_data
    tenant = _tenant(request)
    org_node_id = _org_node_id(request)

    # Resolve optional linked comment
    linked_comment = None
    if data.get("linked_comment_id"):
        try:
            linked_comment = queries.get_comment(tenant.pk, str(data["linked_comment_id"]))
        except AssetNotFound as exc:
            return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    try:
        annotation = services.create_annotation(
            tenant_id=tenant.pk,
            organization_node_id=org_node_id,
            document=doc,
            version=version,
            author=request.user,
            annotation_type=data["annotation_type"],
            body=data.get("body", ""),
            page_number=data.get("page_number"),
            position_data=data.get("position_data", {}),
            color=data.get("color", "#FFFF00"),
            linked_comment=linked_comment,
        )
    except (AnnotationError, CollaborationError) as exc:
        return Response({"error": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(AnnotationSerializer(annotation).data, status=status.HTTP_201_CREATED)


# ---------------------------------------------------------------------------
# Annotation detail (retrieve / update / delete)
# ---------------------------------------------------------------------------

@api_view(["GET", "PUT", "DELETE"])
@permission_classes([IsAuthenticated])
def annotation_detail(
    request: Request, doc_id: str, ver_id: str, ann_id: str
) -> Response:
    """
    GET    — retrieve an annotation.
    PUT    — update mutable fields (author only).
    DELETE — soft-delete annotation.
    """
    _require_perm(request, PERM_ANNOTATION_VIEW)

    try:
        _get_document(request, doc_id)
        _get_version(request, ver_id)
        tenant = _tenant(request)
        annotation = queries.get_annotation(tenant.pk, ann_id)
    except AssetNotFound as exc:
        return Response({"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        return Response(AnnotationSerializer(annotation).data)

    if request.method == "PUT":
        if not _is_owner(request, annotation) and not _has_perm(request, PERM_ANNOTATION_DELETE_ANY):
            return Response(
                {"error": "Only the author may edit an annotation."},
                status=status.HTTP_403_FORBIDDEN,
            )
        ser = AnnotationUpdateSerializer(data=request.data)
        if not ser.is_valid():
            return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)
        data = ser.validated_data
        kwargs = {}
        if "body" in data:
            kwargs["body"] = data["body"]
        if "color" in data:
            kwargs["color"] = data["color"]
        if "position_data" in data:
            kwargs["position_data"] = data["position_data"]
        if "page_number" in data:
            kwargs["page_number"] = data["page_number"]
        annotation = services.update_annotation(annotation, **kwargs)
        return Response(AnnotationSerializer(annotation).data)

    # DELETE
    can_delete_own = _is_owner(request, annotation) and _has_perm(request, PERM_ANNOTATION_DELETE_OWN)
    can_delete_any = _has_perm(request, PERM_ANNOTATION_DELETE_ANY)
    if not can_delete_own and not can_delete_any:
        return Response(
            {"error": "Permission required to delete this annotation."},
            status=status.HTTP_403_FORBIDDEN,
        )
    services.delete_annotation(annotation)
    return Response(status=status.HTTP_204_NO_CONTENT)
