from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import Group
from django.core.exceptions import PermissionDenied
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_POST
from core.models import AuditEvent

User = get_user_model()

def _require_superuser(request):
    u = request.user
    if not u.is_authenticated or not u.is_active or not u.is_superuser:
        raise PermissionDenied("Only an active superuser may manage staff.")

def _audit(actor, action, user, description, metadata=None):
    AuditEvent.objects.create(actor=actor, action=action, entity_type="User",
        entity_id=str(user.pk), description=description, metadata=metadata or {})

@login_required
def staff_management(request):
    _require_superuser(request)
    return render(request, "accounts/staff_management.html", {
        "staff_users": User.objects.prefetch_related("groups").order_by("username"),
        "groups": Group.objects.order_by("name"),
    })

@login_required
@require_POST
def staff_create(request):
    _require_superuser(request)
    username = request.POST.get("username", "").strip()
    email = request.POST.get("email", "").strip()
    password = request.POST.get("temporary_password", "")
    selected = request.POST.getlist("groups")
    if not username or len(username) > 150:
        messages.error(request, "Enter a username of 1–150 characters."); return redirect("staff_management")
    if not password:
        messages.error(request, "Enter a temporary password."); return redirect("staff_management")
    if User.objects.filter(username__iexact=username).exists():
        messages.error(request, "That username is already in use."); return redirect("staff_management")
    if email and User.objects.filter(email__iexact=email).exclude(email="").exists():
        messages.error(request, "That email address is already assigned."); return redirect("staff_management")
    groups = list(Group.objects.filter(pk__in=selected))
    if len(groups) != len(set(selected)):
        messages.error(request, "One or more selected roles are invalid."); return redirect("staff_management")
    with transaction.atomic():
        user = User.objects.create_user(username=username, email=email, password=password,
                                        is_staff=True, is_active=True, is_superuser=False)
        user.groups.set(groups)
        _audit(request.user, "staff_created", user, f"Created staff account '{username}'.",
               {"username": username, "email": email, "roles": [g.name for g in groups]})
    messages.success(request, f"Staff account '{username}' created.")
    return redirect("staff_management")

@login_required
@require_POST
def staff_update_roles(request, user_id):
    _require_superuser(request)
    user = get_object_or_404(User, pk=user_id)
    if user.is_superuser:
        messages.error(request, "Superuser roles cannot be changed here."); return redirect("staff_management")
    selected = request.POST.getlist("groups")
    groups = list(Group.objects.filter(pk__in=selected))
    if len(groups) != len(set(selected)):
        messages.error(request, "One or more selected roles are invalid."); return redirect("staff_management")
    before = list(user.groups.order_by("name").values_list("name", flat=True))
    after = [g.name for g in groups]
    with transaction.atomic():
        user.groups.set(groups)
        _audit(request.user, "staff_roles_updated", user, f"Updated roles for '{user.username}'.",
               {"previous_roles": before, "roles": after})
    messages.success(request, f"Roles updated for '{user.username}'.")
    return redirect("staff_management")

@login_required
@require_POST
def staff_toggle_active(request, user_id):
    _require_superuser(request)
    user = get_object_or_404(User, pk=user_id)
    if user.pk == request.user.pk or user.is_superuser:
        messages.error(request, "You cannot deactivate this administrator here."); return redirect("staff_management")
    user.is_active = not user.is_active
    user.save(update_fields=["is_active"])
    state = "activated" if user.is_active else "deactivated"
    _audit(request.user, f"staff_{state}", user, f"{state.title()} staff account '{user.username}'.",
           {"username": user.username, "is_active": user.is_active})
    messages.success(request, f"Account '{user.username}' {state}.")
    return redirect("staff_management")

@login_required
@require_POST
def staff_reset_password(request, user_id):
    _require_superuser(request)
    user = get_object_or_404(User, pk=user_id)
    if user.is_superuser:
        messages.error(request, "Use the administrator password recovery process for superuser accounts.")
        return redirect("staff_management")
    password = request.POST.get("temporary_password", "")
    if not password:
        messages.error(request, "Enter a temporary password."); return redirect("staff_management")
    user.set_password(password)
    user.save(update_fields=["password"])
    _audit(request.user, "staff_password_reset", user, f"Reset password for '{user.username}'.",
           {"username": user.username, "reset_by": request.user.username})
    messages.success(request, f"Password reset for '{user.username}'. Share it securely.")
    return redirect("staff_management")
