"""RBAC admin API — operational (staff) users, roles (groups) and permissions.

Separated from business users (buyers/sellers in core.User). These endpoints
manage Django ``auth.User`` staff accounts, ``auth.Group`` roles and
``auth.Permission`` capabilities.

Reads require an authenticated staff session; all mutations require a
superuser (access management is a Security-Admin capability per V2 §5).
Every mutation is written to AuditLog.
"""

from django.contrib.auth.models import Group, Permission, User as StaffUser
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError as DjangoValidationError
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from .admin_api import _audit, _paginate, _require_admin
from .views import _is_admin_request


def _require_superuser(request):
    user = getattr(request, "user", None)
    if not (user and user.is_authenticated and user.is_active and user.is_staff and user.is_superuser):
        return Response(
            {"detail": "This action requires a superuser (Security Admin)."},
            status=status.HTTP_403_FORBIDDEN,
        )
    return None


# ---------------------------------------------------------------------------
# Serializers
# ---------------------------------------------------------------------------


def _serialize_staff(u: StaffUser):
    return {
        "id": u.pk,
        "username": u.get_username(),
        "display_name": (u.get_full_name() or u.get_username()).strip(),
        "first_name": u.first_name,
        "last_name": u.last_name,
        "email": u.email,
        "is_active": u.is_active,
        "is_staff": u.is_staff,
        "is_superuser": u.is_superuser,
        "roles": [{"id": g.pk, "name": g.name} for g in u.groups.all()],
        "last_login": u.last_login,
        "date_joined": u.date_joined,
    }


def _serialize_permission(p: Permission):
    return {
        "id": p.pk,
        "codename": p.codename,
        "name": p.name,
        "app_label": p.content_type.app_label,
        "model": p.content_type.model,
        "label": f"{p.content_type.app_label}.{p.codename}",
    }


def _serialize_role(g: Group):
    return {
        "id": g.pk,
        "name": g.name,
        "member_count": g.user_set.count(),
        "permission_count": g.permissions.count(),
        "permissions": [{"id": p.pk, "label": f"{p.content_type.app_label}.{p.codename}", "name": p.name}
                        for p in g.permissions.select_related("content_type").all()],
    }


# ---------------------------------------------------------------------------
# Staff users
# ---------------------------------------------------------------------------


@api_view(["GET", "POST"])
def admin_staff_list(request):
    err = _require_admin(request)
    if err:
        return err

    if request.method == "POST":
        err = _require_superuser(request)
        if err:
            return err
        data = request.data
        username = str(data.get("username") or "").strip()
        password = str(data.get("password") or "")
        if not username or not password:
            return Response({"detail": "username and password are required."}, status=status.HTTP_400_BAD_REQUEST)
        if StaffUser.objects.filter(username=username).exists():
            return Response({"detail": "A user with that username already exists."}, status=status.HTTP_400_BAD_REQUEST)
        try:
            validate_password(password)
        except DjangoValidationError as exc:
            return Response({"detail": " ".join(exc.messages)}, status=status.HTTP_400_BAD_REQUEST)

        u = StaffUser(
            username=username,
            email=str(data.get("email") or "").strip(),
            first_name=str(data.get("first_name") or "").strip(),
            last_name=str(data.get("last_name") or "").strip(),
            is_staff=True,
            is_active=True,
            is_superuser=bool(data.get("is_superuser")),
        )
        u.set_password(password)
        u.save()
        role_ids = data.get("role_ids") or []
        if role_ids:
            u.groups.set(Group.objects.filter(pk__in=role_ids))
        _audit(request, "staff.create", None, {"username": username, "is_superuser": u.is_superuser})
        return Response(_serialize_staff(u), status=status.HTTP_201_CREATED)

    qs = StaffUser.objects.filter(is_staff=True).prefetch_related("groups").order_by("username")
    q = request.query_params.get("q")
    if q:
        from django.db.models import Q
        qs = qs.filter(Q(username__icontains=q) | Q(email__icontains=q) | Q(first_name__icontains=q) | Q(last_name__icontains=q))
    return Response(_paginate(request, qs, _serialize_staff))


