# views/unterstuetzungen.py
# Phase 0: Vision 2026 – Code-Refactoring

import csv
import io
import json
import os
import time
from datetime import datetime, timedelta, date
from decimal import Decimal

import qrcode
import qrcode.image.svg
import requests
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import (Avg, Count, DecimalField, F, IntegerField, Q,
                              Sum, Value)
from django.db.models.functions import Cast, Coalesce, NullIf, Replace
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt
from django_otp.decorators import otp_required
from django_otp.plugins.otp_totp.models import TOTPDevice
from django_otp.plugins.otp_static.models import StaticDevice, StaticToken
from django_otp.util import random_hex
from rest_framework.decorators import api_view
from rest_framework.response import Response

from stiftung.audit import log_action
from stiftung.models import (AppConfiguration, AuditLog, BackupJob, BankTransaction,
                     BriefVorlage, CSVImport, Destinataer,
                     DestinataerEmailEingang, DestinataerNotiz,
                     DestinataerUnterstuetzung,
                     DokumentLink, Foerderung, GeschichteBild, GeschichteSeite,
                     Land, LandAbrechnung, LandVerpachtung, Paechter, Person,
                     Rentmeister, StiftungsKalenderEintrag, StiftungsKonto,
                     UnterstuetzungWiederkehrend, Veranstaltung,
                     Veranstaltungsteilnehmer, Verwaltungskosten,
                     VierteljahresNachweis)
from stiftung.forms import (
    DestinataerForm, DestinataerUnterstuetzungForm, DestinataerNotizForm,
    FoerderungForm, GeschichteBildForm, GeschichteSeiteForm,
    LandForm, LandVerpachtungForm, LandAbrechnungForm,
    PaechterForm, DokumentLinkForm,
    RentmeisterForm, StiftungsKontoForm, VerwaltungskostenForm,
    BankTransactionForm, BankImportForm,
    UnterstuetzungForm, UnterstuetzungWiederkehrendForm,
    UnterstuetzungMarkAsPaidForm, VierteljahresNachweisForm,
    UserCreationForm, UserUpdateForm, PasswordChangeForm, UserPermissionForm,
    TwoFactorSetupForm, TwoFactorVerifyForm, TwoFactorDisableForm,
    BackupTokenRegenerateForm, PersonForm,
    VeranstaltungForm, VeranstaltungsteilnehmerForm,
)


@login_required
def unterstuetzungen_list(request):
    """Liste der Destinatärunterstützungen (Administration)."""
    status = request.GET.get("status", "")
    export_format = (
        request.POST.get("format")
        if request.method == "POST"
        else request.GET.get("format", "")
    )
    selected_ids_param = (
        request.POST.get("selected_entries", "")
        if request.method == "POST"
        else request.GET.get("selected_entries", "")
    )
    selected_ids = (
        [id for id in selected_ids_param.split(",") if id] if selected_ids_param else []
    )

    qs = DestinataerUnterstuetzung.objects.select_related(
        "destinataer", "konto", "ausgezahlt_von", "wiederkehrend_von"
    ).order_by("-faellig_am", "destinataer__nachname")

    if status:
        qs = qs.filter(status=status)

    # Enhanced CSV export with field selection
    if export_format == "csv":
        return export_unterstuetzungen_csv(request, qs, selected_ids)

    # Enhanced PDF export with corporate identity
    elif export_format == "pdf":
        return export_unterstuetzungen_pdf(request, qs, selected_ids)

    # Get quarterly confirmation statistics
    quarterly_stats = {}
    total_quarterly = VierteljahresNachweis.objects.count()
    for status_code, status_name in VierteljahresNachweis.STATUS_CHOICES:
        count = VierteljahresNachweis.objects.filter(status=status_code).count()
        quarterly_stats[status_code] = {
            'name': status_name,
            'count': count
        }

    context = {
        "unterstuetzungen": qs,
        "status_filter": status,
        "quarterly_stats": quarterly_stats,
        "total_quarterly": total_quarterly,
    }
    return render(request, "stiftung/unterstuetzungen_list.html", context)


def export_unterstuetzungen_csv(request, queryset, selected_ids=None):
    """Enhanced CSV export with field selection"""
    import csv
    from datetime import datetime

    from django.http import HttpResponse

    # If specific entries are selected, filter to only those
    if selected_ids:
        queryset = queryset.filter(id__in=selected_ids)

    # Get selected fields from request (handle both 'fields' and 'selected_fields' parameter names)
    selected_fields_param = ""
    if request.method == "POST":
        # Try 'fields' first (new format), then 'selected_fields' (legacy)
        fields_list = request.POST.getlist("fields")
        if fields_list:
            selected_fields_param = ",".join(fields_list)
        else:
            selected_fields_param = request.POST.get("selected_fields", "")
    else:
        # Try 'fields' first (new format), then 'selected_fields' (legacy)
        fields_list = request.GET.getlist("fields")
        if fields_list:
            selected_fields_param = ",".join(fields_list)
        else:
            selected_fields_param = request.GET.get("selected_fields", "")
    
    selected_fields = selected_fields_param.split(",") if selected_fields_param else []

    if not selected_fields:
        # Default field set
        selected_fields = [
            "destinataer_name",
            "betrag",
            "faellig_am",
            "empfaenger_iban",
            "verwendungszweck",
            "status",
            "empfaenger_name",
            "beschreibung",
        ]

    # Field definitions with headers and data extraction
    field_definitions = {
        # Core payment fields
        "id": ("ID", lambda u: str(u.id)),
        "betrag": ("Betrag (€)", lambda u: f"{u.betrag:.2f}"),
        "faellig_am": (
            "Fällig am",
            lambda u: u.faellig_am.strftime("%d.%m.%Y") if u.faellig_am else "",
        ),
        "status": ("Status", lambda u: u.get_status_display()),
        "beschreibung": ("Beschreibung", lambda u: u.beschreibung or ""),
        "ausgezahlt_am": (
            "Ausgezahlt am",
            lambda u: u.ausgezahlt_am.strftime("%d.%m.%Y") if u.ausgezahlt_am else "",
        ),
        "erstellt_am": (
            "Erstellt am",
            lambda u: u.erstellt_am.strftime("%d.%m.%Y %H:%M") if u.erstellt_am else "",
        ),
        "aktualisiert_am": (
            "Aktualisiert am",
            lambda u: (
                u.aktualisiert_am.strftime("%d.%m.%Y %H:%M")
                if u.aktualisiert_am
                else ""
            ),
        ),
        # Destinataer fields
        "destinataer_name": (
            "Destinatär Name",
            lambda u: u.destinataer.get_full_name() if u.destinataer else "",
        ),
        "destinataer_vorname": (
            "Vorname",
            lambda u: u.destinataer.vorname if u.destinataer else "",
        ),
        "destinataer_nachname": (
            "Nachname",
            lambda u: u.destinataer.nachname if u.destinataer else "",
        ),
        "familienzweig": (
            "Familienzweig",
            lambda u: u.destinataer.familienzweig if u.destinataer else "",
        ),
        "geburtsdatum": (
            "Geburtsdatum",
            lambda u: (
                u.destinataer.geburtsdatum.strftime("%d.%m.%Y")
                if u.destinataer and u.destinataer.geburtsdatum
                else ""
            ),
        ),
        "email": ("E-Mail", lambda u: u.destinataer.email if u.destinataer else ""),
        "telefon": (
            "Telefon",
            lambda u: u.destinataer.telefon if u.destinataer else "",
        ),
        "destinataer_iban": (
            "Destinatär IBAN",
            lambda u: u.destinataer.iban if u.destinataer else "",
        ),
        "strasse": ("Straße", lambda u: u.destinataer.strasse if u.destinataer else ""),
        "plz": ("PLZ", lambda u: u.destinataer.plz if u.destinataer else ""),
        "ort": ("Ort", lambda u: u.destinataer.ort if u.destinataer else ""),
        "adresse": (
            "Adresse",
            lambda u: (
                f"{u.destinataer.strasse}, {u.destinataer.plz} {u.destinataer.ort}".strip(
                    ", "
                )
                if u.destinataer
                else ""
            ),
        ),
        "berufsgruppe": (
            "Berufsgruppe",
            lambda u: u.destinataer.berufsgruppe if u.destinataer else "",
        ),
        "ausbildungsstand": (
            "Ausbildungsstand",
            lambda u: u.destinataer.ausbildungsstand if u.destinataer else "",
        ),
        "institution": (
            "Institution",
            lambda u: u.destinataer.institution if u.destinataer else "",
        ),
        "jaehrliches_einkommen": (
            "Jährliches Einkommen (€)",
            lambda u: (
                f"{u.destinataer.jaehrliches_einkommen:.2f}"
                if u.destinataer and u.destinataer.jaehrliches_einkommen
                else ""
            ),
        ),
        "haushaltsgroesse": (
            "Haushaltsgröße",
            lambda u: (
                str(u.destinataer.haushaltsgroesse)
                if u.destinataer and u.destinataer.haushaltsgroesse
                else ""
            ),
        ),
        "monatliche_bezuege": (
            "Monatliche Bezüge (€)",
            lambda u: (
                f"{u.destinataer.monatliche_bezuege:.2f}"
                if u.destinataer and u.destinataer.monatliche_bezuege
                else ""
            ),
        ),
        "vermoegen": (
            "Vermögen (€)",
            lambda u: (
                f"{u.destinataer.vermoegen:.2f}"
                if u.destinataer and u.destinataer.vermoegen
                else ""
            ),
        ),
        # Payment details
        "empfaenger_iban": ("Empfänger IBAN", lambda u: u.empfaenger_iban or ""),
        "empfaenger_name": ("Empfänger Name", lambda u: u.empfaenger_name or ""),
        "verwendungszweck": ("Verwendungszweck", lambda u: u.verwendungszweck or ""),
        # Account fields
        "konto_name": ("Konto", lambda u: str(u.konto) if u.konto else ""),
        "konto_bank": ("Bank", lambda u: u.konto.bank_name if u.konto else ""),
        "konto_iban": ("Konto IBAN", lambda u: u.konto.iban if u.konto else ""),
        # System fields
        "ausgezahlt_von": (
            "Ausgezahlt von",
            lambda u: u.ausgezahlt_von.get_full_name() if u.ausgezahlt_von else "",
        ),
        "ist_wiederkehrend": (
            "Wiederkehrend",
            lambda u: "Ja" if u.wiederkehrend_von else "Nein",
        ),
    }

    # Create CSV response
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"unterstuetzungen_{timestamp}.csv"

    response = HttpResponse(content_type="text/csv; charset=utf-8")
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response, delimiter=";", quoting=csv.QUOTE_ALL)

    # Write headers
    headers = [
        field_definitions[field][0]
        for field in selected_fields
        if field in field_definitions
    ]
    writer.writerow(headers)

    # Write data rows
    for u in queryset:
        row = []
        for field in selected_fields:
            if field in field_definitions:
                try:
                    value = field_definitions[field][1](u)
                    row.append(value)
                except Exception:
                    row.append("")  # Fallback for any errors
            else:
                row.append("")  # Unknown field
        writer.writerow(row)

    return response


