"""WS5.13 — Staff-facing notification template editor.

The PRD calls for several "staff-configurable" surfaces — among them, per-verb
notification templates so operators can tune subject lines and body copy
without a code change. Today the rows are editable only via Django admin,
which is gated to ``is_staff`` users and isn't brand-aligned. This view
exposes the same data behind the IILMP shell so admins (and downstream
ops staff with the ``manage_users`` permission) can edit copy themselves.
"""
from __future__ import annotations

from django import forms
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404, redirect, render
from django.views import View
from django.views.generic import ListView

from apps.core.notifications.models import Notification, NotificationTemplate
from apps.core.audit.rep_helpers import action_update, audit_rep


def _can_edit_templates(user) -> bool:
    if not user.is_authenticated:
        return False
    if user.is_superuser or user.is_staff:
        return True
    return user.has_perm("core.change_notificationtemplate")


class _TemplateEditorMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return self.handle_no_permission()
        if not _can_edit_templates(request.user):
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)


class NotificationTemplateForm(forms.ModelForm):
    class Meta:
        model = NotificationTemplate
        fields = (
            "name",
            "subject",
            "headline_default",
            "action_label_default",
            "body_text",
            "body_html",
            "is_active",
        )
        widgets = {
            "name": forms.TextInput(),
            "subject": forms.TextInput(),
            "headline_default": forms.TextInput(),
            "action_label_default": forms.TextInput(),
            "body_text": forms.Textarea(attrs={"rows": 8, "spellcheck": "true"}),
            "body_html": forms.Textarea(attrs={"rows": 12, "spellcheck": "false", "class": "font-mono"}),
        }


class NotificationTemplateListView(_TemplateEditorMixin, ListView):
    """List every active notification verb, marked with whether a custom
    template row exists for it. The PRD's seeded set covers ~40 verbs, but
    new ones get added over time — listing from the enum keeps the page in
    sync without manual housekeeping."""

    template_name = "notifications/manage/template_list.html"
    context_object_name = "rows"
    paginate_by = 100

    def get_queryset(self):
        # Shape: list of (verb_value, label, NotificationTemplate or None).
        existing = {t.verb: t for t in NotificationTemplate.objects.all()}
        params = self.request.GET
        q = (params.get("q") or "").strip().lower()
        prefix = (params.get("prefix") or "").strip()
        rows = []
        for verb_value, label in Notification.Verb.choices:
            if prefix and not verb_value.startswith(prefix):
                continue
            if q and q not in verb_value.lower() and q not in str(label).lower():
                continue
            rows.append(
                {
                    "verb": verb_value,
                    "label": label,
                    "template": existing.get(verb_value),
                }
            )
        return rows

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["filter_q"] = self.request.GET.get("q", "")
        ctx["filter_prefix"] = self.request.GET.get("prefix", "")
        # Module prefix shortcuts so staff can jump straight to "all REP" etc.
        ctx["prefixes"] = [
            ("rep_", "REP"),
            ("rims_", "RIMS"),
            ("repo_", "Repository"),
            ("mel_", "M&EL"),
            ("alumni_", "Alumni"),
            ("smehub_", "SME-Hub"),
        ]
        ctx["seeded_count"] = NotificationTemplate.objects.filter(is_active=True).count()
        return ctx


class NotificationTemplateEditView(_TemplateEditorMixin, View):
    template_name = "notifications/manage/template_edit.html"

    def _get_or_init(self, verb: str) -> NotificationTemplate:
        """Return a row for ``verb`` — fetch the existing one or build (un-saved)
        from the seed defaults if no row exists yet."""
        existing = NotificationTemplate.objects.filter(verb=verb).first()
        if existing:
            return existing
        # Try to build from the seeded defaults so first-time edits start with
        # the canonical PRD-faithful copy rather than a blank form.
        from apps.core.notifications.default_templates import DEFAULT_TEMPLATES

        seed = next((e for e in DEFAULT_TEMPLATES if e["verb"] == verb), None)
        if seed:
            return NotificationTemplate(
                verb=verb,
                name=seed["name"],
                subject=seed["subject"],
                body_text=seed.get("body_text", ""),
                body_html=seed.get("body_html", ""),
                headline_default=seed.get("headline_default", ""),
                action_label_default=seed.get("action_label_default", "Open in IILMP"),
                is_active=True,
            )
        # New verb with no seed — start blank.
        label = dict(Notification.Verb.choices).get(verb, verb)
        return NotificationTemplate(verb=verb, name=str(label), subject="", is_active=True)

    def get(self, request, verb):
        if verb not in dict(Notification.Verb.choices):
            raise PermissionDenied  # not a real verb
        instance = self._get_or_init(verb)
        form = NotificationTemplateForm(instance=instance)
        return render(
            request,
            self.template_name,
            {"form": form, "instance": instance, "verb": verb},
        )

    def post(self, request, verb):
        if verb not in dict(Notification.Verb.choices):
            raise PermissionDenied
        instance = self._get_or_init(verb)
        form = NotificationTemplateForm(request.POST, instance=instance)
        if not form.is_valid():
            return render(
                request,
                self.template_name,
                {"form": form, "instance": instance, "verb": verb},
            )
        # Persist verb explicitly (form.fields excludes it; for new instances
        # we set it from the URL kwarg).
        instance.verb = verb
        for field in form.cleaned_data:
            setattr(instance, field, form.cleaned_data[field])
        instance.save()
        audit_rep(
            actor=request.user,
            action=action_update(),
            target_model="NotificationTemplate",
            object_id=instance.pk,
            object_repr=verb,
            changes={"changed_fields": sorted(form.changed_data)},
            request=request,
        )
        messages.success(request, f"Saved template for {verb}.")
        return redirect("core:notification_template_list")