@api_view(["GET", "POST"])
def admin_staff_detail(request, staff_id):
    err = _require_admin(request)
    if err:
        return err
    u = StaffUser.objects.filter(pk=staff_id).first()
    if not u:
        return Response({"detail": "Staff user not found."}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        return Response(_serialize_staff(u))

    # Update (superuser only)
    err = _require_superuser(request)
    if err:
        return err
    data = request.data
    changed = {}
    for field in ("email", "first_name", "last_name"):
        if field in data:
            setattr(u, field, str(data.get(field) or "").strip())
            changed[field] = getattr(u, field)
    if "is_active" in data:
        # Guard: cannot deactivate self.
        if u.pk == request.user.pk and not bool(data.get("is_active")):
            return Response({"detail": "You cannot deactivate your own account."}, status=status.HTTP_400_BAD_REQUEST)
        u.is_active = bool(data.get("is_active"))
        changed["is_active"] = u.is_active
    if "is_superuser" in data:
        if u.pk == request.user.pk and not bool(data.get("is_superuser")):
            return Response({"detail": "You cannot remove your own superuser flag."}, status=status.HTTP_400_BAD_REQUEST)
        u.is_superuser = bool(data.get("is_superuser"))
        changed["is_superuser"] = u.is_superuser
    u.save()
    if "role_ids" in data:
        u.groups.set(Group.objects.filter(pk__in=(data.get("role_ids") or [])))
        changed["role_ids"] = list(u.groups.values_list("pk", flat=True))
    _audit(request, "staff.update", None, {"username": u.username, "changed": changed})
    return Response(_serialize_staff(u))


@api_view(["POST"])
def admin_staff_reset_password(request, staff_id):
    err = _require_superuser(request)
    if err:
        return err
    u = StaffUser.objects.filter(pk=staff_id).first()
    if not u:
        return Response({"detail": "Staff user not found."}, status=status.HTTP_404_NOT_FOUND)
    password = str(request.data.get("password") or "")
    try:
        validate_password(password, user=u)
    except DjangoValidationError as exc:
        return Response({"detail": " ".join(exc.messages)}, status=status.HTTP_400_BAD_REQUEST)
    u.set_password(password)
    u.save(update_fields=["password"])
    _audit(request, "staff.reset_password", None, {"username": u.username})
    return Response({"detail": "Password updated."})


@api_view(["POST"])
def admin_staff_delete(request, staff_id):
    err = _require_superuser(request)
    if err:
        return err
    u = StaffUser.objects.filter(pk=staff_id).first()
    if not u:
        return Response({"detail": "Staff user not found."}, status=status.HTTP_404_NOT_FOUND)
    if u.pk == request.user.pk:
        return Response({"detail": "You cannot delete your own account."}, status=status.HTTP_400_BAD_REQUEST)
    if u.is_superuser and StaffUser.objects.filter(is_superuser=True, is_active=True).count() <= 1:
        return Response({"detail": "Cannot delete the last active superuser."}, status=status.HTTP_400_BAD_REQUEST)
    username = u.username
    u.is_active = False  # soft-disable; keep row for audit integrity
    u.save(update_fields=["is_active"])
    _audit(request, "staff.deactivate", None, {"username": username})
    return Response({"detail": f"{username} deactivated."})


# ---------------------------------------------------------------------------
# Roles (groups)
# ---------------------------------------------------------------------------


@api_view(["GET", "POST"])
def admin_roles(request):
    err = _require_admin(request)
    if err:
        return err

    if request.method == "POST":
        err = _require_superuser(request)
        if err:
            return err
        name = str(request.data.get("name") or "").strip()
        if not name:
            return Response({"detail": "name is required."}, status=status.HTTP_400_BAD_REQUEST)
        if Group.objects.filter(name=name).exists():
            return Response({"detail": "A role with that name already exists."}, status=status.HTTP_400_BAD_REQUEST)
        g = Group.objects.create(name=name)
        perm_ids = request.data.get("permission_ids") or []
        if perm_ids:
            g.permissions.set(Permission.objects.filter(pk__in=perm_ids))
        _audit(request, "role.create", None, {"name": name, "permissions": len(perm_ids)})
        return Response(_serialize_role(g), status=status.HTTP_201_CREATED)

    qs = Group.objects.all().order_by("name")
    return Response(_paginate(request, qs, _serialize_role, default_page_size=100))


@api_view(["GET", "POST"])
def admin_role_detail(request, role_id):
    err = _require_admin(request)
    if err:
        return err
    g = Group.objects.filter(pk=role_id).first()
    if not g:
        return Response({"detail": "Role not found."}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        return Response(_serialize_role(g))

    err = _require_superuser(request)
    if err:
        return err
    data = request.data
    if "name" in data:
        new_name = str(data.get("name") or "").strip()
        if not new_name:
            return Response({"detail": "name cannot be empty."}, status=status.HTTP_400_BAD_REQUEST)
        if Group.objects.filter(name=new_name).exclude(pk=g.pk).exists():
            return Response({"detail": "Another role already uses that name."}, status=status.HTTP_400_BAD_REQUEST)
        g.name = new_name
        g.save(update_fields=["name"])
    if "permission_ids" in data:
        g.permissions.set(Permission.objects.filter(pk__in=(data.get("permission_ids") or [])))
    _audit(request, "role.update", None, {"name": g.name, "permission_count": g.permissions.count()})
    return Response(_serialize_role(g))


@api_view(["POST"])
def admin_role_delete(request, role_id):
    err = _require_superuser(request)
    if err:
        return err
    g = Group.objects.filter(pk=role_id).first()
    if not g:
        return Response({"detail": "Role not found."}, status=status.HTTP_404_NOT_FOUND)
    name = g.name
    members = g.user_set.count()
    g.delete()
    _audit(request, "role.delete", None, {"name": name, "had_members": members})
    return Response({"detail": f"Role '{name}' deleted."})


# ---------------------------------------------------------------------------
# Permissions catalog
# ---------------------------------------------------------------------------


@api_view(["GET"])
def admin_permissions(request):
    err = _require_admin(request)
    if err:
        return err
    qs = Permission.objects.select_related("content_type").order_by("content_type__app_label", "codename")
    app = request.query_params.get("app")
    if app:
        qs = qs.filter(content_type__app_label=app)
    grouped: dict = {}
    for p in qs:
        grouped.setdefault(p.content_type.app_label, []).append(_serialize_permission(p))
    return Response({"count": qs.count(), "groups": grouped})
