"""Correspondence API views."""

from __future__ import annotations

from django.db.models import Q
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.correspondence import selectors as sel
from simorgh.apps.correspondence import services
from simorgh.apps.correspondence.models import (
    Category,
    Letter,
    LetterheadTemplate,
    LetterTemplate,
    NumberingTemplate,
)
from simorgh.apps.correspondence.permissions import (
    PERM_CATEGORY_ADD,
    PERM_CATEGORY_CHANGE,
    PERM_CATEGORY_DELETE,
    PERM_LETTER_ADD,
    PERM_LETTER_ARCHIVE,
    PERM_LETTER_CHANGE,
    PERM_LETTER_DELETE,
    PERM_LETTER_VIEW,
    PERM_LETTERHEAD_ADD,
    PERM_LETTERHEAD_CHANGE,
    PERM_LETTERHEAD_DELETE,
    PERM_NUMBERING_ADD,
    PERM_NUMBERING_CHANGE,
    PERM_NUMBERING_DELETE,
    PERM_TEMPLATE_ADD,
    PERM_TEMPLATE_CHANGE,
    PERM_TEMPLATE_DELETE,
)
from simorgh.apps.correspondence.serializers import (
    AttachmentAddSerializer,
    CategoryCreateSerializer,
    CategoryListSerializer,
    CategoryUpdateSerializer,
    LetterCancelSerializer,
    LetterCreateSerializer,
    LetterDetailSerializer,
    LetterheadTemplateCreateSerializer,
    LetterheadTemplateSerializer,
    LetterListSerializer,
    LetterRecipientInputSerializer,
    LetterStatusChangeSerializer,
    LetterTemplateCreateSerializer,
    LetterTemplateSerializer,
    LetterUpdateSerializer,
    NumberingTemplateCreateSerializer,
    NumberingTemplateSerializer,
    ReplyCreateSerializer,
)