def export_unterstuetzungen_pdf(request, queryset, selected_ids=None):
    """Enhanced PDF export with corporate identity and field selection"""
    # If specific entries are selected, filter to only those
    if selected_ids:
        queryset = queryset.filter(id__in=selected_ids)

    # Get selected fields from request (handle both 'fields' and 'selected_fields' parameter names)
    selected_fields_param = ""
    if request.method == "POST":
        # Try 'fields' first (new format), then 'selected_fields' (legacy)
        fields_list = request.POST.getlist("fields")
        if fields_list:
            selected_fields_param = ",".join(fields_list)
        else:
            selected_fields_param = request.POST.get("selected_fields", "")
    else:
        # Try 'fields' first (new format), then 'selected_fields' (legacy)
        fields_list = request.GET.getlist("fields")
        if fields_list:
            selected_fields_param = ",".join(fields_list)
        else:
            selected_fields_param = request.GET.get("selected_fields", "")
    
    selected_fields = selected_fields_param.split(",") if selected_fields_param else []

    if not selected_fields:
        # Default field set for PDF (fewer fields than CSV for better readability)
        selected_fields = [
            "destinataer_name",
            "betrag",
            "faellig_am",
            "empfaenger_iban",
            "verwendungszweck", 
            "status",
            "beschreibung",
            "ausgezahlt_am",
        ]

    # Field definitions with display names (reuse from CSV but select PDF-appropriate subset)
    field_definitions = {
        # Core payment fields
        "destinataer_name": "Destinatär",
        "betrag": "Betrag (€)",
        "faellig_am": "Fällig am",
        "status": "Status",
        "beschreibung": "Beschreibung",
        "ausgezahlt_am": "Ausgezahlt am",
        "erstellt_am": "Erstellt am",
        "empfaenger_iban": "Empfänger IBAN",
        "empfaenger_name": "Empfänger",
        "verwendungszweck": "Verwendungszweck",
        "konto_name": "Konto",
        "ist_wiederkehrend": "Wiederkehrend",
    }

    # Filter to only include fields that are both selected and defined
    filtered_fields = {
        k: v for k, v in field_definitions.items() if k in selected_fields
    }

    # Prepare data with field extraction logic
    data_for_pdf = []
    for item in queryset:
        row_data = {}
        for field_key in filtered_fields.keys():
            try:
                if field_key == "destinataer_name":
                    row_data[field_key] = (
                        item.destinataer.get_full_name() if item.destinataer else ""
                    )
                elif field_key == "betrag":
                    row_data[field_key] = f"€{item.betrag:.2f}" if item.betrag else ""
                elif field_key == "faellig_am":
                    row_data[field_key] = (
                        item.faellig_am.strftime("%d.%m.%Y") if item.faellig_am else ""
                    )
                elif field_key == "status":
                    row_data[field_key] = item.get_status_display()
                elif field_key == "beschreibung":
                    row_data[field_key] = item.beschreibung or ""
                elif field_key == "ausgezahlt_am":
                    row_data[field_key] = (
                        item.ausgezahlt_am.strftime("%d.%m.%Y")
                        if item.ausgezahlt_am
                        else ""
                    )
                elif field_key == "erstellt_am":
                    row_data[field_key] = (
                        item.erstellt_am.strftime("%d.%m.%Y")
                        if item.erstellt_am
                        else ""
                    )
                elif field_key == "empfaenger_iban":
                    row_data[field_key] = item.empfaenger_iban or ""
                elif field_key == "empfaenger_name":
                    row_data[field_key] = item.empfaenger_name or ""
                elif field_key == "verwendungszweck":
                    row_data[field_key] = item.verwendungszweck or ""
                elif field_key == "konto_name":
                    row_data[field_key] = str(item.konto) if item.konto else ""
                elif field_key == "ist_wiederkehrend":
                    row_data[field_key] = "Ja" if item.wiederkehrend_von else "Nein"
                else:
                    # Generic field access
                    row_data[field_key] = getattr(item, field_key, "") or ""
            except Exception:
                row_data[field_key] = ""  # Fallback for any errors

        data_for_pdf.append(row_data)

    # Use PDF generator
    pdf_gen = get_pdf_generator()
    return pdf_gen.export_data_list_pdf(
        data=data_for_pdf,
        fields_config=filtered_fields,
        title="Unterstützungen Export",
        filename_prefix="unterstuetzungen",
        request_user=request.user,
    )


def export_foerderungen_csv(request, queryset, selected_ids=None):
    """Enhanced CSV export for Förderungen with field selection"""
    import csv
    from datetime import datetime

    from django.http import HttpResponse

    # If specific entries are selected, filter to only those
    if selected_ids:
        queryset = queryset.filter(id__in=selected_ids)

    # Get selected fields from request (default to all if none specified)
    selected_fields_param = (
        request.POST.get("selected_fields", "")
        if request.method == "POST"
        else request.GET.get("selected_fields", "")
    )
    selected_fields = selected_fields_param.split(",") if selected_fields_param else []

    if not selected_fields:
        # Default field set
        selected_fields = [
            "destinataer_name",
            "jahr",
            "betrag",
            "kategorie",
            "status",
            "antragsdatum",
            "beschreibung",
        ]

    # Field definitions with headers and data extraction
    field_definitions = {
        # Core fields
        "id": ("ID", lambda f: str(f.id)),
        "destinataer_name": (
            "Destinatär Name",
            lambda f: f.destinataer.get_full_name() if f.destinataer else "",
        ),
        "jahr": ("Jahr", lambda f: str(f.jahr)),
        "betrag": ("Betrag (€)", lambda f: f"{f.betrag:.2f}"),
        "kategorie": ("Kategorie", lambda f: f.get_kategorie_display()),
        "status": ("Status", lambda f: f.get_status_display()),
        "antragsdatum": (
            "Antragsdatum",
            lambda f: f.antragsdatum.strftime("%d.%m.%Y") if f.antragsdatum else "",
        ),
        "bewilligungsdatum": (
            "Bewilligungsdatum",
            lambda f: (
                f.bewilligungsdatum.strftime("%d.%m.%Y") if f.bewilligungsdatum else ""
            ),
        ),
        "auszahlungsdatum": (
            "Auszahlungsdatum",
            lambda f: (
                f.auszahlungsdatum.strftime("%d.%m.%Y") if f.auszahlungsdatum else ""
            ),
        ),
        "beschreibung": ("Beschreibung", lambda f: f.beschreibung or ""),
        "begruendung": ("Begründung", lambda f: f.begruendung or ""),
        "verwendungsnachweis_datum": (
            "Verwendungsnachweis Datum",
            lambda f: (
                f.verwendungsnachweis_datum.strftime("%d.%m.%Y")
                if f.verwendungsnachweis_datum
                else ""
            ),
        ),
        "verwendungsnachweis_status": (
            "Verwendungsnachweis Status",
            lambda f: (
                f.get_verwendungsnachweis_status_display()
                if f.verwendungsnachweis_status
                else ""
            ),
        ),
        # Destinataer fields
        "destinataer_vorname": (
            "Vorname",
            lambda f: f.destinataer.vorname if f.destinataer else "",
        ),
        "destinataer_nachname": (
            "Nachname",
            lambda f: f.destinataer.nachname if f.destinataer else "",
        ),
        "familienzweig": (
            "Familienzweig",
            lambda f: f.destinataer.familienzweig if f.destinataer else "",
        ),
        "email": ("E-Mail", lambda f: f.destinataer.email if f.destinataer else ""),
        "telefon": (
            "Telefon",
            lambda f: f.destinataer.telefon if f.destinataer else "",
        ),
        "adresse": (
            "Adresse",
            lambda f: (
                f"{f.destinataer.strasse}, {f.destinataer.plz} {f.destinataer.ort}".strip(
                    ", "
                )
                if f.destinataer
                else ""
            ),
        ),
        "berufsgruppe": (
            "Berufsgruppe",
            lambda f: f.destinataer.berufsgruppe if f.destinataer else "",
        ),
        "ausbildungsstand": (
            "Ausbildungsstand",
            lambda f: f.destinataer.ausbildungsstand if f.destinataer else "",
        ),
        "institution": (
            "Institution",
            lambda f: f.destinataer.institution if f.destinataer else "",
        ),
        # System fields
        "erstellt_am": (
            "Erstellt am",
            lambda f: f.erstellt_am.strftime("%d.%m.%Y %H:%M") if f.erstellt_am else "",
        ),
        "aktualisiert_am": (
            "Aktualisiert am",
            lambda f: (
                f.aktualisiert_am.strftime("%d.%m.%Y %H:%M")
                if f.aktualisiert_am
                else ""
            ),
        ),
    }

    # Create CSV response
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"foerderungen_{timestamp}.csv"

    response = HttpResponse(content_type="text/csv; charset=utf-8")
    response["Content-Disposition"] = f'attachment; filename="{filename}"'

    writer = csv.writer(response, delimiter=";", quoting=csv.QUOTE_ALL)

    # Write headers
    headers = [
        field_definitions[field][0]
        for field in selected_fields
        if field in field_definitions
    ]
    writer.writerow(headers)

    # Write data rows
    for f in queryset:
        row = []
        for field in selected_fields:
            if field in field_definitions:
                try:
                    value = field_definitions[field][1](f)
                    row.append(value)
                except Exception:
                    row.append("")  # Fallback for any errors
            else:
                row.append("")  # Unknown field
        writer.writerow(row)

    return response


