"""Content Engine API views.

URL layout (all under /api/v1/content/):

  Categories
    GET  /categories/              — list tenant categories
    POST /categories/              — create category  (content.category.manage)
    GET  /categories/<pk>/         — retrieve
    PATCH /categories/<pk>/        — update           (content.category.manage)
    DELETE /categories/<pk>/       — delete           (content.category.manage)
    GET  /categories/tree/         — nested tree

  Items
    GET  /items/                   — list (filterable)
    POST /items/                   — create draft     (content.item.create)
    GET  /items/search/            — full-text search
    GET  /items/my/                — current user's items
    GET  /items/recent/            — recently published
    GET  /items/<public_id>/       — retrieve         (content.item.view)
    PATCH /items/<public_id>/      — update           (content.item.update)
    DELETE /items/<public_id>/     — soft-delete      (content.item.delete)
    POST /items/<public_id>/submit/   — submit for review (content.item.publish)
    POST /items/<public_id>/approve/  — approve        (content.item.publish)
    POST /items/<public_id>/publish/  — publish        (content.item.publish)
    POST /items/<public_id>/archive/  — archive        (content.item.publish)
    POST /items/<public_id>/revert/   — revert to draft (content.item.publish)
    GET  /items/<public_id>/versions/ — version list

  Public portal
    GET  /public/<slug>/           — public item by slug
"""

from __future__ import annotations

from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response

from simorgh.apps.content import selectors as sel
from simorgh.apps.content import services
from simorgh.apps.content.models import ContentCategory, ContentItem, ContentVersion

# ── Permission constants ────────────────────────────────────────────────────

PERM_CATEGORY_MANAGE = "content.category.manage"
PERM_ITEM_VIEW       = "content.item.view"
PERM_ITEM_CREATE     = "content.item.create"
PERM_ITEM_UPDATE     = "content.item.update"
PERM_ITEM_PUBLISH    = "content.item.publish"
PERM_ITEM_DELETE     = "content.item.delete"
PERM_VERSION_VIEW    = "content.version.view"


# ── Helpers ──────────────────────────────────────────────────────────────────

def _get_tenant_id(request: Request) -> int | None:
    tenant = getattr(request, "tenant", None)
    return tenant.pk if tenant else None


def _require_tenant(request: Request) -> int:
    tid = _get_tenant_id(request)
    if not tid:
        from rest_framework.exceptions import PermissionDenied
        raise PermissionDenied("Tenant header required.")
    return tid


def _get_org_node_id(request: Request, tenant_id: int) -> int:
    try:
        from simorgh.apps.organizations.models import OrganizationNode
        node = OrganizationNode.objects.filter(tenant_id=tenant_id).order_by("lft").first()
        return node.pk if node else 0
    except Exception:
        return 0


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_actor_id(request: Request) -> int | None:
    from simorgh.core.context import current_request_context
    ctx = current_request_context()
    return ctx.actor.pk if ctx.actor else None


# ── Serializers ─────────────────────────────────────────────────────────────

def _serialize_category(cat: ContentCategory) -> dict:
    return {
        "id": cat.pk,
        "public_id": str(cat.public_id),
        "name": cat.name,
        "slug": cat.slug,
        "parent_id": cat.parent_id,
        "parent_name": cat.parent.name if cat.parent else None,
        "description": cat.description,
        "icon": cat.icon,
        "sort_order": cat.sort_order,
        "is_active": cat.is_active,
        "created_at": cat.created_at.isoformat(),
    }