# ═══════════════════════════════════════════════════════════════════════════
# 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

    tenant = _tenant(request)
    m = (
        Membership.objects.filter(
            tenant=tenant,
            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=tenant)
            .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_letter_or_404(pk: int, request: Request) -> Letter:
    tenant = _tenant(request)
    try:
        return (
            Letter.objects.select_related(
                "category", "template", "sender", "letterhead", "parent"
            )
            .prefetch_related("attachments", "recipients")
            .get(pk=pk, tenant=tenant)
        )
    except Letter.DoesNotExist:
        from rest_framework.exceptions import NotFound

        raise NotFound("Letter not found.")


# ═══════════════════════════════════════════════════════════════════════════
# Letters
# ═══════════════════════════════════════════════════════════════════════════


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def letters_list(request: Request) -> Response:
    tenant = _tenant(request)
    org_id = _org_node_id(request)

    if request.method == "GET":
        _require_perm(request, PERM_LETTER_VIEW)
        qs = sel.letters_for_tenant(
            tenant.pk,
            letter_type=request.query_params.get("letter_type"),
            status=request.query_params.get("status"),
            priority=request.query_params.get("priority"),
            security=request.query_params.get("security"),
            category_id=_int_param(request, "category_id"),
            sender_id=_int_param(request, "sender_id"),
            q=request.query_params.get("q"),
            date_from=request.query_params.get("date_from"),
            date_to=request.query_params.get("date_to"),
        )
        serializer = LetterListSerializer(qs, many=True)
        return Response(serializer.data)

    # POST
    _require_perm(request, PERM_LETTER_ADD)
    ser = LetterCreateSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    data = ser.validated_data

    letter = services.LetterService.create(
        tenant_id=tenant.pk,
        organization_node_id=org_id,
        letter_type=data["letter_type"],
        subject=data["subject"],
        body=data.get("body", ""),
        priority=data.get("priority", "normal"),
        security=data.get("security", "normal"),
        sender_id=data.get("sender_id") or request.user.pk,
        sender_external=data.get("sender_external", ""),
        sender_organization=data.get("sender_organization", ""),
        category_id=data.get("category_id"),
        template_id=data.get("template_id"),
        numbering_template_id=data.get("numbering_template_id"),
        letterhead_id=data.get("letterhead_id"),
        registration_date=data.get("registration_date"),
        letter_date=data.get("letter_date"),
        received_date=data.get("received_date"),
        due_date=data.get("due_date"),
        external_reference=data.get("external_reference", ""),
        summary=data.get("summary", ""),
        parent_id=data.get("parent_id"),
        tag_ids=data.get("tag_ids"),
        auto_number=data.get("auto_number", True),
        actor_id=request.user.pk,
        metadata=data.get("metadata"),
    )

    # Add recipients if provided
    if data.get("recipients"):
        services.LetterService.add_recipients(letter=letter, recipients=data["recipients"])

    letter = _get_letter_or_404(letter.pk, request)
    return Response(LetterDetailSerializer(letter).data, status=status.HTTP_201_CREATED)


@api_view(["GET", "PATCH", "DELETE"])
@permission_classes([IsAuthenticated])
def letter_detail(request: Request, pk: int) -> Response:
    _require_perm(request, PERM_LETTER_VIEW)
    letter = _get_letter_or_404(pk, request)

    if request.method == "GET":
        return Response(LetterDetailSerializer(letter).data)

    if request.method == "PATCH":
        _require_perm(request, PERM_LETTER_CHANGE)
        ser = LetterUpdateSerializer(data=request.data, partial=True)
        ser.is_valid(raise_exception=True)
        letter = services.LetterService.update(
            letter, actor_id=request.user.pk, **ser.validated_data
        )
        return Response(LetterDetailSerializer(letter).data)

    # DELETE
    _require_perm(request, PERM_LETTER_DELETE)
    letter.delete(actor_id=request.user.pk)
    return Response(status=status.HTTP_204_NO_CONTENT)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def letter_transition(request: Request, pk: int) -> Response:
    _require_perm(request, PERM_LETTER_CHANGE)
    letter = _get_letter_or_404(pk, request)

    ser = LetterStatusChangeSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    new_status = ser.validated_data["status"]

    letter = services.LetterService.change_status(
        letter, new_status, actor_id=request.user.pk
    )
    return Response(LetterDetailSerializer(letter).data)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def letter_send(request: Request, pk: int) -> Response:
    _require_perm(request, PERM_LETTER_CHANGE)
    letter = _get_letter_or_404(pk, request)
    letter = services.LetterService.send(letter, actor_id=request.user.pk)
    return Response(LetterDetailSerializer(letter).data)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def letter_archive(request: Request, pk: int) -> Response:
    _require_perm(request, PERM_LETTER_ARCHIVE)
    letter = _get_letter_or_404(pk, request)
    letter = services.LetterService.archive(letter, actor_id=request.user.pk)
    return Response(LetterDetailSerializer(letter).data)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def letter_cancel(request: Request, pk: int) -> Response:
    _require_perm(request, PERM_LETTER_CHANGE)
    letter = _get_letter_or_404(pk, request)

    ser = LetterCancelSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    letter = services.LetterService.cancel(
        letter,
        reason=ser.validated_data.get("reason", ""),
        actor_id=request.user.pk,
    )
    return Response(LetterDetailSerializer(letter).data)


# ── Letter reply ───────────────────────────────────────────────────────


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def letter_reply(request: Request, pk: int) -> Response:
    _require_perm(request, PERM_LETTER_ADD)
    parent_letter = _get_letter_or_404(pk, request)
    tenant = _tenant(request)
    org_id = _org_node_id(request)

    ser = ReplyCreateSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    data = ser.validated_data

    reply = services.LetterService.reply(
        parent_letter=parent_letter,
        tenant_id=tenant.pk,
        organization_node_id=org_id,
        subject=data.get("subject"),
        body=data.get("body", ""),
        sender_id=request.user.pk,
        priority=data.get("priority", "normal"),
        letter_type=data.get("letter_type", "outgoing"),
        actor_id=request.user.pk,
    )
    return Response(LetterDetailSerializer(reply).data, status=status.HTTP_201_CREATED)


# ── Letter recipients ──────────────────────────────────────────────────


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def letter_recipients(request: Request, pk: int) -> Response:
    _require_perm(request, PERM_LETTER_VIEW)
    letter = _get_letter_or_404(pk, request)

    if request.method == "GET":
        from simorgh.apps.correspondence.serializers import LetterRecipientSerializer

        recipients = sel.letter_recipients(letter.pk)
        return Response(LetterRecipientSerializer(recipients, many=True).data)

    # POST
    _require_perm(request, PERM_LETTER_CHANGE)
    ser = LetterRecipientInputSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    data = ser.validated_data

    recipient = services.LetterService.add_recipient(
        letter=letter,
        recipient_id=data.get("recipient_id"),
        recipient_name=data.get("recipient_name", ""),
        recipient_organization=data.get("recipient_organization", ""),
        recipient_type=data.get("recipient_type", "to"),
    )
    from simorgh.apps.correspondence.serializers import LetterRecipientSerializer

    return Response(
        LetterRecipientSerializer(recipient).data, status=status.HTTP_201_CREATED
    )


# ── Letter attachments ─────────────────────────────────────────────────


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def letter_attachments(request: Request, pk: int) -> Response:
    _require_perm(request, PERM_LETTER_VIEW)
    letter = _get_letter_or_404(pk, request)

    if request.method == "GET":
        from simorgh.apps.correspondence.serializers import LetterAttachmentSerializer

        attachments = sel.letter_attachments(letter.pk)
        return Response(LetterAttachmentSerializer(attachments, many=True).data)

    # POST
    _require_perm(request, PERM_LETTER_CHANGE)
    ser = AttachmentAddSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    data = ser.validated_data

    att = services.LetterService.add_attachment(
        letter_id=letter.pk,
        file_id=data["file_id"],
        attachment_type=data.get("attachment_type", "attachment"),
        description=data.get("description", ""),
        sort_order=data.get("sort_order", 0),
    )
    from simorgh.apps.correspondence.serializers import LetterAttachmentSerializer

    return Response(
        LetterAttachmentSerializer(att).data, status=status.HTTP_201_CREATED
    )


# ── Full-text search ───────────────────────────────────────────────────


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def letters_search(request: Request) -> Response:
    _require_perm(request, PERM_LETTER_VIEW)
    tenant = _tenant(request)

    query = request.query_params.get("q", "")
    if not query:
        return Response([], status=status.HTTP_200_OK)

    qs = Letter.objects.filter(tenant=tenant).filter(
        Q(subject__icontains=query)
        | Q(body__icontains=query)
        | Q(registration_number__icontains=query)
        | Q(sender_external__icontains=query)
        | Q(sender_organization__icontains=query)
        | Q(external_reference__icontains=query)
        | Q(summary__icontains=query)
    ).order_by("-created_at")[:50]

    return Response(LetterListSerializer(qs, many=True).data)


# ═══════════════════════════════════════════════════════════════════════════
# Categories
# ═══════════════════════════════════════════════════════════════════════════


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def categories_list(request: Request) -> Response:
    tenant = _tenant(request)
    org_id = _org_node_id(request)

    if request.method == "GET":
        qs = sel.categories_for_tenant(
            tenant.pk,
            category_type=request.query_params.get("category_type"),
            parent_id=_int_param(request, "parent_id"),
            is_active=_bool_param(request, "is_active", True),
        )
        return Response(CategoryListSerializer(qs, many=True).data)

    # POST
    _require_perm(request, PERM_CATEGORY_ADD)
    ser = CategoryCreateSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    data = ser.validated_data

    cat = services.CategoryService.create(
        tenant_id=tenant.pk,
        organization_node_id=org_id,
        name=data["name"],
        category_type=data.get("category_type", "subject"),
        parent_id=data.get("parent_id"),
        description=data.get("description", ""),
        sort_order=data.get("sort_order", 0),
    )
    return Response(CategoryListSerializer(cat).data, status=status.HTTP_201_CREATED)


@api_view(["GET", "PATCH", "DELETE"])
@permission_classes([IsAuthenticated])
def category_detail(request: Request, pk: int) -> Response:
    tenant = _tenant(request)

    try:
        category = Category.objects.get(pk=pk, tenant=tenant)
    except Category.DoesNotExist:
        from rest_framework.exceptions import NotFound

        raise NotFound("Category not found.")

    if request.method == "GET":
        return Response(CategoryListSerializer(category).data)

    if request.method == "PATCH":
        _require_perm(request, PERM_CATEGORY_CHANGE)
        ser = CategoryUpdateSerializer(data=request.data, partial=True)
        ser.is_valid(raise_exception=True)
        category = services.CategoryService.update(category, **ser.validated_data)
        return Response(CategoryListSerializer(category).data)

    # DELETE
    _require_perm(request, PERM_CATEGORY_DELETE)
    category.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)