def export_foerderungen_pdf(request, queryset, selected_ids=None):
    """Enhanced PDF export for Förderungen with corporate identity and field selection"""
    # If specific entries are selected, filter to only those
    if selected_ids:
        queryset = queryset.filter(id__in=selected_ids)

    # Get selected fields from request (default to key fields if none specified)
    selected_fields_param = (
        request.POST.get("selected_fields", "")
        if request.method == "POST"
        else request.GET.get("selected_fields", "")
    )
    selected_fields = selected_fields_param.split(",") if selected_fields_param else []

    if not selected_fields:
        # Default field set for PDF (fewer fields than CSV for better readability)
        selected_fields = [
            "destinataer_name",
            "jahr",
            "betrag",
            "kategorie",
            "status",
            "antragsdatum",
        ]

    # Field definitions with display names
    field_definitions = {
        "destinataer_name": "Destinatär",
        "jahr": "Jahr",
        "betrag": "Betrag (€)",
        "kategorie": "Kategorie",
        "status": "Status",
        "antragsdatum": "Antragsdatum",
        "bewilligungsdatum": "Bewilligungsdatum",
        "auszahlungsdatum": "Auszahlungsdatum",
        "beschreibung": "Beschreibung",
        "begruendung": "Begründung",
        "verwendungsnachweis_status": "Verwendungsnachweis",
    }

    # Filter to only include fields that are both selected and defined
    filtered_fields = {
        k: v for k, v in field_definitions.items() if k in selected_fields
    }

    # Prepare data with field extraction logic
    data_for_pdf = []
    for item in queryset:
        row_data = {}
        for field_key in filtered_fields.keys():
            try:
                if field_key == "destinataer_name":
                    row_data[field_key] = (
                        item.destinataer.get_full_name() if item.destinataer else ""
                    )
                elif field_key == "jahr":
                    row_data[field_key] = str(item.jahr)
                elif field_key == "betrag":
                    row_data[field_key] = f"€{item.betrag:.2f}" if item.betrag else ""
                elif field_key == "kategorie":
                    row_data[field_key] = item.get_kategorie_display()
                elif field_key == "status":
                    row_data[field_key] = item.get_status_display()
                elif field_key == "antragsdatum":
                    row_data[field_key] = (
                        item.antragsdatum.strftime("%d.%m.%Y")
                        if item.antragsdatum
                        else ""
                    )
                elif field_key == "bewilligungsdatum":
                    row_data[field_key] = (
                        item.bewilligungsdatum.strftime("%d.%m.%Y")
                        if item.bewilligungsdatum
                        else ""
                    )
                elif field_key == "auszahlungsdatum":
                    row_data[field_key] = (
                        item.auszahlungsdatum.strftime("%d.%m.%Y")
                        if item.auszahlungsdatum
                        else ""
                    )
                elif field_key == "beschreibung":
                    row_data[field_key] = (item.beschreibung or "")[:100] + (
                        "..." if len(item.beschreibung or "") > 100 else ""
                    )
                elif field_key == "begruendung":
                    row_data[field_key] = (item.begruendung or "")[:100] + (
                        "..." if len(item.begruendung or "") > 100 else ""
                    )
                elif field_key == "verwendungsnachweis_status":
                    row_data[field_key] = (
                        item.get_verwendungsnachweis_status_display()
                        if item.verwendungsnachweis_status
                        else ""
                    )
                else:
                    # Generic field access
                    row_data[field_key] = getattr(item, field_key, "") or ""
            except Exception:
                row_data[field_key] = ""  # Fallback for any errors

        data_for_pdf.append(row_data)

    # Use PDF generator
    pdf_gen = get_pdf_generator()
    return pdf_gen.export_data_list_pdf(
        data=data_for_pdf,
        fields_config=filtered_fields,
        title="Förderungen Export",
        filename_prefix="foerderungen",
        request_user=request.user,
    )


@login_required
def unterstuetzung_edit(request, pk):
    obj = get_object_or_404(DestinataerUnterstuetzung, pk=pk)
    if request.method == "POST":
        form = DestinataerUnterstuetzungForm(request.POST, instance=obj)
        if form.is_valid():
            form.save()
            messages.success(request, "Unterstützung aktualisiert.")
            return redirect("stiftung:unterstuetzungen_list")
    else:
        form = DestinataerUnterstuetzungForm(instance=obj)
    return render(
        request,
        "stiftung/unterstuetzung_form.html",
        {"form": form, "title": "Unterstützung bearbeiten"},
    )


@login_required
def unterstuetzung_delete(request, pk):
    obj = get_object_or_404(DestinataerUnterstuetzung, pk=pk)

    # Check if this will also delete the recurring template
    will_delete_template = False
    if obj.wiederkehrend_von:
        andere_zahlungen = (
            DestinataerUnterstuetzung.objects.filter(
                wiederkehrend_von=obj.wiederkehrend_von
            )
            .exclude(pk=pk)
            .exists()
        )
        will_delete_template = not andere_zahlungen

    if request.method == "POST":
        # Check if this support payment is linked to a recurring payment template
        wiederkehrend_template = obj.wiederkehrend_von

        # Delete the support payment
        obj.delete()

        # If this was generated from a recurring template and there are no other
        # payments from this template, delete the template too
        if wiederkehrend_template:
            # Check if there are other payments from this recurring template
            andere_zahlungen = DestinataerUnterstuetzung.objects.filter(
                wiederkehrend_von=wiederkehrend_template
            ).exists()

            # If no other payments exist from this template, delete the template too
            if not andere_zahlungen:
                wiederkehrend_template.delete()
                messages.success(
                    request,
                    "Unterstützung und wiederkehrende Zahlungsvorlage gelöscht.",
                )
            else:
                messages.success(request, "Unterstützung gelöscht.")
        else:
            messages.success(request, "Unterstützung gelöscht.")

        return redirect("stiftung:unterstuetzungen_list")

    context = {
        "obj": obj,
        "will_delete_template": will_delete_template,
    }
    return render(request, "stiftung/unterstuetzung_confirm_delete.html", context)


@login_required
def unterstuetzungen_all(request):
    """List all support payments - destinataer-focused view"""
    status = request.GET.get("status")
    destinataer_id = request.GET.get("destinataer")
    export = request.GET.get("format", "")
    selected_ids = (
        request.POST.getlist("selected_entries") if request.method == "POST" else []
    )

    unterstuetzungen = DestinataerUnterstuetzung.objects.select_related(
        "destinataer", "konto", "ausgezahlt_von", "wiederkehrend_von"
    ).order_by("-faellig_am")

    # Filtering
    if status:
        unterstuetzungen = unterstuetzungen.filter(status=status)
    if destinataer_id:
        unterstuetzungen = unterstuetzungen.filter(destinataer_id=destinataer_id)

    # Enhanced CSV export with field selection
    if export == "csv":
        return export_unterstuetzungen_csv(request, unterstuetzungen, selected_ids)

    # PDF export (simple table via WeasyPrint; graceful fallback if missing)
    if export == "pdf":
        try:
            from django.template.loader import render_to_string
            from weasyprint import HTML

            html = render_to_string(
                "stiftung/unterstuetzungen_pdf.html",
                {"unterstuetzungen": unterstuetzungen},
            )
            from django.http import HttpResponse

            pdf = HTML(string=html).write_pdf()
            resp = HttpResponse(pdf, content_type="application/pdf")
            resp["Content-Disposition"] = "inline; filename=unterstuetzungen.pdf"
            return resp
        except Exception:
            pass

    # Statistics
    total_betrag = unterstuetzungen.aggregate(total=Sum("betrag"))["total"] or 0
    
    # Get quarterly confirmation statistics
    quarterly_stats = {}
    total_quarterly = VierteljahresNachweis.objects.count()
    for status_code, status_name in VierteljahresNachweis.STATUS_CHOICES:
        count = VierteljahresNachweis.objects.filter(status=status_code).count()
        quarterly_stats[status_code] = {
            'name': status_name,
            'count': count
        }

    # Available destinataer for filter
    destinataer = Destinataer.objects.all().order_by("nachname", "vorname")

    context = {
        "page_obj": unterstuetzungen,  # Use directly for now (pagination can be added later)
        "unterstuetzungen": unterstuetzungen,
        "title": "Alle Unterstützungen",
        "status_filter": status,
        "total_betrag": total_betrag,
        "quarterly_stats": quarterly_stats,
        "total_quarterly": total_quarterly,
        "status_choices": DestinataerUnterstuetzung.STATUS_CHOICES,
        "destinataer": destinataer,
    }
    return render(request, "stiftung/unterstuetzungen_all.html", context)