def _serialize_item(item: ContentItem, *, detail: bool = False) -> dict:
    data = {
        "id": item.pk,
        "public_id": str(item.public_id),
        "title": item.title,
        "slug": item.slug,
        "content_type": item.content_type,
        "content_type_display": item.get_content_type_display() if hasattr(item, "get_content_type_display") else item.content_type,
        "category_id": item.category_id,
        "category_name": item.category.name if item.category else None,
        "excerpt": item.excerpt,
        "status": item.status,
        "status_display": item.get_status_display() if hasattr(item, "get_status_display") else item.status,
        "visibility": item.visibility,
        "visibility_display": item.get_visibility_display() if hasattr(item, "get_visibility_display") else item.visibility,
        "author_id": item.author_id,
        "author_name": item.author.get_full_name() or item.author.email if item.author_id else None,
        "published_at": item.published_at.isoformat() if item.published_at else None,
        "archived_at": item.archived_at.isoformat() if item.archived_at else None,
        "view_count": item.view_count,
        "helpful_count": item.helpful_count,
        "not_helpful_count": item.not_helpful_count,
        "created_at": item.created_at.isoformat(),
        "updated_at": item.updated_at.isoformat(),
    }
    if detail:
        data.update({
            "body": item.body,
            "meta_title": item.meta_title,
            "meta_description": item.meta_description,
            "meta_keywords": item.meta_keywords,
            "ai_description": item.ai_description,
            "ai_semantic_type": item.ai_semantic_type,
            "ai_summary": item.ai_summary,
            "related_content": item.related_content,
            "workspace_id": item.workspace_id,
            "version_count": item.versions.count(),
        })
    return data


def _serialize_version(ver: ContentVersion) -> dict:
    return {
        "id": ver.pk,
        "public_id": str(ver.public_id),
        "content_item_id": ver.content_item_id,
        "version": ver.version,
        "title": ver.title,
        "body": ver.body,
        "excerpt": ver.excerpt,
        "status": ver.status,
        "changed_by_id": ver.changed_by_id,
        "changed_by_name": ver.changed_by.get_full_name() or ver.changed_by.email if ver.changed_by_id else None,
        "change_summary": ver.change_summary,
        "created_at": ver.created_at.isoformat(),
    }


# ── Categories ──────────────────────────────────────────────────────────────

@api_view(["GET"])
@permission_classes([IsAuthenticated])
def category_list(request: Request) -> Response:
    tid = _get_tenant_id(request)
    if not tid:
        return Response([], status=status.HTTP_200_OK)
    cats = sel.categories_for_tenant(tid)
    return Response([_serialize_category(c) for c in cats])


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def category_tree_view(request: Request) -> Response:
    tid = _get_tenant_id(request)
    if not tid:
        return Response([], status=status.HTTP_200_OK)
    tree = sel.category_tree(tid)
    return Response(tree)


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

    try:
        cat = sel.category_by_pk(tid, pk)
    except ContentCategory.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        return Response(_serialize_category(cat))

    _require_perm(request, PERM_CATEGORY_MANAGE)

    if request.method == "DELETE":
        services.delete_category(cat)
        return Response(status=status.HTTP_204_NO_CONTENT)

    # PATCH
    data = request.data
    cat = services.update_category(
        cat,
        name=data.get("name"),
        slug=data.get("slug"),
        parent_id=data.get("parent_id", ...),
        description=data.get("description"),
        icon=data.get("icon"),
        sort_order=data.get("sort_order"),
        is_active=data.get("is_active"),
    )
    return Response(_serialize_category(cat))


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def category_create(request: Request) -> Response:
    tid = _require_tenant(request)
    _require_perm(request, PERM_CATEGORY_MANAGE)
    org_id = _get_org_node_id(request, tid)

    data = request.data
    cat = services.create_category(
        tenant_id=tid,
        organization_node_id=org_id,
        name=data["name"],
        slug=data["slug"],
        parent_id=data.get("parent_id"),
        description=data.get("description", ""),
        icon=data.get("icon", ""),
        sort_order=data.get("sort_order", 0),
    )
    return Response(_serialize_category(cat), status=status.HTTP_201_CREATED)


# ── Content Items ───────────────────────────────────────────────────────────