# ═══════════════════════════════════════════════════════════════════════════
# Letter Templates
# ═══════════════════════════════════════════════════════════════════════════


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def templates_list(request: Request) -> Response:
    tenant = _tenant(request)
    org_id = _org_node_id(request)

    if request.method == "GET":
        qs = sel.letter_templates_for_tenant(
            tenant.pk,
            letter_type=request.query_params.get("letter_type"),
            is_active=_bool_param(request, "is_active", True),
        )
        return Response(LetterTemplateSerializer(qs, many=True).data)

    # POST
    _require_perm(request, PERM_TEMPLATE_ADD)
    ser = LetterTemplateCreateSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    data = ser.validated_data

    tpl = services.TemplateService.create_letter_template(
        tenant_id=tenant.pk,
        organization_node_id=org_id,
        name=data["name"],
        letter_type=data.get("letter_type", "outgoing"),
        subject_template=data.get("subject_template", ""),
        body_template=data.get("body_template", ""),
        letterhead_id=data.get("letterhead_id"),
        numbering_template_id=data.get("numbering_template_id"),
        is_default=data.get("is_default", False),
    )
    return Response(LetterTemplateSerializer(tpl).data, status=status.HTTP_201_CREATED)


@api_view(["GET", "PATCH", "DELETE"])
@permission_classes([IsAuthenticated])
def template_detail(request: Request, pk: int) -> Response:
    tenant = _tenant(request)

    try:
        tpl = LetterTemplate.objects.get(pk=pk, tenant=tenant)
    except LetterTemplate.DoesNotExist:
        from rest_framework.exceptions import NotFound

        raise NotFound("Template not found.")

    if request.method == "GET":
        return Response(LetterTemplateSerializer(tpl).data)

    if request.method == "PATCH":
        _require_perm(request, PERM_TEMPLATE_CHANGE)
        ser = LetterTemplateCreateSerializer(data=request.data, partial=True)
        ser.is_valid(raise_exception=True)
        data = ser.validated_data
        for field, value in data.items():
            if value is not None:
                setattr(tpl, field, value)
        tpl.save()
        return Response(LetterTemplateSerializer(tpl).data)

    # DELETE
    _require_perm(request, PERM_TEMPLATE_DELETE)
    tpl.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)