@login_required
def unterstuetzung_create(request):
    """Create a new support payment"""
    # Get destinataer from URL parameter if provided
    destinataer_id = request.GET.get("destinataer")
    initial = {}
    if destinataer_id:
        initial["destinataer"] = destinataer_id
        # Pre-populate IBAN and name if destinataer is specified
        try:
            destinataer = Destinataer.objects.get(pk=destinataer_id)
            if hasattr(destinataer, "iban") and destinataer.iban:
                initial["empfaenger_iban"] = destinataer.iban
            initial["empfaenger_name"] = destinataer.get_full_name()
        except Destinataer.DoesNotExist:
            pass

    if request.method == "POST":
        form = UnterstuetzungForm(request.POST)
        if form.is_valid():
            ist_wiederkehrend = form.cleaned_data.get("ist_wiederkehrend", False)

            if ist_wiederkehrend:
                # Create recurring payment template
                wiederkehrend = UnterstuetzungWiederkehrend.objects.create(
                    destinataer=form.cleaned_data["destinataer"],
                    konto=form.cleaned_data["konto"],
                    betrag=form.cleaned_data["betrag"],
                    intervall=form.cleaned_data["intervall"],
                    beschreibung=form.cleaned_data["beschreibung"],
                    empfaenger_iban=form.cleaned_data["empfaenger_iban"],
                    empfaenger_name=form.cleaned_data["empfaenger_name"],
                    verwendungszweck=form.cleaned_data["verwendungszweck"],
                    erste_zahlung_am=form.cleaned_data["faellig_am"],
                    letzte_zahlung_am=form.cleaned_data.get("letzte_zahlung_am"),
                    naechste_generierung=form.cleaned_data["faellig_am"],
                    erstellt_von=request.user,
                )

                # Create the first payment
                unterstuetzung = form.save(commit=False)
                unterstuetzung.wiederkehrend_von = wiederkehrend
                unterstuetzung.save()

                messages.success(
                    request,
                    f"Wiederkehrende Unterstützung für {unterstuetzung.destinataer} wurde erfolgreich erstellt. Die erste Zahlung ist am {unterstuetzung.faellig_am} fällig.",
                )
            else:
                # Create single payment
                unterstuetzung = form.save()
                messages.success(
                    request,
                    f"Unterstützung für {unterstuetzung.destinataer} wurde erfolgreich erstellt.",
                )

            return redirect("stiftung:unterstuetzung_detail", pk=unterstuetzung.pk)
    else:
        form = UnterstuetzungForm(initial=initial)

    context = {
        "form": form,
        "title": "Neue Unterstützung erstellen",
    }
    return render(request, "stiftung/unterstuetzung_form.html", context)


@login_required
def get_destinataer_info(request, destinataer_id):
    """AJAX endpoint to get Destinataer IBAN and name information"""
    try:
        destinataer = Destinataer.objects.get(pk=destinataer_id)
        data = {
            "success": True,
            "name": destinataer.get_full_name(),
            "iban": getattr(destinataer, "iban", "") or "",
        }
    except Destinataer.DoesNotExist:
        data = {"success": False, "error": "Destinataer not found"}

    return JsonResponse(data)


@login_required
def unterstuetzung_detail(request, pk):
    """View support payment details"""
    unterstuetzung = get_object_or_404(DestinataerUnterstuetzung, pk=pk)

    # Check if this payment can be marked as paid
    can_mark_paid = unterstuetzung.can_be_marked_paid()

    context = {
        "unterstuetzung": unterstuetzung,
        "title": f"Unterstützung für {unterstuetzung.destinataer.get_full_name()}",
        "can_mark_paid": can_mark_paid,
    }
    return render(request, "stiftung/unterstuetzung_detail.html", context)


@login_required
def unterstuetzung_mark_paid(request, pk):
    """Mark a support payment as paid"""
    unterstuetzung = get_object_or_404(DestinataerUnterstuetzung, pk=pk)

    if not unterstuetzung.can_be_marked_paid():
        messages.error(
            request, "Diese Unterstützung kann nicht als bezahlt markiert werden."
        )
        return redirect("stiftung:unterstuetzung_detail", pk=pk)

    if request.method == "POST":
        form = UnterstuetzungMarkAsPaidForm(request.POST)
        if form.is_valid():
            unterstuetzung.status = "ausgezahlt"
            unterstuetzung.ausgezahlt_am = form.cleaned_data["ausgezahlt_am"]
            unterstuetzung.ausgezahlt_von = request.user

            # Add optional note to description
            bemerkung = form.cleaned_data.get("bemerkung")
            if bemerkung:
                if unterstuetzung.beschreibung:
                    unterstuetzung.beschreibung += f" | Zahlung: {bemerkung}"
                else:
                    unterstuetzung.beschreibung = f"Zahlung: {bemerkung}"

            unterstuetzung.save()
            messages.success(request, f"Unterstützung wurde als bezahlt markiert.")
            return redirect("stiftung:unterstuetzung_detail", pk=pk)
    else:
        form = UnterstuetzungMarkAsPaidForm()

    context = {
        "form": form,
        "unterstuetzung": unterstuetzung,
        "title": f"Zahlung markieren - {unterstuetzung.destinataer.get_full_name()}",
    }
    return render(request, "stiftung/unterstuetzung_mark_paid.html", context)


@login_required
def wiederkehrende_unterstuetzungen(request):
    """List all recurring support payment templates"""
    from django.db.models import Count

    # Check for cleanup request
    if request.GET.get("cleanup") == "1":
        # Find templates with no associated payments
        verwaiste_templates = UnterstuetzungWiederkehrend.objects.annotate(
            zahlung_count=Count("destinataerunterstuetzung")
        ).filter(zahlung_count=0)

        if verwaiste_templates.exists():
            anzahl_geloescht = verwaiste_templates.count()
            template_namen = list(
                verwaiste_templates.values_list("destinataer__nachname", flat=True)
            )
            verwaiste_templates.delete()
            messages.success(
                request,
                f'{anzahl_geloescht} verwaiste Zahlungsvorlagen bereinigt: {", ".join(template_namen[:5])}{"..." if len(template_namen) > 5 else ""}',
            )
        else:
            messages.info(request, "Keine verwaisten Zahlungsvorlagen gefunden.")

        return redirect("stiftung:wiederkehrende_unterstuetzungen")

    # Get all templates with payment counts
    templates = (
        UnterstuetzungWiederkehrend.objects.select_related("destinataer", "konto")
        .annotate(aktive_zahlungen=Count("destinataerunterstuetzung"))
        .all()
    )

    context = {
        "templates": templates,
        "title": "Wiederkehrende Unterstützungen",
    }
    return render(request, "stiftung/wiederkehrende_unterstuetzungen.html", context)


@login_required
def quarterly_confirmation_update(request, pk):
    """Update quarterly confirmation for destinataer"""
    nachweis = get_object_or_404(VierteljahresNachweis, pk=pk)
    
    if request.method == "POST":
        form = VierteljahresNachweisForm(request.POST, request.FILES, instance=nachweis)
        if form.is_valid():
            quarterly_proof = form.save(commit=False)
            
            # Calculate current status before saving
            old_status = nachweis.status
            
            # Auto-update status based on completion
            if quarterly_proof.is_complete():
                if quarterly_proof.status in ['offen', 'teilweise']:
                    quarterly_proof.status = 'eingereicht'
                    quarterly_proof.eingereicht_am = timezone.now()
            else:
                # If not complete, set to teilweise if some fields are filled
                has_partial_data = (
                    quarterly_proof.einkommenssituation_bestaetigt or 
                    quarterly_proof.vermogenssituation_bestaetigt or
                    quarterly_proof.studiennachweis_eingereicht
                )
                if has_partial_data and quarterly_proof.status == 'offen':
                    quarterly_proof.status = 'teilweise'
            
            quarterly_proof.save()
            
            # Try to create automatic support payment if complete
            if quarterly_proof.is_complete() and quarterly_proof.status == 'eingereicht':
                support_payment = create_quarterly_support_payment(quarterly_proof)
                if support_payment:
                    messages.success(
                        request,
                        f"Automatische Unterstützung über {support_payment.betrag}€ für {support_payment.destinataer.get_full_name()} wurde erstellt (fällig am {support_payment.faellig_am.strftime('%d.%m.%Y')})."
                    )
                else:
                    # Log why payment wasn't created
                    reasons = []
                    if not quarterly_proof.destinataer.vierteljaehrlicher_betrag:
                        reasons.append("kein vierteljährlicher Betrag hinterlegt")
                    if not quarterly_proof.destinataer.iban:
                        reasons.append("keine IBAN hinterlegt")
                    if not quarterly_proof.destinataer.standard_konto and not StiftungsKonto.objects.exists():
                        reasons.append("kein Auszahlungskonto verfügbar")
                    
                    if reasons:
                        messages.warning(
                            request,
                            f"Automatische Unterstützung konnte nicht erstellt werden: {', '.join(reasons)}"
                        )
            
            # Debug message to see what happened
            status_changed = old_status != quarterly_proof.status
            status_msg = f" (Status: {old_status} → {quarterly_proof.status})" if status_changed else f" (Status: {quarterly_proof.status})"
            
            messages.success(
                request,
                f"Vierteljahresnachweis für {nachweis.destinataer.get_full_name()} "
                f"({nachweis.jahr} Q{nachweis.quartal}) wurde erfolgreich aktualisiert{status_msg}."
            )
            return redirect("stiftung:destinataer_detail", pk=nachweis.destinataer.pk)
        else:
            # Add form errors to messages
            for field, errors in form.errors.items():
                for error in errors:
                    messages.error(request, f"Fehler in {field}: {error}")
    
    # If GET request or form errors, redirect back to destinataer detail
    return redirect("stiftung:destinataer_detail", pk=nachweis.destinataer.pk)


