"""Membership API views — user-to-org-node role assignment management."""
from __future__ import annotations

from django.contrib.auth import get_user_model
from django.db import IntegrityError
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.memberships.models import Membership

User = get_user_model()


def _serialize_membership(m: Membership, *, include_users: bool = False) -> dict:
    data: dict = {
        "id": m.pk,
        "tenant_id": m.tenant_id,
        "organization_node_id": m.organization_node_id,
        "organization_node_name": m.organization_node.name if m.organization_node_id else None,
        "role_id": str(m.role_id) if m.role_id else None,
        "role_code": m.role.code,
        "role_name": m.role.name,
        "status": m.status,
        "scope_extra": m.scope_extra,
        "created_at": m.created_at.isoformat() if m.created_at else None,
        "updated_at": m.updated_at.isoformat() if m.updated_at else None,
    }
    if include_users:
        data["users"] = [
            {"id": u.pk, "display_name": u.username, "email": u.email or None, "mobile": getattr(u, "mobile", None)}
            for u in m.users.all()
        ]
    data["users_count"] = m.users.count()
    return data


# ---------------------------------------------------------------------------
# Membership CRUD
# ---------------------------------------------------------------------------

@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def membership_list(request: Request) -> Response:
    """GET: list memberships. POST: create a new membership."""
    if not request.user.is_staff:
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "POST":
        data = request.data
        org_node_id = data.get("organization_node_id")
        role_id = data.get("role_id")
        if not org_node_id or not role_id:
            return Response(
                {"error": {"code": "missing_fields", "message": "organization_node_id and role_id are required."}},
                status=status.HTTP_400_BAD_REQUEST,
            )

        user_ids = data.get("user_ids", [])
        scope_extra = data.get("scope_extra", {})
        mstatus = str(data.get("status", "active"))

        try:
            membership = Membership.objects.create(
                tenant=tenant,
                organization_node_id=int(org_node_id),
                role_id=str(role_id),
                status=mstatus,
                scope_extra=scope_extra,
            )
        except IntegrityError:
            return Response(
                {"error": {"code": "duplicate", "message": "This combination of tenant, node, and role already exists."}},
                status=status.HTTP_409_CONFLICT,
            )

        if user_ids:
            users = User.objects.filter(pk__in=user_ids)
            membership.users.set(users)

        return Response(_serialize_membership(membership, include_users=True), status=status.HTTP_201_CREATED)

    # GET
    qs = Membership.objects.filter(tenant=tenant).select_related("organization_node", "role").prefetch_related("users")

    org_node_id = request.query_params.get("organization_node_id")
    if org_node_id:
        qs = qs.filter(organization_node_id=int(org_node_id))

    role_id = request.query_params.get("role_id")
    if role_id:
        qs = qs.filter(role_id=str(role_id))

    q_status = request.query_params.get("status")
    if q_status:
        qs = qs.filter(status=q_status)

    user_id = request.query_params.get("user_id")
    if user_id:
        qs = qs.filter(users__id=int(user_id))

    include_users = request.query_params.get("include_users") == "1"
    return Response([_serialize_membership(m, include_users=include_users) for m in qs.order_by("organization_node__path_string")])


@api_view(["GET", "PATCH", "DELETE"])
@permission_classes([IsAuthenticated])
def membership_detail(request: Request, pk: int) -> Response:
    """GET, PATCH or DELETE a membership."""
    if not request.user.is_staff:
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        membership = Membership.objects.select_related("organization_node", "role").prefetch_related("users").get(
            pk=pk, tenant=tenant
        )
    except Membership.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "DELETE":
        membership.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    if request.method == "PATCH":
        data = request.data
        update_fields: list[str] = []

        if "status" in data:
            membership.status = str(data["status"])
            update_fields.append("status")

        if "scope_extra" in data:
            membership.scope_extra = data["scope_extra"]
            update_fields.append("scope_extra")

        if update_fields:
            membership.save(update_fields=update_fields)

        if "user_ids" in data:
            users = User.objects.filter(pk__in=data["user_ids"])
            membership.users.set(users)

    return Response(_serialize_membership(membership, include_users=True))


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def membership_add_users(request: Request, pk: int) -> Response:
    """POST /api/v1/memberships/<pk>/add-users/ — add users to a membership."""
    if not request.user.is_staff:
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        membership = Membership.objects.get(pk=pk, tenant=tenant)
    except Membership.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    user_ids = request.data.get("user_ids", [])
    if not user_ids:
        return Response({"error": {"code": "missing_user_ids", "message": "user_ids is required."}}, status=status.HTTP_400_BAD_REQUEST)

    users = User.objects.filter(pk__in=user_ids)
    membership.users.add(*users)
    return Response(_serialize_membership(membership, include_users=True))


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def membership_remove_users(request: Request, pk: int) -> Response:
    """POST /api/v1/memberships/<pk>/remove-users/ — remove users from a membership."""
    if not request.user.is_staff:
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        membership = Membership.objects.get(pk=pk, tenant=tenant)
    except Membership.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    user_ids = request.data.get("user_ids", [])
    if not user_ids:
        return Response({"error": {"code": "missing_user_ids", "message": "user_ids is required."}}, status=status.HTTP_400_BAD_REQUEST)

    users = User.objects.filter(pk__in=user_ids)
    membership.users.remove(*users)
    return Response(_serialize_membership(membership, include_users=True))