# ═══════════════════════════════════════════════════════════════════════════
# Numbering Templates
# ═══════════════════════════════════════════════════════════════════════════


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def numbering_templates_list(request: Request) -> Response:
    tenant = _tenant(request)
    org_id = _org_node_id(request)

    if request.method == "GET":
        qs = sel.numbering_templates_for_tenant(
            tenant.pk,
            letter_type=request.query_params.get("letter_type"),
        )
        return Response(NumberingTemplateSerializer(qs, many=True).data)

    # POST
    _require_perm(request, PERM_NUMBERING_ADD)
    ser = NumberingTemplateCreateSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    data = ser.validated_data

    nt = services.TemplateService.create_numbering_template(
        tenant_id=tenant.pk,
        organization_node_id=org_id,
        name=data["name"],
        pattern=data.get("pattern", "{year}/{counter:04d}"),
        prefix=data.get("prefix", ""),
        letter_type=data.get("letter_type", "outgoing"),
        is_default=data.get("is_default", False),
    )
    return Response(NumberingTemplateSerializer(nt).data, status=status.HTTP_201_CREATED)


@api_view(["GET", "PATCH", "DELETE"])
@permission_classes([IsAuthenticated])
def numbering_template_detail(request: Request, pk: int) -> Response:
    tenant = _tenant(request)

    try:
        nt = NumberingTemplate.objects.get(pk=pk, tenant=tenant)
    except NumberingTemplate.DoesNotExist:
        from rest_framework.exceptions import NotFound

        raise NotFound("Numbering template not found.")

    if request.method == "GET":
        return Response(NumberingTemplateSerializer(nt).data)

    if request.method == "PATCH":
        _require_perm(request, PERM_NUMBERING_CHANGE)
        ser = NumberingTemplateCreateSerializer(data=request.data, partial=True)
        ser.is_valid(raise_exception=True)
        data = ser.validated_data
        for field, value in data.items():
            if value is not None:
                setattr(nt, field, value)
        nt.save()
        return Response(NumberingTemplateSerializer(nt).data)

    # DELETE
    _require_perm(request, PERM_NUMBERING_DELETE)
    nt.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)