def create_quarterly_support_payment(nachweis):
    """
    Get or create a single support payment for this quarterly confirmation
    Ensures only one payment exists per destinataer per quarter
    """
    from datetime import date
    destinataer = nachweis.destinataer
    
    # Check if all requirements are met
    if not nachweis.is_complete():
        return None
    
    # Check if destinataer has required payment info
    if not destinataer.vierteljaehrlicher_betrag or destinataer.vierteljaehrlicher_betrag <= 0:
        return None
    
    if not destinataer.iban:
        return None
    
    # Search for existing payment using payment due date from quarterly confirmation
    # This is more accurate than using quarter date range, especially for Q1 (Dec 15 prev year)
    payment_due_date = nachweis.zahlung_faelligkeitsdatum
    if not payment_due_date:
        # Fallback: calculate if not set
        if nachweis.quartal == 1:
            payment_due_date = date(nachweis.jahr - 1, 12, 15)
        elif nachweis.quartal == 2:
            payment_due_date = date(nachweis.jahr, 3, 15)
        elif nachweis.quartal == 3:
            payment_due_date = date(nachweis.jahr, 6, 15)
        else:  # Q4
            payment_due_date = date(nachweis.jahr, 9, 15)
    
    # Search for existing payment - match by payment due date and description
    # Use a date range around the due date (±30 days) to catch any variations
    from datetime import timedelta
    date_start = payment_due_date - timedelta(days=30)
    date_end = payment_due_date + timedelta(days=30)
    
    existing_payment = DestinataerUnterstuetzung.objects.filter(
        destinataer=destinataer,
        faellig_am__gte=date_start,
        faellig_am__lte=date_end
    ).filter(
        Q(beschreibung__contains=f"Q{nachweis.quartal}/{nachweis.jahr}") |
        Q(beschreibung__contains=f"Vierteljährliche Unterstützung Q{nachweis.quartal}/{nachweis.jahr}")
    ).first()
    
    if existing_payment:
        # Update existing payment to ensure it matches current requirements
        existing_payment.betrag = destinataer.vierteljaehrlicher_betrag
        existing_payment.empfaenger_iban = destinataer.iban
        existing_payment.empfaenger_name = destinataer.get_full_name()
        existing_payment.verwendungszweck = f"Vierteljährliche Unterstützung Q{nachweis.quartal}/{nachweis.jahr} - {destinataer.get_full_name()}"
        existing_payment.beschreibung = f"Vierteljährliche Unterstützung Q{nachweis.quartal}/{nachweis.jahr} (automatisch erstellt)"
        existing_payment.save()
        return existing_payment
    
    # Get default payment account
    default_konto = destinataer.standard_konto
    if not default_konto:
        # Try to get any StiftungsKonto
        default_konto = StiftungsKonto.objects.first()
        if not default_konto:
            return None
    
    # Use payment due date from quarterly confirmation (already calculated by model)
    # This ensures consistency with zahlung_faelligkeitsdatum
    payment_due_date = nachweis.zahlung_faelligkeitsdatum
    if not payment_due_date:
        # Fallback: calculate if not set (should not happen, but safety check)
        if nachweis.quartal == 1:  # Q1 payment due December 15 of previous year
            payment_due_date = date(nachweis.jahr - 1, 12, 15)
        elif nachweis.quartal == 2:  # Q2 payment due March 15
            payment_due_date = date(nachweis.jahr, 3, 15)
        elif nachweis.quartal == 3:  # Q3 payment due June 15
            payment_due_date = date(nachweis.jahr, 6, 15)
        else:  # Q4 payment due September 15
            payment_due_date = date(nachweis.jahr, 9, 15)
    
    # Create the support payment
    payment = DestinataerUnterstuetzung.objects.create(
        destinataer=destinataer,
        konto=default_konto,
        betrag=destinataer.vierteljaehrlicher_betrag,
        faellig_am=payment_due_date,
        status='geplant',
        beschreibung=f"Vierteljährliche Unterstützung Q{nachweis.quartal}/{nachweis.jahr} (automatisch erstellt)",
        empfaenger_iban=destinataer.iban,
        empfaenger_name=destinataer.get_full_name(),
        verwendungszweck=f"Vierteljährliche Unterstützung Q{nachweis.quartal}/{nachweis.jahr} - {destinataer.get_full_name()}",
        erstellt_am=timezone.now(),
        aktualisiert_am=timezone.now()
    )
    
    return payment


@login_required
def quarterly_confirmation_create(request, destinataer_id):
    """Create a new quarterly confirmation for a destinataer"""
    import logging
    logger = logging.getLogger(__name__)
    logger.info(f"quarterly_confirmation_create called: method={request.method}, destinataer_id={destinataer_id}")
    
    destinataer = get_object_or_404(Destinataer, pk=destinataer_id)
    
    if request.method == "POST":
        logger.info(f"POST data: {request.POST}")
        jahr = request.POST.get('jahr')
        quartal = request.POST.get('quartal')
        
        if jahr and quartal:
            try:
                jahr = int(jahr)
                quartal = int(quartal)
                
                # Check if this quarter already exists
                existing = VierteljahresNachweis.objects.filter(
                    destinataer=destinataer,
                    jahr=jahr,
                    quartal=quartal
                ).exists()
                
                if existing:
                    messages.warning(
                        request,
                        f"Quartal {jahr} Q{quartal} existiert bereits für {destinataer.get_full_name()}."
                    )
                else:
                    # Create new quarterly confirmation
                    try:
                        nachweis = VierteljahresNachweis.objects.create(
                            destinataer=destinataer,
                            jahr=jahr,
                            quartal=quartal,
                            studiennachweis_erforderlich=True,  # Always required now
                        )
                        # Deadlines are automatically set by the model's save() method
                        # studiennachweis_faelligkeitsdatum: semester-based (Q1/Q2→Mar 15, Q3/Q4→Sep 15)
                        # zahlung_faelligkeitsdatum: quarterly advance (Q1→Dec 15 prev year, Q2→Mar 15, Q3→Jun 15, Q4→Sep 15)
                        
                        # Refresh from database to ensure deadlines are set
                        nachweis.refresh_from_db()
                        
                        studiennachweis_str = nachweis.studiennachweis_faelligkeitsdatum.strftime('%d.%m.%Y') if nachweis.studiennachweis_faelligkeitsdatum else "Nicht gesetzt"
                        zahlung_str = nachweis.zahlung_faelligkeitsdatum.strftime('%d.%m.%Y') if nachweis.zahlung_faelligkeitsdatum else "Nicht gesetzt"
                        
                        messages.success(
                            request,
                            f"Quartal {jahr} Q{quartal} wurde erfolgreich für {destinataer.get_full_name()} erstellt. "
                            f"Studiennachweis fällig: {studiennachweis_str}, "
                            f"Zahlung fällig: {zahlung_str}."
                        )
                    except Exception as e:
                        from django.db import IntegrityError
                        if isinstance(e, IntegrityError):
                            messages.error(
                                request,
                                f"Quartal {jahr} Q{quartal} existiert bereits für {destinataer.get_full_name()}."
                            )
                        else:
                            messages.error(
                                request,
                                f"Fehler beim Erstellen des Quartals: {str(e)}"
                            )
                    
            except (ValueError, TypeError):
                messages.error(request, "Ungültige Jahr- oder Quartalswerte.")
        else:
            messages.error(request, "Jahr und Quartal müssen angegeben werden.")
    
    return redirect("stiftung:destinataer_detail", pk=destinataer.pk)


@login_required
def quarterly_confirmation_edit(request, pk):
    """Standalone edit view for quarterly confirmation"""
    from stiftung.models import DokumentDatei

    nachweis = get_object_or_404(VierteljahresNachweis, pk=pk)

    if request.method == "POST":
        # DMS-Dokument entfernen (Verknuepfung loesen)
        entferne_dok_id = request.POST.get("entferne_dms_dokument")
        if entferne_dok_id:
            nachweis.nachweis_dokumente.remove(entferne_dok_id)
            messages.success(request, "DMS-Dokument-Verknuepfung entfernt.")
            return redirect("stiftung:quarterly_confirmation_edit", pk=pk)

        form = VierteljahresNachweisForm(request.POST, request.FILES, instance=nachweis)
        if form.is_valid():
            quarterly_proof = form.save(commit=False)

            # Kategorie-spezifische DMS-Dokumente zuweisen
            for field_name, dms_field in [
                ("studiennachweis_dms_id", "studiennachweis_dms_dokument"),
                ("einkommenssituation_dms_id", "einkommenssituation_dms_dokument"),
                ("vermogenssituation_dms_id", "vermogenssituation_dms_dokument"),
            ]:
                dms_id = request.POST.get(field_name)
                if dms_id:
                    try:
                        dok = DokumentDatei.objects.get(pk=dms_id)
                        setattr(quarterly_proof, dms_field, dok)
                    except DokumentDatei.DoesNotExist:
                        pass
                elif dms_id == "":
                    # Leere Auswahl = Verknuepfung entfernen
                    setattr(quarterly_proof, dms_field, None)

            # Generisches DMS-Dokument hinzufuegen (Abwaertskompatibilitaet)
            dms_dok_id = request.POST.get("dms_dokument_hinzufuegen")
            if dms_dok_id:
                try:
                    dok = DokumentDatei.objects.get(pk=dms_dok_id)
                    # Save first so M2M can be set
                    quarterly_proof.save()
                    quarterly_proof.nachweis_dokumente.add(dok)
                except DokumentDatei.DoesNotExist:
                    pass

            # Calculate current status before saving
            old_status = nachweis.status

            # Auto-update status based on completion
            if quarterly_proof.is_complete():
                if quarterly_proof.status in ['offen', 'teilweise']:
                    quarterly_proof.status = 'eingereicht'
                    quarterly_proof.eingereicht_am = timezone.now()
            else:
                # If not complete, set to teilweise if some fields are filled
                has_partial_data = (
                    quarterly_proof.einkommenssituation_bestaetigt or
                    quarterly_proof.vermogenssituation_bestaetigt or
                    quarterly_proof.studiennachweis_eingereicht
                )
                if has_partial_data and quarterly_proof.status == 'offen':
                    quarterly_proof.status = 'teilweise'

            quarterly_proof.save()

            # Try to create automatic support payment if complete
            if quarterly_proof.is_complete() and quarterly_proof.status == 'eingereicht':
                support_payment = create_quarterly_support_payment(quarterly_proof)
                if support_payment:
                    messages.success(
                        request,
                        f"Automatische Unterstützung über {support_payment.betrag}€ für {support_payment.destinataer.get_full_name()} wurde erstellt (fällig am {support_payment.faellig_am.strftime('%d.%m.%Y')})."
                    )
                else:
                    # Log why payment wasn't created
                    reasons = []
                    if not quarterly_proof.destinataer.vierteljaehrlicher_betrag:
                        reasons.append("kein vierteljährlicher Betrag hinterlegt")
                    if not quarterly_proof.destinataer.iban:
                        reasons.append("keine IBAN hinterlegt")
                    if not quarterly_proof.destinataer.standard_konto and not StiftungsKonto.objects.exists():
                        reasons.append("kein Auszahlungskonto verfügbar")

                    if reasons:
                        messages.warning(
                            request,
                            f"Automatische Unterstützung konnte nicht erstellt werden: {', '.join(reasons)}"
                        )

            # Debug message to see what happened
            status_changed = old_status != quarterly_proof.status
            status_msg = f" (Status: {old_status} → {quarterly_proof.status})" if status_changed else f" (Status: {quarterly_proof.status})"

            messages.success(
                request,
                f"Vierteljahresnachweis für {nachweis.destinataer.get_full_name()} "
                f"({nachweis.jahr} Q{nachweis.quartal}) wurde erfolgreich aktualisiert{status_msg}."
            )
            return redirect("stiftung:destinataer_detail", pk=nachweis.destinataer.pk)
        else:
            # Add form errors to messages
            for field, errors in form.errors.items():
                for error in errors:
                    messages.error(request, f"Fehler in {field}: {error}")
    else:
        form = VierteljahresNachweisForm(instance=nachweis)

    # Alle DMS-Dokumente des Destinataers (fuer Kategorie-Auswahl in den Sektionen)
    alle_dms_dokumente = (
        DokumentDatei.objects.filter(destinataer=nachweis.destinataer)
        .exclude(kontext="email")
        .order_by("kontext", "titel")
    )

    # Generisch verknuepfte Dokumente (M2M) und noch nicht verknuepfte (fuer Bottom-Sektion)
    verknuepfte_nachweis_dokumente = nachweis.nachweis_dokumente.all().order_by("kontext", "titel")
    verknuepfte_ids = set(verknuepfte_nachweis_dokumente.values_list("pk", flat=True))
    verfuegbare_dms_dokumente = alle_dms_dokumente.exclude(pk__in=verknuepfte_ids)

    context = {
        'form': form,
        'nachweis': nachweis,
        'destinataer': nachweis.destinataer,
        'title': f'Vierteljahresnachweis bearbeiten - {nachweis.destinataer.get_full_name()} {nachweis.jahr} Q{nachweis.quartal}',
        'alle_dms_dokumente': alle_dms_dokumente,
        'verknuepfte_nachweis_dokumente': verknuepfte_nachweis_dokumente,
        'verfuegbare_dms_dokumente': verfuegbare_dms_dokumente,
    }
    return render(request, 'stiftung/quarterly_confirmation_edit.html', context)


