from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render

from core.models import AuditEvent, Customer, Dispatch, Order, Payment

# Initial SKEVER permission catalogue. These permissions are stored in Django's
# existing auth_permission table. Operational views must enforce them separately.
PERMISSION_SECTIONS = [
    ("Orders", Order, [
        ("skv_view_orders", "View orders"),
        ("skv_accept_orders", "Accept orders"),
        ("skv_decline_orders", "Decline orders"),
        ("skv_hold_orders", "Place orders on hold"),
        ("skv_edit_order_items", "Edit order items"),
    ]),
    ("Payments", Payment, [
        ("skv_view_payments", "View payment details"),
        ("skv_verify_payments", "Verify payment submissions"),
        ("skv_reverse_payments", "Reverse or refund payments"),
    ]),
    ("Dispatch", Dispatch, [
        ("skv_view_dispatches", "View dispatches"),
        ("skv_assign_dispatches", "Assign deliveries"),
        ("skv_update_dispatches", "Update delivery status"),
    ]),
    # No catalogue model is registered yet, so these are temporary permission
    # records attached to Customer's content type until a catalogue model exists.
    ("Catalogue", Customer, [
        ("skv_view_catalogue", "View catalogue"),
        ("skv_edit_catalogue", "Add and edit catalogue items"),
        ("skv_change_prices", "Change product prices"),
    ]),
    ("Administration", Order, [
        ("skv_manage_staff", "Create and manage staff"),
        ("skv_manage_roles", "Create roles and assign permissions"),
        ("skv_view_audit_logs", "View audit logs"),
    ]),
]


def _permission_rows():
    return [
        {"category": category, "model": model, "codename": codename, "name": name}
        for category, model, items in PERMISSION_SECTIONS
        for codename, name in items
    ]


def _require_superuser(request):
    # Keep role management restricted to the existing active Django superuser
    # until staff permissions have been fully wired and tested.
    user = request.user
    if not user.is_authenticated or not user.is_active or not user.is_superuser:
        raise PermissionDenied("Only an active superuser may manage roles.")


@login_required
def role_management(request):
    _require_superuser(request)

    editing_group = None
    edit_id = request.GET.get("edit")
    if edit_id:
        editing_group = get_object_or_404(Group, pk=edit_id)

    catalog = _permission_rows()
    existing = {
        p.codename: p
        for p in Permission.objects.filter(
            codename__in=[row["codename"] for row in catalog],
            content_type__app_label="core",
        )
    }

    sections = []
    for category, model, items in PERMISSION_SECTIONS:
        rows = []
        for codename, label in items:
            permission = existing.get(codename)
            rows.append({
                "codename": codename,
                "label": label,
                "checked": bool(
                    editing_group and permission
                    and editing_group.permissions.filter(pk=permission.pk).exists()
                ),
            })
        sections.append({"category": category, "items": rows})

    if request.method == "POST":
        action = request.POST.get("action", "save")
        if action == "delete":
            group = get_object_or_404(Group, pk=request.POST.get("group_id"))
            if group.user_set.exists():
                messages.error(request, "This role is assigned to staff. Unassign staff before deleting it.")
                return redirect("role_management")
            name, group_id = group.name, group.pk
            group.delete()
            AuditEvent.objects.create(
                actor=request.user, action="role_deleted", entity_type="Group",
                entity_id=str(group_id), description=f"Deleted role '{name}'.",
                metadata={"role_name": name},
            )
            messages.success(request, f"Role '{name}' deleted.")
            return redirect("role_management")

        group_id = request.POST.get("group_id", "").strip()
        name = request.POST.get("role_name", "").strip()
        change_note = request.POST.get("change_note", "").strip()
        selected = set(request.POST.getlist("permissions"))
        valid = {row["codename"] for row in catalog}

        if not name or len(name) > 150:
            messages.error(request, "Enter a role name of 1–150 characters.")
            return redirect("role_management" + (f"?edit={group_id}" if group_id else ""))
        if selected - valid:
            raise PermissionDenied("Invalid permission selection.")

        duplicates = Group.objects.filter(name__iexact=name)
        if group_id:
            duplicates = duplicates.exclude(pk=group_id)
        if duplicates.exists():
            messages.error(request, "A role with that name already exists.")
            return redirect("role_management" + (f"?edit={group_id}" if group_id else ""))

        with transaction.atomic():
            if group_id:
                group = get_object_or_404(Group, pk=group_id)
                previous_name = group.name
                group.name = name
                group.save()
                event_action = "role_updated"
            else:
                group = Group.objects.create(name=name)
                previous_name = ""
                event_action = "role_created"

            custom_permissions = Permission.objects.filter(
                codename__in=list(valid), content_type__app_label="core"
            )
            preserved = set(
                group.permissions.exclude(pk__in=custom_permissions.values("pk"))
                .values_list("pk", flat=True)
            )
            selected_ids = []
            for row in catalog:
                if row["codename"] not in selected:
                    continue
                content_type = ContentType.objects.get_for_model(row["model"])
                permission, _ = Permission.objects.get_or_create(
                    content_type=content_type,
                    codename=row["codename"],
                    defaults={"name": row["name"]},
                )
                if permission.name != row["name"]:
                    permission.name = row["name"]
                    permission.save(update_fields=["name"])
                selected_ids.append(permission.pk)

            group.permissions.set(list(preserved) + selected_ids)
            AuditEvent.objects.create(
                actor=request.user, action=event_action, entity_type="Group",
                entity_id=str(group.pk),
                description=f"{event_action.replace('_', ' ').title()}: {group.name}",
                metadata={
                    "role_name": group.name,
                    "previous_name": previous_name,
                    "change_note": change_note,
                    "permissions": sorted(selected),
                },
            )

        messages.success(request, f"Role '{group.name}' saved.")
        return redirect("role_management")

    return render(request, "accounts/role_management.html", {
        "groups": Group.objects.order_by("name"),
        "editing_group": editing_group,
        "sections": sections,
    })