@api_view(["GET"])
@permission_classes([IsAuthenticated])
def item_list(request: Request) -> Response:
    tid = _get_tenant_id(request)
    if not tid:
        return Response([], status=status.HTTP_200_OK)

    _require_perm(request, PERM_ITEM_VIEW)

    items = sel.items_for_tenant(
        tid,
        content_type=request.GET.get("content_type"),
        status=request.GET.get("status"),
        category_id=_int_param(request, "category_id"),
        author_id=_int_param(request, "author_id"),
        visibility=request.GET.get("visibility"),
        workspace_id=_int_param(request, "workspace_id"),
    )
    return Response([_serialize_item(item) for item in items[:100]])


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def item_search(request: Request) -> Response:
    tid = _get_tenant_id(request)
    if not tid:
        return Response([], status=status.HTTP_200_OK)

    q = request.GET.get("q", "").strip()
    if len(q) < 2:
        return Response([], status=status.HTTP_200_OK)

    items = sel.search_items(tid, q)
    return Response([_serialize_item(item) for item in items])


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def item_create(request: Request) -> Response:
    tid = _require_tenant(request)
    _require_perm(request, PERM_ITEM_CREATE)
    org_id = _get_org_node_id(request, tid)
    actor_id = _get_actor_id(request)

    data = request.data
    item = services.create_content_item(
        tenant_id=tid,
        organization_node_id=org_id,
        title=data["title"],
        slug=data["slug"],
        content_type=data["content_type"],
        author_id=actor_id or int(data.get("author_id", 0)),
        category_id=data.get("category_id"),
        body=data.get("body", ""),
        excerpt=data.get("excerpt", ""),
        visibility=data.get("visibility", "internal"),
        workspace_id=data.get("workspace_id"),
        meta_title=data.get("meta_title", ""),
        meta_description=data.get("meta_description", ""),
        meta_keywords=data.get("meta_keywords", ""),
    )
    return Response(_serialize_item(item, detail=True), status=status.HTTP_201_CREATED)