@login_required
def quarterly_confirmation_approve(request, pk):
    """Approve quarterly confirmation (staff only)"""
    if not request.user.is_staff:
        messages.error(request, "Sie haben keine Berechtigung für diese Aktion.")
        return redirect("stiftung:destinataer_list")
    
    nachweis = get_object_or_404(VierteljahresNachweis, pk=pk)
    
    if request.method == "POST":
        if nachweis.status in ['eingereicht', 'geprueft']:
            # Check if we need to create or update support payment
            related_payment = nachweis.get_related_support_payment()
            
            if nachweis.status == 'eingereicht' or (nachweis.status == 'geprueft' and not related_payment):
                # Approve the quarterly confirmation
                nachweis.status = 'geprueft'
                nachweis.geprueft_am = timezone.now()
                nachweis.geprueft_von = request.user
                nachweis.save()
                
                # Auto-approve next quarter for semester-based tracking (Q1→Q2, Q3→Q4)
                auto_approved_next = nachweis.auto_approve_next_quarter()
                if auto_approved_next:
                    messages.info(
                        request,
                        f"Q{auto_approved_next.quartal} wurde automatisch auf Basis der Q{nachweis.quartal}-Nachweise freigegeben."
                    )
            
            # Handle support payment - create if missing, update if exists
            # Check if payment already exists before calling create_quarterly_support_payment()
            payment_existed_before = related_payment is not None
            
            # Use create_quarterly_support_payment() which handles both cases (find existing or create new)
            related_payment = create_quarterly_support_payment(nachweis)
            if related_payment:
                # Update status to 'in_bearbeitung' for both new and existing payments
                old_status = related_payment.status
                related_payment.status = 'in_bearbeitung'
                related_payment.aktualisiert_am = timezone.now()
                related_payment.save()
                
                if payment_existed_before:
                    messages.success(
                        request,
                        f"Vierteljahresnachweis freigegeben und bestehende Unterstützung für {nachweis.destinataer.get_full_name()} "
                        f"({nachweis.jahr} Q{nachweis.quartal}) wurde von '{old_status}' auf 'in Bearbeitung' aktualisiert."
                    )
                else:
                    messages.success(
                        request,
                        f"Vierteljahresnachweis freigegeben und neue Unterstützung über {related_payment.betrag}€ für {nachweis.destinataer.get_full_name()} "
                        f"({nachweis.jahr} Q{nachweis.quartal}) wurde erstellt."
                    )
            else:
                messages.warning(
                    request,
                    f"Vierteljahresnachweis freigegeben, aber Unterstützung konnte nicht erstellt werden. "
                    f"Bitte prüfen Sie die Einstellungen für {nachweis.destinataer.get_full_name()}."
                )
        else:
            messages.error(
                request,
                "Nur eingereichte oder bereits genehmigte Nachweise können verarbeitet werden."
            )
    
    return redirect("stiftung:destinataer_detail", pk=nachweis.destinataer.pk)


@login_required
def quarterly_confirmation_reset(request, pk):
    """Reset quarterly confirmation status (staff only)"""
    if not request.user.is_staff:
        messages.error(request, "Sie haben keine Berechtigung für diese Aktion.")
        return redirect("stiftung:destinataer_list")
    
    nachweis = get_object_or_404(VierteljahresNachweis, pk=pk)
    
    if request.method == "POST":
        if nachweis.status in ['geprueft', 'eingereicht']:
            # Reset the quarterly confirmation status
            nachweis.status = 'eingereicht' if nachweis.is_complete() else 'teilweise'
            nachweis.geprueft_am = None
            nachweis.geprueft_von = None
            nachweis.aktualisiert_am = timezone.now()
            nachweis.save()
            
            # Reset related support payment status if it exists
            related_payment = nachweis.get_related_support_payment()
            if related_payment and related_payment.status == 'in_bearbeitung':
                related_payment.status = 'geplant'
                related_payment.aktualisiert_am = timezone.now()
                related_payment.save()
                
                messages.success(
                    request,
                    f"Vierteljahresnachweis und zugehörige Unterstützung für {nachweis.destinataer.get_full_name()} "
                    f"({nachweis.jahr} Q{nachweis.quartal}) wurden zurückgesetzt."
                )
            else:
                messages.success(
                    request,
                    f"Vierteljahresnachweis für {nachweis.destinataer.get_full_name()} "
                    f"({nachweis.jahr} Q{nachweis.quartal}) wurde zurückgesetzt."
                )
        else:
            messages.error(
                request,
                "Nur genehmigte oder eingereichte Nachweise können zurückgesetzt werden."
            )
    
    return redirect("stiftung:destinataer_detail", pk=nachweis.destinataer.pk)


# ============================================================
# Phase 2: Destinatär-Timeline, Nachweis-Board, Zahlungs-Pipeline
# ============================================================


@login_required
def destinataer_timeline(request, pk):
    """2a: Chronologische Timeline eines Destinatärs – alle Events in einer Ansicht."""
    destinataer = get_object_or_404(Destinataer, pk=pk)

    typ_filter = request.GET.get("typ", "")

    events = []

    if not typ_filter or typ_filter == "zahlung":
        for u in destinataer.unterstuetzungen.select_related("konto", "ausgezahlt_von", "freigegeben_von").order_by("-faellig_am"):
            events.append({
                "datum": u.faellig_am,
                "typ": "zahlung",
                "icon": "fa-money-bill-wave",
                "farbe": "success" if u.status == "ausgezahlt" else ("danger" if u.is_overdue() else "primary"),
                "titel": f"Zahlung €{u.betrag}",
                "beschreibung": u.beschreibung or u.get_status_display(),
                "status": u.get_status_display(),
                "objekt": u,
            })

    if not typ_filter or typ_filter == "nachweis":
        for n in destinataer.quartalseinreichungen.order_by("-jahr", "-quartal"):
            datum = n.zahlung_faelligkeitsdatum or n.faelligkeitsdatum
            if datum:
                events.append({
                    "datum": datum,
                    "typ": "nachweis",
                    "icon": "fa-file-alt",
                    "farbe": "success" if n.status in ("geprueft", "auto_geprueft") else ("danger" if n.is_overdue() else "warning"),
                    "titel": f"Nachweis {n.jahr} Q{n.quartal}",
                    "beschreibung": n.get_status_display(),
                    "status": n.get_status_display(),
                    "objekt": n,
                })

    if not typ_filter or typ_filter == "email":
        for e in destinataer.email_eingaenge.order_by("-eingangsdatum"):
            events.append({
                "datum": e.eingangsdatum.date() if hasattr(e.eingangsdatum, "date") else e.eingangsdatum,
                "typ": "email",
                "icon": "fa-envelope",
                "farbe": "info",
                "titel": e.betreff or "(kein Betreff)",
                "beschreibung": e.absender_email,
                "status": e.get_status_display(),
                "objekt": e,
            })

    if not typ_filter or typ_filter == "notiz":
        for n in destinataer.notizen_eintraege.order_by("-erstellt_am"):
            events.append({
                "datum": n.erstellt_am.date() if hasattr(n.erstellt_am, "date") else n.erstellt_am,
                "typ": "notiz",
                "icon": "fa-sticky-note",
                "farbe": "secondary",
                "titel": n.titel or "Notiz",
                "beschreibung": (n.text[:100] + "…") if n.text and len(n.text) > 100 else n.text,
                "status": f"von {n.erstellt_von.get_full_name() or n.erstellt_von.username}" if n.erstellt_von else "",
                "objekt": n,
            })

    events.sort(key=lambda e: e["datum"] if e["datum"] else date.min, reverse=True)

    context = {
        "destinataer": destinataer,
        "events": events,
        "typ_filter": typ_filter,
    }
    return render(request, "stiftung/destinataer_timeline.html", context)


