"""Repository ↔ REP OER integration (PRD REPO-SP8)."""

from __future__ import annotations

import logging

from django.conf import settings
from rest_framework import permissions, serializers, status
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.repository.documents.audit_helper import action_access, audit_repository
from apps.repository.documents.models import Document
from apps.repository.documents.serializers import DocumentSerializer

logger = logging.getLogger(__name__)


class RepOERSearchView(ListAPIView):
    """List published Repository documents suitable to link as course OER."""

    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = DocumentSerializer

    def get_queryset(self):
        qs = Document.objects.public()
        q = (self.request.query_params.get("q") or "").strip()
        if q:
            qs = qs.filter(title__icontains=q)
        dt = self.request.query_params.get("type")
        if dt:
            qs = qs.filter(document_type=dt)
        return qs[:50]

    def list(self, request, *args, **kwargs):
        # FRREP-INT010 — inbound REP OER search queries join the integration
        # audit trail (previously only outbound events were logged).
        q = (request.query_params.get("q") or "").strip()
        audit_repository(
            actor=request.user if request.user.is_authenticated else None,
            action=action_access(),
            target_model="RepOERSearch",
            object_id="",
            object_repr=f"REP OER search: {q}"[:200],
            changes={"query": q, "type": request.query_params.get("type") or "", "source": "REP"},
            request=request,
        )
        return super().list(request, *args, **kwargs)


class _LinkSerializer(serializers.Serializer):
    document_uid = serializers.UUIDField()
    course_id = serializers.CharField(max_length=64)
    instructor_id = serializers.CharField(max_length=64, required=False, allow_blank=True)


class RepLinkRegisterView(APIView):
    """REP records that a Repository document is linked as OER in a course."""

    permission_classes = (permissions.IsAuthenticated,)

    def post(self, request):
        from apps.repository.documents.models import IntegrationOutbox

        payload = _LinkSerializer(data=request.data)
        payload.is_valid(raise_exception=True)
        doc = Document.objects.filter(document_uid=payload.validated_data["document_uid"]).first()
        if not doc:
            return Response({"detail": "Unknown document_uid"}, status=status.HTTP_404_NOT_FOUND)
        IntegrationOutbox.objects.create(
            target=IntegrationOutbox.Target.REP,
            event_type="rep_oer_linked",
            payload={
                "document_id": doc.pk,
                "public_document_id": doc.public_document_id,
                "course_id": payload.validated_data["course_id"],
                "instructor_id": payload.validated_data.get("instructor_id", ""),
            },
        )
        return Response(
            {"document_id": doc.public_document_id, "link": "recorded"},
            status=status.HTTP_201_CREATED,
        )


REP_DOCUMENT_EVENT_PATH = "/rep/api/integrations/repository/document-event/"
"""Path on the REP side (``apps.rep.urls``); used only if a REP base URL is
ever configured for a cross-process deployment. This deployment runs REP and
Repository in the same Django process, so ``deliver`` below calls REP's
service functions directly instead.
"""


def deliver(outbox_row) -> None:
    """Deliver a REP-targeted outbox row.

    Handles ``document_withdrawn`` / ``document_reinstated`` by flagging (or
    clearing) the ``is_broken`` state on any REP ``OERLink`` rows pointing at
    the affected document. Same-process today (no REP base URL is
    configured), so the REP service functions are called in-process; a
    ``REP_BASE_URL``-style setting could route this over HTTP later without
    changing the outbox/dispatch contract. Best-effort: any failure here is
    logged and swallowed so it never breaks delivery of other outbox rows
    (MEL/SME-Hub) processed in the same dispatch pass.
    """
    event_type = outbox_row.event_type
    if event_type not in {"document_withdrawn", "document_reinstated"}:
        logger.info(
            "rep_link.deliver: no handler for event_type=%s (doc %s); logging only",
            event_type,
            outbox_row.payload.get("document_id"),
        )
        return

    document_uid = outbox_row.payload.get("document_uid")
    if not document_uid:
        logger.warning(
            "rep_link.deliver: %s payload missing document_uid (doc %s); skipping",
            event_type,
            outbox_row.payload.get("document_id"),
        )
        return

    base = (getattr(settings, "REP_BASE_URL", "") or "").rstrip("/")
    try:
        if base:
            _post_to_rep(base=base, event_type=event_type, document_uid=document_uid, payload=outbox_row.payload)
        else:
            _deliver_same_process(event_type=event_type, document_uid=document_uid, payload=outbox_row.payload)
    except Exception as exc:  # pragma: no cover - defensive, always logged
        logger.warning("rep_link.deliver: %s failed for doc %s: %s", event_type, document_uid, exc)


def _deliver_same_process(*, event_type: str, document_uid, payload: dict) -> None:
    # In-process delivery marked/cleared "broken OER link" flags in the REP LMS.
    # REP was retired for Moodle, so there is no in-process consumer; a Moodle
    # OER-link integration can be added here later. No-op for now.
    logger.info(
        "rep_link.deliver: %s for doc %s ignored (REP LMS retired)",
        event_type, document_uid,
    )


def _post_to_rep(*, base: str, event_type: str, document_uid, payload: dict) -> None:
    import requests

    requests.post(
        f"{base}{REP_DOCUMENT_EVENT_PATH}",
        json={
            "event_type": event_type,
            "document_uid": str(document_uid),
            "reason": payload.get("reason", ""),
        },
        timeout=5,
    )