@api_view(["GET", "PATCH", "DELETE"])
@permission_classes([IsAuthenticated])
def item_detail(request: Request, public_id: str) -> Response:
    tid = _require_tenant(request)

    try:
        item = sel.item_by_public_id(tid, public_id)
    except ContentItem.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        _require_perm(request, PERM_ITEM_VIEW)
        return Response(_serialize_item(item, detail=True))

    if request.method == "DELETE":
        _require_perm(request, PERM_ITEM_DELETE)
        services.delete_content_item(item)
        return Response(status=status.HTTP_204_NO_CONTENT)

    # PATCH
    _require_perm(request, PERM_ITEM_UPDATE)
    data = request.data

    item = services.update_content_item(
        item,
        title=data.get("title"),
        slug=data.get("slug"),
        content_type=data.get("content_type"),
        category_id=data.get("category_id", ...),
        body=data.get("body"),
        excerpt=data.get("excerpt"),
        visibility=data.get("visibility"),
        meta_title=data.get("meta_title"),
        meta_description=data.get("meta_description"),
        meta_keywords=data.get("meta_keywords"),
        ai_description=data.get("ai_description"),
        ai_semantic_type=data.get("ai_semantic_type"),
        ai_summary=data.get("ai_summary"),
        related_content=data.get("related_content"),
    )
    return Response(_serialize_item(item, detail=True))


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def item_submit(request: Request, public_id: str) -> Response:
    tid = _require_tenant(request)
    _require_perm(request, PERM_ITEM_PUBLISH)
    actor_id = _get_actor_id(request)

    try:
        item = sel.item_by_public_id(tid, public_id)
    except ContentItem.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        item = services.submit_for_review(item, actor_id=actor_id or 0)
    except ValueError as e:
        return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(_serialize_item(item, detail=True))


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def item_approve(request: Request, public_id: str) -> Response:
    tid = _require_tenant(request)
    _require_perm(request, PERM_ITEM_PUBLISH)
    actor_id = _get_actor_id(request)

    try:
        item = sel.item_by_public_id(tid, public_id)
    except ContentItem.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        item = services.approve_content(item, actor_id=actor_id or 0, note=request.data.get("note", ""))
    except ValueError as e:
        return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(_serialize_item(item, detail=True))


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def item_publish(request: Request, public_id: str) -> Response:
    tid = _require_tenant(request)
    _require_perm(request, PERM_ITEM_PUBLISH)
    actor_id = _get_actor_id(request)

    try:
        item = sel.item_by_public_id(tid, public_id)
    except ContentItem.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        item = services.publish_content(
            item,
            actor_id=actor_id or 0,
            change_summary=request.data.get("change_summary", ""),
        )
    except ValueError as e:
        return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(_serialize_item(item, detail=True))


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def item_archive(request: Request, public_id: str) -> Response:
    tid = _require_tenant(request)
    _require_perm(request, PERM_ITEM_PUBLISH)
    actor_id = _get_actor_id(request)

    try:
        item = sel.item_by_public_id(tid, public_id)
    except ContentItem.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        item = services.archive_content(item, actor_id=actor_id or 0)
    except ValueError as e:
        return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(_serialize_item(item, detail=True))


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def item_revert(request: Request, public_id: str) -> Response:
    tid = _require_tenant(request)
    _require_perm(request, PERM_ITEM_PUBLISH)
    actor_id = _get_actor_id(request)

    try:
        item = sel.item_by_public_id(tid, public_id)
    except ContentItem.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        item = services.revert_to_draft(item, actor_id=actor_id or 0)
    except ValueError as e:
        return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    return Response(_serialize_item(item, detail=True))


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def item_versions(request: Request, public_id: str) -> Response:
    tid = _require_tenant(request)
    _require_perm(request, PERM_VERSION_VIEW)

    try:
        item = sel.item_by_public_id(tid, public_id)
    except ContentItem.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    versions = sel.versions_for_item(item)
    return Response([_serialize_version(v) for v in versions])


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def item_version_detail(request: Request, public_id: str, version: int) -> Response:
    tid = _require_tenant(request)
    _require_perm(request, PERM_VERSION_VIEW)

    try:
        item = sel.item_by_public_id(tid, public_id)
        ver = sel.version_by_id(tid, item.pk, version)
    except (ContentItem.DoesNotExist, ContentVersion.DoesNotExist):
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    return Response(_serialize_version(ver))


@api_view(["POST"])
@permission_classes([AllowAny])
def item_feedback(request: Request, public_id: str) -> Response:
    tid = _get_tenant_id(request)

    try:
        item = sel.item_by_public_id(tid or 0, public_id)
    except ContentItem.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    data = request.data
    services.record_feedback(
        item,
        user_id=_get_actor_id(request),
        is_helpful=data.get("helpful", True),
        comment=data.get("comment", ""),
    )
    return Response({"detail": "Feedback recorded."})


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def my_items(request: Request) -> Response:
    tid = _require_tenant(request)
    actor_id = _get_actor_id(request)
    if not actor_id:
        return Response([], status=status.HTTP_200_OK)

    items = sel.my_items(tid, actor_id)
    return Response([_serialize_item(item) for item in items])


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def recent_items(request: Request) -> Response:
    tid = _get_tenant_id(request)
    if not tid:
        return Response([], status=status.HTTP_200_OK)
    items = sel.recent_items(tid)
    return Response([_serialize_item(item) for item in items])


# ── Public Portal ────────────────────────────────────────────────────────────

@api_view(["GET"])
@permission_classes([AllowAny])
def public_item(request: Request, slug: str) -> Response:
    tid = _get_tenant_id(request)
    if not tid:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        item = sel.item_by_slug(tid, slug)
    except ContentItem.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    services.increment_view_count(item)
    return Response(_serialize_item(item, detail=True))


# ── Utility ──────────────────────────────────────────────────────────────────

def _int_param(request: Request, key: str) -> int | None:
    val = request.GET.get(key)
    if val is not None:
        try:
            return int(val)
        except (ValueError, TypeError):
            return None
    return None