@login_required
def nachweis_board(request):
    """2b: Nachweis-Board – Quartals-Übersicht aller Destinatäre."""
    heute = date.today()
    jahr_filter = int(request.GET.get("jahr", heute.year))
    status_filter = request.GET.get("status", "")

    destinataere = Destinataer.objects.filter(aktiv=True).order_by("nachname", "vorname")

    # Auto-create missing VierteljahresNachweis records for the filtered year
    for d in destinataere:
        for q in range(1, 5):
            VierteljahresNachweis.objects.get_or_create(
                destinataer=d, jahr=jahr_filter, quartal=q,
                defaults={"status": "offen"},
            )

    board = []
    for d in destinataere:
        quartale = {}
        for q in range(1, 5):
            nachweis = VierteljahresNachweis.objects.filter(
                destinataer=d, jahr=jahr_filter, quartal=q
            ).first()
            quartale[q] = nachweis
        board.append({"destinataer": d, "quartale": quartale})

    if status_filter:
        board = [
            row for row in board
            if any(
                (q is not None and q.status == status_filter)
                for q in row["quartale"].values()
            )
        ]

    overdue_count = VierteljahresNachweis.objects.filter(
        jahr=jahr_filter,
        status__in=["offen", "teilweise"],
        studiennachweis_faelligkeitsdatum__lt=heute,
    ).count()

    verfuegbare_jahre = list(range(heute.year - 2, heute.year + 2))

    context = {
        "board": board,
        "jahr_filter": jahr_filter,
        "status_filter": status_filter,
        "overdue_count": overdue_count,
        "verfuegbare_jahre": verfuegbare_jahre,
        "status_choices": VierteljahresNachweis.STATUS_CHOICES,
        "heute": heute,
    }
    return render(request, "stiftung/nachweis_board.html", context)


@login_required
def batch_erinnerung_senden(request):
    """2b: Batch-Erinnerungen an säumige Destinatäre – Audit-Log-Einträge."""
    if request.method != "POST":
        return redirect("stiftung:nachweis_board")

    heute = date.today()
    jahr = int(request.POST.get("jahr", heute.year))

    overdue = VierteljahresNachweis.objects.filter(
        jahr=jahr,
        status__in=["offen", "teilweise"],
        studiennachweis_faelligkeitsdatum__lt=heute,
        destinataer__aktiv=True,
    ).select_related("destinataer")

    count = 0
    for nachweis in overdue:
        try:
            log_action(
                request,
                action="update",
                entity_type="destinataer",
                entity_id=str(nachweis.id),
                entity_name=nachweis.destinataer.get_full_name(),
                description=f"ERINNERUNG: Nachweis {nachweis.jahr} Q{nachweis.quartal} – {nachweis.destinataer.get_full_name()} ({nachweis.destinataer.email})",
            )
            count += 1
        except Exception:
            pass

    messages.success(
        request,
        f"{count} Erinnerung(en) im Audit-Log vermerkt.",
    )
    return redirect("stiftung:nachweis_board")


@login_required
def nachweis_aufforderung_senden(request, nachweis_pk):
    """
    Sendet eine Nachweis-Aufforderungs-E-Mail für einen einzelnen Nachweis.
    Erstellt einen UploadToken und versendet den Link per E-Mail an den Destinatär.
    POST-only (CSRF-geschützt).
    """
    from stiftung.tasks import send_nachweis_aufforderung

    if request.method != "POST":
        return redirect("stiftung:nachweis_board")

    nachweis = get_object_or_404(
        VierteljahresNachweis.objects.select_related("destinataer"),
        id=nachweis_pk,
    )
    destinataer = nachweis.destinataer

    if not destinataer.email:
        messages.error(
            request,
            f"Destinatär {destinataer.get_full_name()} hat keine E-Mail-Adresse hinterlegt.",
        )
        return redirect("stiftung:destinataer_detail", pk=destinataer.id)

    base_url = request.build_absolute_uri("/").rstrip("/")
    send_nachweis_aufforderung.delay(
        str(destinataer.id), str(nachweis.id), base_url=base_url
    )

    log_action(
        request,
        action="update",
        entity_type="destinataer",
        entity_id=str(nachweis.id),
        entity_name=destinataer.get_full_name(),
        description=f"Nachweis-Aufforderung per E-Mail gesendet an {destinataer.get_full_name()} ({destinataer.email}) – Nachweis {nachweis.jahr} Q{nachweis.quartal}",
    )

    messages.success(
        request,
        f"Nachweis-Aufforderung wird per E-Mail an {destinataer.email} gesendet.",
    )
    return redirect("stiftung:destinataer_detail", pk=destinataer.id)


@login_required
def batch_nachweis_aufforderung_senden(request):
    """
    Batch: Nachweis-Aufforderungen an alle Destinatäre mit offenen Nachweisen versenden.
    POST-only. Sendet für jeden offenen Nachweis einen UploadToken per E-Mail.
    """
    from stiftung.tasks import send_nachweis_aufforderung

    if request.method != "POST":
        return redirect("stiftung:nachweis_board")

    heute = date.today()
    jahr = int(request.POST.get("jahr", heute.year))

    offene_nachweise = VierteljahresNachweis.objects.filter(
        jahr=jahr,
        status__in=["offen", "teilweise", "nachbesserung"],
        destinataer__aktiv=True,
    ).select_related("destinataer")

    base_url = request.build_absolute_uri("/").rstrip("/")
    count = 0
    ohne_email = 0

    for nachweis in offene_nachweise:
        if not nachweis.destinataer.email:
            ohne_email += 1
            continue
        send_nachweis_aufforderung.delay(
            str(nachweis.destinataer.id), str(nachweis.id), base_url=base_url
        )
        count += 1

    log_action(
        request,
        action="update",
        entity_type="system",
        entity_id="",
        entity_name="Batch-Nachweis-Aufforderung",
        description=f"Batch-Nachweis-Aufforderung {jahr}: {count} E-Mails angestoßen, {ohne_email} ohne E-Mail-Adresse.",
    )

    meldung = f"{count} Nachweis-Aufforderung(en) werden per E-Mail versendet."
    if ohne_email:
        meldung += f" {ohne_email} Destinatär(e) haben keine E-Mail-Adresse."
    messages.success(request, meldung)
    return redirect("stiftung:nachweis_board")


@login_required
def zahlungs_pipeline(request):
    """2c: Zahlungs-Pipeline – 4-Stufen-Kanban-Ansicht."""
    heute = date.today()
    destinataer_id = request.GET.get("destinataer", "")
    konto_id = request.GET.get("konto", "")

    qs = DestinataerUnterstuetzung.objects.select_related(
        "destinataer", "konto", "ausgezahlt_von", "freigegeben_von", "erstellt_von"
    ).exclude(status="storniert")

    if destinataer_id:
        qs = qs.filter(destinataer_id=destinataer_id)
    if konto_id:
        qs = qs.filter(konto_id=konto_id)

    pipeline = {
        "offen": qs.filter(status__in=["geplant", "faellig"]).order_by("faellig_am"),
        "nachweis_eingereicht": qs.filter(status="nachweis_eingereicht").order_by("faellig_am"),
        "freigegeben": qs.filter(status__in=["freigegeben", "in_bearbeitung"]).order_by("faellig_am"),
        "ueberwiesen": qs.filter(status__in=["ausgezahlt", "abgeschlossen"]).order_by("-ausgezahlt_am"),
    }

    stage_meta = {
        "offen": ("Offen", "secondary", "fa-clock"),
        "nachweis_eingereicht": ("Nachweis eingereicht", "info", "fa-file-alt"),
        "freigegeben": ("Freigegeben (4-Augen)", "warning", "fa-shield-alt"),
        "ueberwiesen": ("Überwiesen", "success", "fa-university"),
    }

    pipeline_stages = [
        {
            "key": key,
            "label": stage_meta[key][0],
            "farbe": stage_meta[key][1],
            "icon": stage_meta[key][2],
            "zahlungen": list(pipeline[key]),
            "gesamt": pipeline[key].aggregate(s=Sum("betrag"))["s"] or Decimal("0"),
        }
        for key in ["offen", "nachweis_eingereicht", "freigegeben", "ueberwiesen"]
    ]

    destinataere = Destinataer.objects.filter(aktiv=True).order_by("nachname", "vorname")
    konten = StiftungsKonto.objects.filter(aktiv=True).order_by("kontoname")

    context = {
        "pipeline_stages": pipeline_stages,
        "destinataere": destinataere,
        "konten": konten,
        "destinataer_filter": destinataer_id,
        "konto_filter": konto_id,
        "heute": heute,
    }
    return render(request, "stiftung/zahlungs_pipeline.html", context)


@login_required
def unterstuetzung_freigeben(request, pk):
    """2c: 4-Augen-Prinzip – Freigabe durch zweiten Nutzer."""
    unterstuetzung = get_object_or_404(DestinataerUnterstuetzung, pk=pk)

    if request.method == "POST":
        if not unterstuetzung.can_be_freigegeben(request.user):
            messages.error(
                request,
                "Freigabe nicht möglich: Status nicht korrekt oder Sie sind der Ersteller (Vier-Augen-Prinzip).",
            )
        else:
            unterstuetzung.status = "freigegeben"
            unterstuetzung.freigegeben_von = request.user
            unterstuetzung.freigegeben_am = date.today()
            unterstuetzung.save()
            messages.success(
                request,
                f"Zahlung €{unterstuetzung.betrag} für {unterstuetzung.destinataer.get_full_name()} freigegeben.",
            )

    next_url = request.POST.get("next") or request.META.get("HTTP_REFERER") or reverse("stiftung:zahlungs_pipeline")
    return redirect(next_url)


@login_required
def unterstuetzung_nachweis_eingereicht(request, pk):
    """2c: Status auf 'Nachweis eingereicht' setzen."""
    unterstuetzung = get_object_or_404(DestinataerUnterstuetzung, pk=pk)
    if request.method == "POST":
        if unterstuetzung.status in ["geplant", "faellig"]:
            unterstuetzung.status = "nachweis_eingereicht"
            unterstuetzung.save()
            messages.success(request, "Status auf 'Nachweis eingereicht' gesetzt.")
        else:
            messages.error(request, "Status-Übergang nicht möglich.")
    next_url = request.POST.get("next") or reverse("stiftung:zahlungs_pipeline")
    return redirect(next_url)


@login_required
def unterstuetzung_abschliessen(request, pk):
    """2c: Abschließen einer überwiesenen Zahlung."""
    unterstuetzung = get_object_or_404(DestinataerUnterstuetzung, pk=pk)
    if request.method == "POST":
        if unterstuetzung.status == "ausgezahlt":
            unterstuetzung.status = "abgeschlossen"
            unterstuetzung.save()
            messages.success(request, "Zahlung als abgeschlossen markiert.")
        else:
            messages.error(request, "Nur überwiesene Zahlungen können abgeschlossen werden.")
    next_url = request.POST.get("next") or reverse("stiftung:zahlungs_pipeline")
    return redirect(next_url)