# ═══════════════════════════════════════════════════════════════════════════
# Letterhead Templates
# ═══════════════════════════════════════════════════════════════════════════


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def letterheads_list(request: Request) -> Response:
    tenant = _tenant(request)
    org_id = _org_node_id(request)

    if request.method == "GET":
        qs = sel.letterhead_templates_for_tenant(
            tenant.pk,
            is_active=_bool_param(request, "is_active", True),
        )
        return Response(LetterheadTemplateSerializer(qs, many=True).data)

    # POST
    _require_perm(request, PERM_LETTERHEAD_ADD)
    ser = LetterheadTemplateCreateSerializer(data=request.data)
    ser.is_valid(raise_exception=True)
    data = ser.validated_data

    lh = services.TemplateService.create_letterhead_template(
        tenant_id=tenant.pk,
        organization_node_id=org_id,
        name=data["name"],
        header_html=data.get("header_html", ""),
        footer_html=data.get("footer_html", ""),
        css_styles=data.get("css_styles", ""),
        is_default=data.get("is_default", False),
    )
    return Response(LetterheadTemplateSerializer(lh).data, status=status.HTTP_201_CREATED)


@api_view(["GET", "PATCH", "DELETE"])
@permission_classes([IsAuthenticated])
def letterhead_detail(request: Request, pk: int) -> Response:
    tenant = _tenant(request)

    try:
        lh = LetterheadTemplate.objects.get(pk=pk, tenant=tenant)
    except LetterheadTemplate.DoesNotExist:
        from rest_framework.exceptions import NotFound

        raise NotFound("Letterhead template not found.")

    if request.method == "GET":
        return Response(LetterheadTemplateSerializer(lh).data)

    if request.method == "PATCH":
        _require_perm(request, PERM_LETTERHEAD_CHANGE)
        ser = LetterheadTemplateCreateSerializer(data=request.data, partial=True)
        ser.is_valid(raise_exception=True)
        data = ser.validated_data
        for field, value in data.items():
            if value is not None:
                setattr(lh, field, value)
        lh.save()
        return Response(LetterheadTemplateSerializer(lh).data)

    # DELETE
    _require_perm(request, PERM_LETTERHEAD_DELETE)
    lh.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)


# ═══════════════════════════════════════════════════════════════════════════
# Param helpers
# ═══════════════════════════════════════════════════════════════════════════


def _int_param(request: Request, name: str) -> int | None:
    val = request.query_params.get(name)
    if val is not None and val != "":
        try:
            return int(val)
        except (TypeError, ValueError):
            pass
    return None


def _bool_param(request: Request, name: str, default: bool | None = None) -> bool | None:
    val = request.query_params.get(name)
    if val is None or val == "":
        return default
    return val.lower() in ("true", "1", "yes")