@login_required
def sepa_xml_export(request):
    """Phase 4: SEPA pain.001 XML-Export mit schwifty IBAN/BIC-Validierung."""
    from xml.etree.ElementTree import Element, SubElement, tostring
    import xml.dom.minidom
    try:
        from schwifty import IBAN as SchwiftyIBAN, BIC as SchwiftyBIC
        schwifty_available = True
    except ImportError:
        schwifty_available = False

    zahlungen = DestinataerUnterstuetzung.objects.filter(
        status="freigegeben"
    ).select_related("destinataer", "konto")

    if not zahlungen.exists():
        messages.warning(request, "Keine freigegebenen Zahlungen für SEPA-Export vorhanden.")
        return redirect("stiftung:zahlungs_pipeline")

    # IBAN/BIC-Validierung mit schwifty
    validierungsfehler = []
    if schwifty_available:
        for zahlung in zahlungen:
            iban_raw = (zahlung.empfaenger_iban or zahlung.destinataer.iban or "").replace(" ", "")
            if not iban_raw:
                validierungsfehler.append(
                    f"{zahlung.destinataer.get_full_name()}: Keine IBAN hinterlegt"
                )
                continue
            try:
                SchwiftyIBAN(iban_raw)
            except Exception:
                validierungsfehler.append(
                    f"{zahlung.destinataer.get_full_name()}: Ungültige IBAN '{iban_raw}'"
                )

    if validierungsfehler:
        for fehler in validierungsfehler:
            messages.error(request, f"SEPA-Validierungsfehler: {fehler}")
        return redirect("stiftung:zahlungs_pipeline")

    heute = date.today()
    msg_id = f"STIFTUNG-{heute.strftime('%Y%m%d%H%M%S')}"
    nb_of_txs = zahlungen.count()
    ctrl_sum = f"{sum(z.betrag for z in zahlungen):.2f}"

    root = Element("Document", {
        "xmlns": "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03",
        "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
    })
    cstmr_cdt = SubElement(root, "CstmrCdtTrfInitn")
    grp_hdr = SubElement(cstmr_cdt, "GrpHdr")
    SubElement(grp_hdr, "MsgId").text = msg_id
    SubElement(grp_hdr, "CreDtTm").text = timezone.now().strftime("%Y-%m-%dT%H:%M:%S")
    SubElement(grp_hdr, "NbOfTxs").text = str(nb_of_txs)
    SubElement(grp_hdr, "CtrlSum").text = ctrl_sum
    initg_pty = SubElement(grp_hdr, "InitgPty")
    SubElement(initg_pty, "Nm").text = "van Hees-Theyssen-Vogel'sche Stiftung"

    pmt_inf = SubElement(cstmr_cdt, "PmtInf")
    SubElement(pmt_inf, "PmtInfId").text = f"PMT-{msg_id}"
    SubElement(pmt_inf, "PmtMtd").text = "TRF"
    SubElement(pmt_inf, "NbOfTxs").text = str(nb_of_txs)
    SubElement(pmt_inf, "CtrlSum").text = ctrl_sum
    pmt_tp_inf = SubElement(pmt_inf, "PmtTpInf")
    svc_lvl = SubElement(pmt_tp_inf, "SvcLvl")
    SubElement(svc_lvl, "Cd").text = "SEPA"
    SubElement(pmt_inf, "ReqdExctnDt").text = heute.strftime("%Y-%m-%d")
    dbtr = SubElement(pmt_inf, "Dbtr")
    SubElement(dbtr, "Nm").text = "van Hees-Theyssen-Vogel'sche Stiftung"

    # Schuldner-IBAN aus aktivem Stiftungskonto
    if zahlungen.first() and zahlungen.first().konto and zahlungen.first().konto.iban:
        dbtr_acct = SubElement(pmt_inf, "DbtrAcct")
        dbtr_acct_id = SubElement(dbtr_acct, "Id")
        SubElement(dbtr_acct_id, "IBAN").text = zahlungen.first().konto.iban.replace(" ", "")
        dbtr_agt = SubElement(pmt_inf, "DbtrAgt")
        fin_instn_id = SubElement(dbtr_agt, "FinInstnId")
        bic_val = zahlungen.first().konto.bic.strip()
        if schwifty_available and bic_val:
            try:
                bic_val = str(SchwiftyBIC(bic_val))
            except Exception:
                pass
        SubElement(fin_instn_id, "BIC").text = bic_val or "NOTPROVIDED"

    for zahlung in zahlungen:
        cdt_trf = SubElement(pmt_inf, "CdtTrfTxInf")
        pmt_id_el = SubElement(cdt_trf, "PmtId")
        SubElement(pmt_id_el, "EndToEndId").text = str(zahlung.id)[:35]
        amt = SubElement(cdt_trf, "Amt")
        instd_amt = SubElement(amt, "InstdAmt", {"Ccy": "EUR"})
        instd_amt.text = f"{zahlung.betrag:.2f}"
        cdtr = SubElement(cdt_trf, "Cdtr")
        SubElement(cdtr, "Nm").text = (zahlung.empfaenger_name or zahlung.destinataer.get_full_name())[:70]
        cdtr_acct = SubElement(cdt_trf, "CdtrAcct")
        cdtr_id = SubElement(cdtr_acct, "Id")
        iban_clean = (zahlung.empfaenger_iban or zahlung.destinataer.iban or "").replace(" ", "")
        SubElement(cdtr_id, "IBAN").text = iban_clean
        rmt_inf = SubElement(cdt_trf, "RmtInf")
        SubElement(rmt_inf, "Ustrd").text = (zahlung.verwendungszweck or zahlung.beschreibung or "Stiftungsunterstützung")[:140]

    xml_str = xml.dom.minidom.parseString(tostring(root, encoding="unicode")).toprettyxml(indent="  ")

    response = HttpResponse(xml_str, content_type="application/xml; charset=utf-8")
    response["Content-Disposition"] = f'attachment; filename="sepa_export_{heute.strftime("%Y%m%d")}.xml"'
    return response


# =============================================================================
# Phase 5: Onboarding – Admin-seitige Verwaltung
# =============================================================================


@login_required
def onboarding_einladung_senden(request):
    """
    Erstellt eine OnboardingEinladung und sendet den Einladungslink per E-Mail.
    Aufruf: POST /destinataere/onboarding/einladen/
    Erwartet: email, vorname (optional), nachname (optional).
    """
    import secrets
    from datetime import timedelta
    from stiftung.models import OnboardingEinladung
    from stiftung.tasks import send_onboarding_einladung

    if request.method != "POST":
        return redirect("stiftung:destinataer_list")

    email = request.POST.get("email", "").strip()
    if not email:
        messages.error(request, "Bitte eine gültige E-Mail-Adresse angeben.")
        return redirect("stiftung:destinataer_list")

    vorname = request.POST.get("vorname", "").strip()
    nachname = request.POST.get("nachname", "").strip()

    # Prüfen ob bereits eine offene Einladung für diese E-Mail existiert
    bestehend = OnboardingEinladung.objects.filter(
        email=email,
        status="offen",
        gueltig_bis__gt=timezone.now(),
    ).first()
    if bestehend:
        messages.warning(
            request,
            f"Für {email} existiert bereits eine gültige Einladung (bis {bestehend.gueltig_bis.strftime('%d.%m.%Y')}). "
            f"Keine neue Einladung erstellt.",
        )
        return redirect("stiftung:destinataer_list")

    token_str = secrets.token_urlsafe(48)
    gueltig_bis = timezone.now() + timedelta(days=30)

    einladung = OnboardingEinladung.objects.create(
        token=token_str,
        email=email,
        vorname=vorname,
        nachname=nachname,
        eingeladen_von=request.user,
        gueltig_bis=gueltig_bis,
        status="offen",
    )

    base_url = request.build_absolute_uri("/").rstrip("/")
    send_onboarding_einladung.delay(str(einladung.id), base_url=base_url)

    log_action(
        request,
        action="create",
        entity_type="destinataer",
        entity_id=str(einladung.id),
        entity_name=email,
        description=f"Onboarding-Einladung gesendet an {email}"
                    + (f" ({vorname} {nachname})" if vorname or nachname else ""),
    )

    messages.success(
        request,
        f"Onboarding-Einladung wurde per E-Mail an {email} gesendet (gültig bis {gueltig_bis.strftime('%d.%m.%Y')}).",
    )
    return redirect("stiftung:onboarding_einladung_liste")


@login_required
def onboarding_einladung_liste(request):
    """Übersicht aller Onboarding-Einladungen."""
    from stiftung.models import OnboardingEinladung

    einladungen = OnboardingEinladung.objects.select_related(
        "eingeladen_von", "destinataer"
    ).order_by("-erstellt_am")

    return render(
        request,
        "stiftung/onboarding_einladung_liste.html",
        {"einladungen": einladungen},
    )


@login_required
def onboarding_einladung_widerrufen(request, pk):
    """Widerruft eine offene Onboarding-Einladung."""
    from stiftung.models import OnboardingEinladung

    einladung = get_object_or_404(OnboardingEinladung, id=pk)

    if request.method == "POST":
        if einladung.status == "offen":
            einladung.status = "widerrufen"
            einladung.save(update_fields=["status"])
            log_action(
                request,
                action="update",
                entity_type="destinataer",
                entity_id=str(einladung.id),
                entity_name=einladung.email,
                description=f"Onboarding-Einladung für {einladung.email} widerrufen",
            )
            messages.success(request, f"Einladung für {einladung.email} wurde widerrufen.")
        else:
            messages.error(request, "Diese Einladung kann nicht mehr widerrufen werden.")
        return redirect("stiftung:onboarding_einladung_liste")

    return render(
        request,
        "stiftung/onboarding_einladung_widerrufen_bestaetigung.html",
        {"einladung": einladung},
    )


# Two-Factor Authentication Views

