// @ts-nocheck
import React from 'react';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { AlertTriangle, ArrowLeft, CheckCircle2, Circle, Copy, Download, ExternalLink, Link2, Loader2, Mail, Printer, QrCode, RefreshCw, Share2, X, ShoppingCart, Save, Plus } from 'lucide-react';

import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import toast from 'react-hot-toast';

import { AdminLayout } from '../components/AdminLayout';
import {
  createQrInvite,
  EventQrInvite,
  getEvent,
  getEventQrInvites,
  revokeEventQrInvite,
  TenantEvent,
  updateEventQrInvite,
  EventQrInviteLayout,
  createEventAddonCheckout,
  getAddonCatalog,
  type EventAddonCatalogItem,
} from '../api';
import { isAuthError } from '../auth/tokens';
import {
  ADMIN_EVENTS_PATH,
} from '../constants';
import { buildLimitWarnings } from '../lib/limitWarnings';
import { buildEventTabs } from '../lib/eventTabs';
import { getApiErrorMessage } from '../lib/apiError';
import { AddonsPicker } from '../components/Addons/AddonsPicker';
import { AddonSummaryList } from '../components/Addons/AddonSummaryList';
import { InviteLayoutCustomizerPanel, QrLayoutCustomization } from './components/InviteLayoutCustomizerPanel';
import { buildDownloadFilename, normalizeEventDateSegment } from './components/invite-layout/fileNames';
import { DesignerCanvas } from './components/invite-layout/DesignerCanvas';
import {
  CANVAS_HEIGHT,
  CANVAS_WIDTH,
  buildDefaultElements,
  clamp,
  normalizeElements,
  payloadToElements,
  LayoutElement,
} from './components/invite-layout/schema';
import {
  generatePdfBytes,
  generatePngDataUrl,
  openPdfInNewTab,
  triggerDownloadFromBlob,
  triggerDownloadFromDataUrl,
} from './components/invite-layout/export-utils';
import { preloadedBackgrounds } from './components/invite-layout/backgrounds';
import { useOnboardingProgress } from '../onboarding';
import { FloatingActionBar, type FloatingAction } from '../components/FloatingActionBar';

interface PageState {
  event: TenantEvent | null;
  invites: EventQrInvite[];
  loading: boolean;
  error: string | null;
}

type TabKey = 'layout' | 'share' | 'export';

function resolveTabKey(value: string | null): TabKey {
  if (value === 'export') {
    return 'export';
  }
  if (value === 'share' || value === 'links') {
    return 'share';
  }
  return 'layout';
}

const HEX_COLOR_FULL = /^#([0-9A-Fa-f]{6})$/;
const HEX_COLOR_SHORT = /^#([0-9A-Fa-f]{3})$/;

function normalizeHexColor(value?: string | null): string | null {
  if (!value) {
    return null;
  }

  const trimmed = value.trim();
  if (HEX_COLOR_FULL.test(trimmed)) {
    return trimmed.toUpperCase();
  }

  if (HEX_COLOR_SHORT.test(trimmed)) {
    const [, shorthand] = HEX_COLOR_SHORT.exec(trimmed)!;
    const expanded = shorthand
      .split('')
      .map((char) => char + char)
      .join('');
    return `#${expanded}`.toUpperCase();
  }

  return null;
}

function normalizeGradient(value: unknown): { angle: number; stops: string[] } | null {
  if (!value || typeof value !== 'object') {
    return null;
  }

  const gradient = value as { angle?: unknown; stops?: unknown };
  const angle = typeof gradient.angle === 'number' ? gradient.angle : 180;
  const stops = Array.isArray(gradient.stops)
    ? gradient.stops
        .map((stop) => normalizeHexColor(typeof stop === 'string' ? stop : null))
        .filter((stop): stop is string => Boolean(stop))
    : [];

  return stops.length ? { angle, stops } : null;
}

function buildBackgroundStyle(background: string | null, gradient: { angle: number; stops: string[] } | null): React.CSSProperties {
  if (gradient) {
    return { backgroundImage: `linear-gradient(${gradient.angle}deg, ${gradient.stops.join(', ')})` };
  }

  return { backgroundColor: background ?? '#F8FAFC' };
}

function toStringList(value: unknown): string[] {
  if (!value) {
    return [];
  }

  if (Array.isArray(value)) {
    return value
      .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))
      .filter((entry) => entry.length > 0);
  }

  if (typeof value === 'object') {
    return Object.values(value as Record<string, unknown>)
      .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))
      .filter((entry) => entry.length > 0);
  }

  if (typeof value === 'string') {
    const trimmed = value.trim();
    return trimmed ? [trimmed] : [];
  }

  return [];
}

function ensureInstructionList(value: unknown, fallback: string[]): string[] {
  const source = toStringList(value);
  const base = source.length ? source : fallback;

  return base
    .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))
    .filter((entry) => entry.length > 0)
    .slice(0, 5);
}

function formatPaperLabel(paper?: string | null): string {
  if (!paper) {
    return 'A4';
  }
  return paper.toUpperCase();
}

function formatQrSizeLabel(sizePx: number | null, fallback: string): string {
  if (!sizePx || Number.isNaN(sizePx)) {
    return fallback;
  }

  return `${sizePx}px`;
}
 
export default function EventInvitesPage(): React.ReactElement {
  const { slug } = useParams<{ slug?: string }>();
  const navigate = useNavigate();
  const { t } = useTranslation('management');
  const { t: tLimits } = useTranslation('common', { keyPrefix: 'limits' });

  const [state, setState] = React.useState<PageState>({ event: null, invites: [], loading: true, error: null });
  const [creatingInvite, setCreatingInvite] = React.useState(false);
  const [revokingId, setRevokingId] = React.useState<number | null>(null);
  const [selectedInviteId, setSelectedInviteId] = React.useState<number | null>(null);
  const [copiedInviteId, setCopiedInviteId] = React.useState<number | null>(null);
  const [customizerSaving, setCustomizerSaving] = React.useState(false);
  const [customizerResetting, setCustomizerResetting] = React.useState(false);
  const [customizerDraft, setCustomizerDraft] = React.useState<QrLayoutCustomization | null>(null);
  const [searchParams, setSearchParams] = useSearchParams();
  const tabParam = searchParams.get('tab');
  const initialTab = resolveTabKey(tabParam);
  const [activeTab, setActiveTab] = React.useState<TabKey>(initialTab);
  const [exportDownloadBusy, setExportDownloadBusy] = React.useState<string | null>(null);
  const [exportPrintBusy, setExportPrintBusy] = React.useState<string | null>(null);
  const [exportError, setExportError] = React.useState<string | null>(null);
  const exportPreviewContainerRef = React.useRef<HTMLDivElement | null>(null);
  const [exportScale, setExportScale] = React.useState(0.34);
  const { markStep } = useOnboardingProgress();

  const load = React.useCallback(async () => {
    if (!slug) {
      setState({ event: null, invites: [], loading: false, error: 'Kein Event-Slug angegeben.' });
      return;
    }

    setState((prev) => ({ ...prev, loading: true, error: null }));
    try {
      const [eventData, invitesData, catalog] = await Promise.all([
        getEvent(slug),
        getEventQrInvites(slug),
        getAddonCatalog(),
      ]);
      setState({ event: eventData, invites: invitesData, loading: false, error: null });
      setSelectedInviteId((current) => current ?? invitesData[0]?.id ?? null);
      setAddonsCatalog(catalog);
    } catch (error) {
      if (!isAuthError(error)) {
        setState({ event: null, invites: [], loading: false, error: 'QR-QR-Code konnten nicht geladen werden.' });
      }
    }
  }, [slug]);

  const recomputeExportScale = React.useCallback(() => {
    const container = exportPreviewContainerRef.current;
    if (!container) {
      return;
    }

    const widthRatio = container.clientWidth / CANVAS_WIDTH;
    const heightRatio = container.clientHeight ? container.clientHeight / CANVAS_HEIGHT : Number.POSITIVE_INFINITY;
    const portraitRatio = 1754 / 1240; // A4 height/width for portrait priority
    const adjustedHeightRatio = heightRatio * portraitRatio;
    const base = Math.min(widthRatio, adjustedHeightRatio);
    const safeBase = Number.isFinite(base) && base > 0 ? base : 1;
    const clampedScale = clamp(safeBase, 0.1, 1);

    setExportScale((prev) => (Math.abs(prev - clampedScale) < 0.001 ? prev : clampedScale));
  }, []);

  React.useEffect(() => {
    void load();
  }, [load]);

  React.useEffect(() => {
    recomputeExportScale();
  }, [recomputeExportScale]);

  React.useEffect(() => {
    const handleResize = () => recomputeExportScale();
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, [recomputeExportScale]);

  React.useEffect(() => {
    const nextTab = resolveTabKey(searchParams.get('tab'));
    setActiveTab((current) => (current === nextTab ? current : nextTab));
  }, [searchParams]);

  const handleTabChange = React.useCallback(
    (value: string) => {
      const nextTab = resolveTabKey(value);
      setActiveTab(nextTab);
      const nextParams = new URLSearchParams(searchParams);
      if (nextTab === 'layout') {
        nextParams.delete('tab');
      } else {
        nextParams.set('tab', nextTab === 'share' ? 'share' : 'export');
      }
      setSearchParams(nextParams, { replace: true });
    },
    [searchParams, setSearchParams]
  );

  const event = state.event;
  const eventName = event ? renderEventName(event.name) : t('toolkit.titleFallback', 'Event');
  const eventDate = event?.event_date ?? null;
  const eventTabs = React.useMemo(() => {
    if (!event || !slug) {
      return [];
    }
    const translateMenu = (key: string, fallback: string) => t(key, { defaultValue: fallback });
    return buildEventTabs(event, translateMenu, {
      photos: event.photo_count ?? event.pending_photo_count ?? undefined,
      tasks: event.tasks_count ?? undefined,
    });
  }, [event, slug, state.invites.length, t]);

  const selectedInvite = React.useMemo(
    () => state.invites.find((invite) => invite.id === selectedInviteId) ?? null,
    [state.invites, selectedInviteId]
  );


  React.useEffect(() => {
    setExportError(null);
    setExportDownloadBusy(null);
    setExportPrintBusy(null);
  }, [selectedInvite?.id]);

  React.useEffect(() => {
    if (state.invites.length === 0) {
      setSelectedInviteId(null);
      return;
    }
    setSelectedInviteId((current) => {
      if (current && state.invites.some((invite) => invite.id === current)) {
        return current;
      }
      return state.invites[0]?.id ?? null;
    });
  }, [state.invites]);

  React.useEffect(() => {
    setCustomizerDraft(null);
  }, [selectedInviteId]);

  const currentCustomization = React.useMemo(() => {
    if (!selectedInvite) {
      return null;
    }
    const metadata = selectedInvite.metadata as Record<string, unknown> | undefined | null;
    const raw = metadata?.layout_customization;
    return raw && typeof raw === 'object' ? (raw as QrLayoutCustomization) : null;
  }, [selectedInvite]);

  const effectiveCustomization = customizerDraft ?? currentCustomization;

  const exportLayout = React.useMemo(() => {
    if (!selectedInvite || selectedInvite.layouts.length === 0) {
      return null;
    }

    const targetId = effectiveCustomization?.layout_id;
    if (targetId) {
      const match = selectedInvite.layouts.find((layout) => layout.id === targetId);
      if (match) {
        return match;
      }
    }

    return selectedInvite.layouts[0];
  }, [selectedInvite, effectiveCustomization?.layout_id]);

  const exportPreview = React.useMemo(() => {
    if (!exportLayout || !selectedInvite) {
      return null;
    }

    const customization = effectiveCustomization ?? null;
    const layoutPreview = exportLayout.preview ?? {};

    const backgroundColor = normalizeHexColor(customization?.background_color ?? (layoutPreview.background as string | undefined)) ?? '#F8FAFC';
    const accentColor = normalizeHexColor(customization?.accent_color ?? (layoutPreview.accent as string | undefined)) ?? '#6366F1';
    const textColor = normalizeHexColor(customization?.text_color ?? (layoutPreview.text as string | undefined)) ?? '#111827';
    const secondaryColor = '#1F2937';
    const badgeColor = normalizeHexColor(customization?.badge_color ?? (layoutPreview.accent as string | undefined)) ?? accentColor;
    const gradient = normalizeGradient(customization?.background_gradient ?? layoutPreview.background_gradient ?? null);
    const backgroundImage = customization?.background_image ?? null;

    const instructions = ensureInstructionList(customization?.instructions, exportLayout.instructions ?? []);
    const workflowSteps = toStringList(t('invites.export.workflow.steps', { returnObjects: true }));
    const tips = toStringList(t('invites.export.tips.items', { returnObjects: true }));

    const formatKeys = exportLayout.formats ?? [];
    const formatBadges = formatKeys.map((format) => String(format).toUpperCase());
    const formatLabel = formatBadges.length ? formatBadges.join(' · ') : t('invites.export.meta.formatsNone', 'Keine Formate hinterlegt');

    const qrSizePx = (layoutPreview.qr_size_px as number | undefined) ?? 480;

    return {
      backgroundStyle: buildBackgroundStyle(backgroundColor, gradient),
      backgroundColor,
      backgroundGradient: gradient,
      backgroundImage,
      badgeLabel: customization?.badge_label?.trim() || t('tasks.customizer.defaults.badgeLabel'),
      badgeColor,
      badgeTextColor: '#FFFFFF',
      accentColor,
      textColor,
      secondaryColor,
      headline: customization?.headline?.trim() || eventName,
      subtitle: customization?.subtitle?.trim() || exportLayout.subtitle || '',
      description: customization?.description?.trim() || exportLayout.description || '',
      instructionsHeading: customization?.instructions_heading?.trim() || t('tasks.customizer.defaults.instructionsHeading'),
      instructions: instructions.slice(0, 4),
      linkHeading: customization?.link_heading?.trim() || t('tasks.customizer.defaults.linkHeading'),
      linkLabel: (customization?.link_label?.trim() || selectedInvite.url || ''),
      ctaLabel: customization?.cta_label?.trim() || t('tasks.customizer.defaults.ctaLabel'),
      layoutLabel: exportLayout.name || t('invites.customizer.layoutFallback', 'Layout'),
      layoutSubtitle: exportLayout.subtitle || '',
      formatLabel,
      formatBadges,
      formats: formatKeys,
      paperLabel: formatPaperLabel('a4'),
      orientationLabel: t('invites.export.meta.orientationPortrait', 'Hochformat'),
      qrSizeLabel: formatQrSizeLabel(qrSizePx, t('invites.export.meta.qrSizeFallback', 'Automatisch')),
      lastUpdated: selectedInvite.created_at ? formatDateTime(selectedInvite.created_at) : null,
      mode: customization?.mode === 'advanced' ? 'advanced' : 'standard',
      workflowSteps: workflowSteps.length
        ? workflowSteps
        : [
            t('invites.export.workflow.default1', 'Testdruck ausführen und Farben prüfen.'),
            t('invites.export.workflow.default2', 'Ausdrucke laminieren oder in Schutzfolien stecken.'),
            t('invites.export.workflow.default3', 'Mehrere QR-Codes im Eingangsbereich und an Hotspots platzieren.'),
          ],
      tips: tips.length
        ? tips
        : [
            t('invites.export.tips.default1', 'Nutze Papier mit mindestens 160 g/m² für langlebige Ausdrucke.'),
            t('invites.export.tips.default2', 'Drucke einen QR-Code zur Sicherheit in Reserve aus.'),
            t('invites.export.tips.default3', 'Fotografiere den gedruckten QR-Code testweise, um die Lesbarkeit zu prüfen.'),
          ],
    };
  }, [exportLayout, effectiveCustomization, selectedInvite, eventName, t]);

  const exportElements = React.useMemo<LayoutElement[]>(() => {
    if (!exportLayout) {
      return [];
    }

    if (effectiveCustomization?.mode === 'advanced' && Array.isArray(effectiveCustomization.elements) && effectiveCustomization.elements.length) {
      return normalizeElements(payloadToElements(effectiveCustomization.elements));
    }

    const baseForm: QrLayoutCustomization = {
      ...effectiveCustomization,
      layout_id: exportLayout.id,
      link_label: effectiveCustomization?.link_label ?? selectedInvite?.url ?? '',
      badge_label: effectiveCustomization?.badge_label ?? exportLayout.badge_label ?? undefined,
      instructions: ensureInstructionList(effectiveCustomization?.instructions, exportLayout.instructions ?? []),
      instructions_heading: effectiveCustomization?.instructions_heading ?? exportLayout.instructions_heading ?? undefined,
      logo_data_url: effectiveCustomization?.logo_data_url ?? undefined,
      logo_url: effectiveCustomization?.logo_url ?? undefined,
    };

    return buildDefaultElements(
      exportLayout,
      baseForm,
      eventName,
      exportLayout.preview?.qr_size_px ?? 480
    );
  }, [exportLayout, effectiveCustomization, selectedInvite?.url, eventName]);

  React.useEffect(() => {
    if (activeTab !== 'export') {
      return;
    }
    recomputeExportScale();
  }, [activeTab, recomputeExportScale, exportElements.length, exportLayout?.id, selectedInvite?.id]);

  React.useEffect(() => {
    if (typeof ResizeObserver !== 'function') {
      return undefined;
    }
    const target = exportPreviewContainerRef.current;
    if (!target) {
      return undefined;
    }

    const observer = new ResizeObserver(() => recomputeExportScale());
    observer.observe(target);

    return () => observer.disconnect();
  }, [recomputeExportScale, activeTab]);

  const exportCanvasKey = React.useMemo(
    () => `export:${selectedInvite?.id ?? 'none'}:${exportLayout?.id ?? 'layout'}:${exportPreview?.mode ?? 'standard'}`,
    [selectedInvite?.id, exportLayout?.id, exportPreview?.mode]
  );

  const exportLogo = effectiveCustomization?.logo_data_url ?? effectiveCustomization?.logo_url ?? null;
  const exportQr = selectedInvite?.qr_code_data_url ?? null;

  const handlePreviewSelect = React.useCallback(() => undefined, []);
  const handlePreviewChange = React.useCallback(() => undefined, []);

  const handleCustomizerDraftChange = React.useCallback((draft: QrLayoutCustomization | null) => {
    setCustomizerDraft((previous) => {
      const prevSignature = previous ? JSON.stringify(previous) : null;
      const nextSignature = draft ? JSON.stringify(draft) : null;
      if (prevSignature === nextSignature) {
        return previous;
      }
      return draft;
    });
  }, []);

  const inviteCountSummary = React.useMemo(() => {
    const active = state.invites.filter((invite) => invite.is_active && !invite.revoked_at).length;
    const total = state.invites.length;
    return { active, total };
  }, [state.invites]);

  const primaryInvite = React.useMemo(() => selectedInvite ?? state.invites[0] ?? null, [selectedInvite, state.invites]);

  const workflowSteps = React.useMemo<InviteWorkflowStep[]>(() => {
    const layoutReady = Boolean(effectiveCustomization);
    const shareReady = state.invites.length > 0;
    const exportReady = Boolean(exportPreview && exportElements.length);
    const mapStatus = (tab: TabKey, done: boolean) => {
      if (done) return 'done';
      if (activeTab === tab) return 'active';
      return 'pending';
    };
    return [
      {
        key: 'layout',
        title: t('invites.workflow.steps.layout.title', 'Vorlage wählen'),
        description: t('invites.workflow.steps.layout.description', 'Wähle ein Layout und passe Texte, Farben und QR-Elemente an.'),
        status: mapStatus('layout', layoutReady),
      },
      {
        key: 'share',
        title: t('invites.workflow.steps.share.title', 'Links & QR teilen'),
        description: t('invites.workflow.steps.share.description', 'Aktiviere Gästelinks, kopiere QR-Codes und verteile sie im Team.'),
        status: mapStatus('share', shareReady),
      },
      {
        key: 'export',
        title: t('invites.workflow.steps.export.title', 'Drucken & Export'),
        description: t('invites.workflow.steps.export.description', 'Erzeuge PDFs oder PNGs für den Druck deiner Karten.'),
        status: mapStatus('export', exportReady),
      },
    ];
  }, [activeTab, effectiveCustomization, exportElements.length, exportPreview, state.invites.length, t]);

  async function handleCreateInvite() {
    if (!slug || creatingInvite) {
      return;
    }

    setCreatingInvite(true);
    setState((prev) => ({ ...prev, error: null }));
    try {
      const invite = await createQrInvite(slug);
      setState((prev) => ({
        ...prev,
        invites: [invite, ...prev.invites.filter((existing) => existing.id !== invite.id)],
      }));
      setSelectedInviteId(invite.id);
      try {
        await navigator.clipboard.writeText(invite.url);
        setCopiedInviteId(invite.id);
        toast.success(t('invites.actions.copied', 'Link kopiert'));
      } catch {
        // ignore clipboard failures
      }
      toast.success(t('invites.actions.created', 'QR-Code erstellt'));
      markStep({
        lastStep: 'invite',
        serverStep: 'invite_created',
        meta: { invite_id: invite.id },
      });
    } catch (error) {
      if (!isAuthError(error)) {
        setState((prev) => ({ ...prev, error: 'QR-QR-Code konnte nicht erstellt werden.' }));
        toast.error(t('invites.actions.createFailed', 'QR-Code konnte nicht erstellt werden.'));
      }
    } finally {
      setCreatingInvite(false);
    }
  }

  async function handleCopy(invite: EventQrInvite) {
    try {
      await navigator.clipboard.writeText(invite.url);
      setCopiedInviteId(invite.id);
      toast.success(t('invites.actions.copied', 'Link kopiert'));
    } catch (error) {
      console.warn('[Invites] Clipboard copy failed', error);
      toast.error(t('invites.actions.copyFailed', 'Link konnte nicht kopiert werden.'));
    }
  }

  React.useEffect(() => {
    if (!copiedInviteId) return;
    const timeout = setTimeout(() => setCopiedInviteId(null), 3000);
    return () => clearTimeout(timeout);
  }, [copiedInviteId]);

  async function handleRevoke(invite: EventQrInvite) {
    if (!slug || invite.revoked_at) {
      return;
    }

    setRevokingId(invite.id);
    setState((prev) => ({ ...prev, error: null }));
    try {
      const updated = await revokeEventQrInvite(slug, invite.id);
      setState((prev) => ({
        ...prev,
        invites: prev.invites.map((existing) => (existing.id === updated.id ? updated : existing)),
      }));
      if (selectedInviteId === invite.id && !updated.is_active) {
        setSelectedInviteId((prevId) => (prevId === updated.id ? null : prevId));
      }
      toast.success(t('invites.actions.revoked', 'QR-Code deaktiviert'));
    } catch (error) {
      if (!isAuthError(error)) {
        setState((prev) => ({ ...prev, error: 'QR-QR-Code konnte nicht deaktiviert werden.' }));
        toast.error(t('invites.actions.revokeFailed', 'QR-Code konnte nicht deaktiviert werden.'));
      }
    } finally {
      setRevokingId(null);
    }
  }

  async function handleSaveCustomization(customization: QrLayoutCustomization) {
    if (!slug || !selectedInvite) {
      return;
    }

    setCustomizerSaving(true);
    setState((prev) => ({ ...prev, error: null }));
    try {
      const updated = await updateEventQrInvite(slug, selectedInvite.id, {
        metadata: { layout_customization: customization },
      });
      setState((prev) => ({
        ...prev,
        invites: prev.invites.map((invite) => (invite.id === updated.id ? updated : invite)),
      }));
      setCustomizerDraft(null);
      toast.success(t('invites.customizer.toastSaved', 'Layout gespeichert'));
      markStep({
        lastStep: 'branding',
        serverStep: 'branding_configured',
        meta: {
          invite_id: selectedInvite.id,
          has_custom_branding: true,
        },
      });
    } catch (error) {
      if (!isAuthError(error)) {
        setState((prev) => ({ ...prev, error: 'Anpassung konnte nicht gespeichert werden.' }));
        toast.error(t('invites.customizer.toastSaveFailed', 'Layout konnte nicht gespeichert werden.'));
      }
    } finally {
      setCustomizerSaving(false);
    }
  }

  async function handleResetCustomization() {
    if (!slug || !selectedInvite) {
      return;
    }

    setCustomizerResetting(true);
    setState((prev) => ({ ...prev, error: null }));
    try {
      const updated = await updateEventQrInvite(slug, selectedInvite.id, {
        metadata: { layout_customization: null },
      });
      setState((prev) => ({
        ...prev,
        invites: prev.invites.map((invite) => (invite.id === updated.id ? updated : invite)),
      }));
      setCustomizerDraft(null);
    } catch (error) {
      if (!isAuthError(error)) {
        setState((prev) => ({ ...prev, error: 'Anpassungen konnten nicht zurückgesetzt werden.' }));
      }
    } finally {
      setCustomizerResetting(false);
    }
  }

  const handleQrDownload = React.useCallback(async () => {
    if (!selectedInvite?.qr_code_data_url) {
      return;
    }

    try {
      const response = await fetch(selectedInvite.qr_code_data_url);
      const blob = await response.blob();
      const objectUrl = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = objectUrl;
      const eventDateSegment = normalizeEventDateSegment(eventDate);
      const downloadName = buildDownloadFilename(
        ['QR Code fuer', eventName, eventDateSegment],
        'png',
        'qr-code',
      );
      link.download = downloadName;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
    } catch (error) {
      console.error('[Invites] QR download failed', error);
      setExportError(t('invites.export.qr.error', 'QR-Code konnte nicht gespeichert werden.'));
    }
  }, [selectedInvite, eventName, eventDate, t]);

  const handleExportDownload = React.useCallback(
    async (format: string) => {
      if (!selectedInvite || !exportLayout || !exportPreview) {
        return;
      }

      const normalizedFormat = format.toLowerCase();
      const busyKey = `${exportLayout.id}-${normalizedFormat}`;
      setExportDownloadBusy(busyKey);
      setExportError(null);

      const eventDateSegment = normalizeEventDateSegment(eventDate);
      const filename = buildDownloadFilename(
        ['QR-Codeslayout', eventName, exportLayout.name ?? null, eventDateSegment],
        normalizedFormat,
        'QR-Codeslayout',
      );

      const exportOptions = {
        elements: exportElements,
        accentColor: exportPreview.accentColor,
        textColor: exportPreview.textColor,
        secondaryColor: exportPreview.secondaryColor ?? '#1F2937',
        badgeColor: exportPreview.badgeColor,
        qrCodeDataUrl: exportQr,
        logoDataUrl: exportLogo,
        backgroundColor: exportPreview.backgroundColor ?? '#FFFFFF',
        backgroundGradient: exportPreview.backgroundGradient ?? null,
        backgroundImageUrl: exportPreview.backgroundImage ?? null,
        readOnly: true,
        selectedId: null,
      } as const;

      try {
        if (normalizedFormat === 'png') {
          const dataUrl = await generatePngDataUrl(exportOptions);
          await triggerDownloadFromDataUrl(dataUrl, filename);
        } else if (normalizedFormat === 'pdf') {
          const pdfBytes = await generatePdfBytes(
            exportOptions,
            'a4',
            'portrait',
          );
          triggerDownloadFromBlob(new Blob([pdfBytes.buffer as ArrayBuffer], { type: 'application/pdf' }), filename);
        } else {
          setExportError(t('invites.customizer.errors.downloadFailed', 'Download fehlgeschlagen. Bitte versuche es erneut.'));
        }
      } catch (error) {
        console.error('[Invites] Export download failed', error);
        setExportError(t('invites.customizer.errors.downloadFailed', 'Download fehlgeschlagen. Bitte versuche es erneut.'));
      } finally {
        setExportDownloadBusy(null);
      }
    },
    [selectedInvite, exportLayout, exportPreview, exportElements, exportQr, exportLogo, eventName, eventDate, t]
  );

  const handleExportPrint = React.useCallback(
    async () => {
      if (!selectedInvite || !exportLayout || !exportPreview) {
        return;
      }

      setExportPrintBusy(exportLayout.id);
      setExportError(null);

      const exportOptions = {
        elements: exportElements,
        accentColor: exportPreview.accentColor,
        textColor: exportPreview.textColor,
        secondaryColor: exportPreview.secondaryColor ?? '#1F2937',
        badgeColor: exportPreview.badgeColor,
        qrCodeDataUrl: exportQr,
        logoDataUrl: exportLogo,
        backgroundColor: exportPreview.backgroundColor ?? '#FFFFFF',
        backgroundGradient: exportPreview.backgroundGradient ?? null,
        backgroundImageUrl: exportPreview.backgroundImage ?? null,
        readOnly: true,
        selectedId: null,
      } as const;

      try {
        const pdfBytes = await generatePdfBytes(
          exportOptions,
          'a4',
          'portrait',
        );

        await openPdfInNewTab(pdfBytes);
      } catch (error) {
        console.error('[Invites] Export print failed', error);
        setExportError(t('invites.customizer.errors.printFailed', 'Druck konnte nicht gestartet werden.'));
      } finally {
        setExportPrintBusy(null);
      }
    },
    [selectedInvite, exportLayout, exportPreview, exportElements, exportQr, exportLogo, t]
  );

  const actions = (
    <div className="flex flex-wrap items-center gap-1 text-sm text-muted-foreground">
      <Button
        size="sm"
        variant="ghost"
        onClick={() => navigate(ADMIN_EVENTS_PATH)}
        className="hover:text-foreground"
      >
        <ArrowLeft className="mr-1 h-3.5 w-3.5" />
        {t('invites.actions.backToList', 'Zurück zur Übersicht')}
      </Button>
    </div>
  );

  const limitWarnings = React.useMemo(
    () => buildLimitWarnings(state.event?.limits, tLimits),
    [state.event?.limits, tLimits]
  );

  const [addonBusy, setAddonBusy] = React.useState<string | null>(null);
  const [addonsCatalog, setAddonsCatalog] = React.useState<EventAddonCatalogItem[]>([]);
  //const [searchParams] = useSearchParams();

  const handleAddonPurchase = React.useCallback(
    async (addonKey?: string) => {
      if (!slug) return;
      setAddonBusy('guests');
      const key = addonKey ?? 'extra_guests_100';
      try {
        const currentUrl = window.location.origin + window.location.pathname;
        const successUrl = `${currentUrl}?addon_success=1`;
        const checkout = await createEventAddonCheckout(slug, {
          addon_key: key,
          quantity: 1,
          success_url: successUrl,
          cancel_url: currentUrl,
        });
        if (checkout.checkout_url) {
          window.location.href = checkout.checkout_url;
        }
      } catch (err) {
        toast(getApiErrorMessage(err, 'Checkout fehlgeschlagen.'));
      } finally {
        setAddonBusy(null);
      }
    },
    [slug],
  );

  const fabActions = React.useMemo<FloatingAction[]>(() => {
    const items: FloatingAction[] = [
      {
        key: 'create-invite',
        label: creatingInvite ? t('invites.actions.creating', 'Erstellen...') : t('invites.actions.create', 'Neue QR-Code erstellen'),
        icon: Plus,
        onClick: () => { void handleCreateInvite(); },
        loading: creatingInvite,
        disabled: creatingInvite || state.event?.limits?.can_add_guests === false,
        tone: 'primary',
      },
      {
        key: 'refresh',
        label: state.loading ? t('invites.actions.refreshing', 'Aktualisieren...') : t('invites.actions.refresh', 'Aktualisieren'),
        icon: RefreshCw,
        onClick: () => { void load(); },
        loading: state.loading,
        disabled: state.loading,
        tone: 'secondary',
      },
    ];

    if (activeTab === 'layout' && selectedInvite && effectiveCustomization) {
      items.unshift({
        key: 'save-layout',
        label: customizerSaving ? t('invites.customizer.actions.saving', 'Speichert...') : t('invites.customizer.actions.save', 'Layout speichern'),
        icon: Save,
        onClick: () => { void handleSaveCustomization(effectiveCustomization); },
        loading: customizerSaving,
        disabled: customizerSaving || customizerResetting,
        tone: 'primary',
      });
    }

    return items;
  }, [activeTab, selectedInvite, effectiveCustomization, customizerSaving, customizerResetting, creatingInvite, state.event?.limits?.can_add_guests, state.loading, t, handleSaveCustomization, load]);

  const limitScopeLabels = React.useMemo(
    () => ({
      photos: tLimits('photosTitle'),
      guests: tLimits('guestsTitle'),
      gallery: tLimits('galleryTitle'),
    }),
    [tLimits]
  );

  React.useEffect(() => {
    const success = searchParams.get('addon_success');
    if (success && slug) {
      toast(t('events.success.addonApplied', 'Add-on angewendet. Limits aktualisieren sich in Kürze.'));
      void load();
      searchParams.delete('addon_success');
      navigate(window.location.pathname, { replace: true });
    }
  }, [searchParams, slug, load, navigate, t]);

  return (
    <AdminLayout
      title={eventName}
      subtitle={t('invites.subtitle', 'Manage QR-Codes, Drucklayouts und Branding für deine Gäste.')}
      actions={actions}
      tabs={eventTabs}
      currentTabKey="invites"
    >
      <div className="pb-28">
        {limitWarnings.length > 0 && (
          <div className="mb-6 space-y-2">
            {limitWarnings.map((warning) => (
              <Alert
                key={warning.id}
                variant={warning.tone === 'danger' ? 'destructive' : 'default'}
                className={warning.tone === 'warning' ? 'border-amber-400/50 bg-amber-50 text-amber-900' : undefined}
              >
                <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
                  <div>
                    <AlertTitle className="flex items-center gap-2 text-sm font-semibold">
                      <AlertTriangle className="h-4 w-4" />
                      {limitScopeLabels[warning.scope]}
                    </AlertTitle>
                    <AlertDescription className="text-sm">
                      {warning.message}
                    </AlertDescription>
                  </div>
                  {warning.scope === 'guests' ? (
                    <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
                      <Button
                        variant="outline"
                        size="sm"
                        onClick={() => { void handleAddonPurchase(); }}
                        disabled={addonBusy === 'guests'}
                      >
                        <ShoppingCart className="mr-2 h-4 w-4" />
                        {t('invites.actions.buyMoreGuests', 'Mehr Gäste freischalten')}
                      </Button>
                      <AddonsPicker
                        addons={addonsCatalog}
                        scope="guests"
                        onCheckout={(key) => { void handleAddonPurchase(key); }}
                        busy={addonBusy === 'guests'}
                        t={(key, fallback) => t(key, fallback)}
                      />
                    </div>
                  ) : null}
                </div>
              </Alert>
            ))}
          </div>
        )}

        {state.event?.addons?.length ? (
          <Card className="mb-6 border-0 bg-white/85 shadow-lg shadow-slate-100/50">
            <CardHeader>
              <CardTitle className="text-base font-semibold text-slate-900">{t('events.sections.addons.title', 'Add-ons & Upgrades')}</CardTitle>
              <CardDescription>{t('events.sections.addons.description', 'Zusätzliche Kontingente für dieses Event.')}</CardDescription>
            </CardHeader>
            <CardContent>
              <AddonSummaryList addons={state.event.addons} t={(key, fallback) => t(key, fallback)} />
            </CardContent>
          </Card>
        ) : null}

        <InviteWorkflowSteps steps={workflowSteps} onSelectStep={(tab) => handleTabChange(tab)} />

        <Tabs value={activeTab} onValueChange={handleTabChange} className="space-y-6">
        <TabsList className="grid w-full max-w-2xl grid-cols-3 gap-1 rounded-full border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-strong)] p-1 text-sm">
          <TabsTrigger value="share" className="rounded-full px-4 py-1.5 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground">
            {t('invites.tabs.share', 'Links & QR teilen')}
          </TabsTrigger>
          <TabsTrigger value="layout" className="rounded-full px-4 py-1.5 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground">
            {t('invites.tabs.layout', 'Layout anpassen')}
          </TabsTrigger>
          <TabsTrigger value="export" className="rounded-full px-4 py-1.5 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground">
            {t('invites.tabs.export', 'Drucken & Export')}
          </TabsTrigger>
        </TabsList>

        {state.error ? (
          <Alert variant="destructive">
            <AlertTitle>{t('invites.errorTitle', 'Aktion fehlgeschlagen')}</AlertTitle>
            <AlertDescription>{state.error}</AlertDescription>
          </Alert>
        ) : null}

        <TabsContent value="layout" className="space-y-6 focus-visible:outline-hidden">
          <section className="rounded-3xl border border-[var(--tenant-border-strong)] bg-gradient-to-br from-[var(--tenant-surface-muted)] via-[var(--tenant-surface)] to-[var(--tenant-surface-strong)] p-6 shadow-xl shadow-primary/10 backdrop-blur-sm transition-colors">
            <div className="mb-6 flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
              <div className="space-y-1">
                <h2 className="text-lg font-semibold text-foreground">{t('invites.customizer.heading', 'QR-Codeslayout anpassen')}</h2>
                <p className="text-sm text-muted-foreground">
                  {t('invites.customizer.copy', 'Bearbeite Texte, Farben und Positionen direkt neben der Live-Vorschau. Änderungen werden sofort sichtbar.')}
                </p>
              </div>
            </div>

            {state.loading ? (
              <InviteCustomizerSkeleton />
            ) : (
              <InviteLayoutCustomizerPanel
                invite={selectedInvite ?? null}
                eventName={eventName}
                eventDate={eventDate}
                saving={customizerSaving}
                resetting={customizerResetting}
                onSave={handleSaveCustomization}
                onReset={handleResetCustomization}
                initialCustomization={currentCustomization}
                draftCustomization={customizerDraft}
                onDraftChange={handleCustomizerDraftChange}
                backgroundImages={preloadedBackgrounds}
              />
            )}
          </section>
        </TabsContent>

        <TabsContent value="export" className="space-y-6 focus-visible:outline-hidden">
          <Card className="border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface)] shadow-xl shadow-primary/10 backdrop-blur-sm transition-colors">
            <CardHeader className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
              <div className="space-y-2">
                <CardTitle className="flex items-center gap-2 text-lg text-foreground">
                  <Printer className="h-5 w-5 text-primary" />
                  {t('invites.export.title', 'Drucken & Export')}
                </CardTitle>
                <CardDescription className="text-sm text-muted-foreground">
                  {t('invites.export.description', 'Überprüfe das Layout, starte Testdrucke und exportiere alle Formate.')} 
                </CardDescription>
              </div>
              <div className="flex flex-col gap-2 sm:flex-row sm:items-start sm:gap-3">
                <Select
                  value={selectedInvite ? String(selectedInvite.id) : ''}
                  onValueChange={(value) => setSelectedInviteId(Number(value))}
                  disabled={state.invites.length === 0}
                >
                  <SelectTrigger className="h-9 w-full min-w-[200px] sm:w-60">
                    <SelectValue placeholder={t('invites.export.selectPlaceholder', 'QR-Code auswählen')} />
                  </SelectTrigger>
                  <SelectContent>
                    {state.invites.map((invite) => (
                      <SelectItem key={invite.id} value={String(invite.id)}>
                        {invite.label || invite.token}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <Button
                  size="sm"
                  variant="outline"
                  onClick={() => void load()}
                  disabled={state.loading}
                >
                  {state.loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
                  {t('invites.actions.refresh', 'Aktualisieren')}
                </Button>
              </div>
            </CardHeader>
            <CardContent className="space-y-6">
              {exportError ? (
                <Alert variant="destructive">
                  <AlertTitle>{t('invites.export.errorTitle', 'Aktion fehlgeschlagen')}</AlertTitle>
                  <AlertDescription>{exportError}</AlertDescription>
                </Alert>
              ) : null}

              {selectedInvite ? (
                exportPreview && exportLayout ? (
                  <div className="space-y-6">
                    <div className="grid gap-6 xl:grid-cols-[minmax(0,1.6fr)_minmax(0,0.6fr)]">
                      <div className="space-y-6">
                        <div className="rounded-3xl border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-strong)]/80 p-6 shadow-inner transition-colors">
                          <div className="flex flex-col gap-3 border-b border-[var(--tenant-border-strong)] pb-4 sm:flex-row sm:items-start sm:justify-between">
                            <div className="space-y-1">
                              <h3 className="text-base font-semibold text-foreground">{exportPreview.layoutLabel}</h3>
                              {exportPreview.layoutSubtitle ? (
                                <p className="text-xs text-muted-foreground">{exportPreview.layoutSubtitle}</p>
                              ) : null}
                              <div className="mt-3 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
                                <Badge variant="outline" className="border-primary/40 bg-primary/10 text-primary">
                                  {exportPreview.mode === 'advanced'
                                    ? t('invites.export.mode.advanced', 'Freier Editor')
                                    : t('invites.export.mode.standard', 'Standardlayout')}
                                </Badge>
                                <span>{exportPreview.paperLabel}</span>
                                <span>•</span>
                                <span>{exportPreview.orientationLabel}</span>
                              </div>
                            </div>
                            <p className="text-xs text-muted-foreground sm:max-w-[220px]">
                              {t('invites.export.previewHint', 'Speichere nach Änderungen, um neue Exportdateien zu erzeugen.')}
                            </p>
                          </div>
                          <div className="mt-6 flex justify-center">
                            {exportElements.length ? (
                              <div
                                ref={exportPreviewContainerRef}
                                className="pointer-events-none w-full max-w-full"
                              >
                                <div className="aspect-[1240/1754] mx-auto max-w-full">
                                  <DesignerCanvas
                                    elements={exportElements}
                                    selectedId={null}
                                    onSelect={handlePreviewSelect}
                                    onChange={handlePreviewChange}
                                    background={exportPreview.backgroundColor}
                                    gradient={exportPreview.backgroundGradient}
                                    backgroundImageUrl={exportPreview.backgroundImage ?? null}
                                    accent={exportPreview.accentColor}
                                    text={exportPreview.textColor}
                                    secondary={exportPreview.secondaryColor}
                                    badge={exportPreview.badgeColor}
                                    qrCodeDataUrl={exportQr}
                                    logoDataUrl={exportLogo}
                                    scale={exportScale}
                                    layoutKey={exportCanvasKey}
                                    readOnly
                                  />
                                </div>
                              </div>
                            ) : (
                              <div className="rounded-3xl border border-dashed border-[var(--tenant-border-strong)] bg-[var(--tenant-layer)]/80 px-10 py-20 text-center text-sm text-[var(--tenant-foreground-soft)]">
                                {t('invites.export.noLayoutPreview', 'Für diese Kombination liegt noch keine Vorschau vor. Speichere das Layout zuerst.')}
                              </div>
                            )}
                          </div>
                        </div>

                        <div className="grid gap-6 lg:grid-cols-2">
                          <Card className="border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-strong)]/70 shadow-sm">
                            <CardHeader className="gap-1">
                              <CardTitle className="text-sm text-foreground">{t('invites.export.meta.title', 'Layout-Details')}</CardTitle>
                              <CardDescription className="text-xs text-muted-foreground">{t('invites.export.meta.description', 'Wichtige Kennzahlen für den Druck.')}</CardDescription>
                            </CardHeader>
                            <CardContent className="space-y-3 text-sm">
                              <div className="flex items-center justify-between">
                                <span className="text-muted-foreground">{t('invites.export.meta.paper', 'Papierformat')}</span>
                                <span className="font-medium text-foreground">{exportPreview.paperLabel}</span>
                              </div>
                              <div className="flex items-center justify-between">
                                <span className="text-muted-foreground">{t('invites.export.meta.orientation', 'Ausrichtung')}</span>
                                <span className="font-medium text-foreground">{exportPreview.orientationLabel}</span>
                              </div>
                              <div className="flex items-center justify-between">
                                <span className="text-muted-foreground">{t('invites.export.meta.qrSize', 'QR-Code-Größe')}</span>
                                <span className="font-medium text-foreground">{exportPreview.qrSizeLabel}</span>
                              </div>
                              <div className="flex flex-wrap items-center gap-2">
                                <span className="text-muted-foreground">{t('invites.export.meta.formats', 'Verfügbare Formate')}</span>
                                <div className="flex flex-wrap gap-1">
                                  {exportPreview.formatBadges.map((item) => (
                                    <Badge key={item} className="bg-primary/10 text-primary">
                                      {item}
                                    </Badge>
                                  ))}
                                </div>
                              </div>
                              {exportPreview.lastUpdated ? (
                                <div className="flex items-center justify-between text-xs text-muted-foreground">
                                  <span>{t('invites.export.meta.updated', 'Zuletzt aktualisiert')}</span>
                                  <span>{exportPreview.lastUpdated}</span>
                                </div>
                              ) : null}
                            </CardContent>
                          </Card>

                          <Card className="border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-strong)]/70 shadow-sm">
                            <CardHeader className="gap-1">
                              <CardTitle className="text-sm text-foreground">{t('invites.export.workflow.title', 'Ablauf vor dem Event')}</CardTitle>
                              <CardDescription className="text-xs text-muted-foreground">{t('invites.export.workflow.description', 'So stellst du sicher, dass Gäste den QR-Code finden.')}</CardDescription>
                            </CardHeader>
                            <CardContent>
                              <ol className="space-y-3 text-sm text-muted-foreground">
                                {exportPreview.workflowSteps.map((step, index) => (
                                  <li key={`workflow-step-${index}`} className="flex items-start gap-3">
                                    <span className="mt-0.5 flex h-6 w-6 items-center justify-center rounded-full bg-primary/10 text-xs font-semibold text-primary">
                                      {index + 1}
                                    </span>
                                    <span>{step}</span>
                                  </li>
                                ))}
                              </ol>
                            </CardContent>
                          </Card>
                        </div>
                      </div>

                      <div className="space-y-6">
                        <Card className="border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-strong)]/70 shadow-sm">
                          <CardHeader className="gap-1">
                            <CardTitle className="text-sm text-foreground">{t('invites.export.actions.title', 'Aktionen')}</CardTitle>
                            <CardDescription className="text-xs text-muted-foreground">{t('invites.export.actions.description', 'Starte deinen Testdruck oder lade die Layouts herunter.')}</CardDescription>
                          </CardHeader>
                          <CardContent className="space-y-4">
                            <Button
                              size="lg"
                              className="w-full bg-gradient-to-r from-amber-500 via-orange-500 to-rose-500 text-white shadow-lg shadow-rose-500/30 hover:from-amber-400 hover:via-orange-500 hover:to-rose-500"
                              onClick={() => void handleExportPrint()}
                              disabled={exportPrintBusy === exportLayout.id || Boolean(exportDownloadBusy)}
                            >
                              {exportPrintBusy === exportLayout.id ? (
                                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                              ) : (
                                <Printer className="mr-2 h-4 w-4" />
                              )}
                              {t('invites.export.actions.printNow', 'Direkt drucken')}
                            </Button>

                            <div className="grid gap-2 sm:grid-cols-2">
                              {exportPreview.formats.map((format) => {
                                const key = format.toLowerCase();
                                const busyKey = `${exportLayout.id}-${key}`;
                                const isBusy = exportDownloadBusy === busyKey;
                                return (
                                  <Button
                                    key={busyKey}
                                    variant="outline"
                                    onClick={() => void handleExportDownload(key)}
                                    disabled={(!!exportDownloadBusy && !isBusy) || exportPrintBusy === exportLayout.id}
                                    className="justify-between"
                                  >
                                    <span>{format.toUpperCase()}</span>
                                    {isBusy ? <Loader2 className="h-4 w-4 animate-spin" /> : <Download className="h-4 w-4" />}
                                  </Button>
                                );
                              })}
                            </div>
                          </CardContent>
                        </Card>

                        <Card className="border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-strong)]/70 shadow-sm">
                          <CardHeader className="gap-1">
                            <CardTitle className="text-sm text-foreground">{t('invites.export.qr.title', 'QR-Code & Link')}</CardTitle>
                            <CardDescription className="text-xs text-muted-foreground">{t('invites.export.qr.description', 'Verteile den Link digital oder erstelle weitere Auszüge.')}</CardDescription>
                          </CardHeader>
                          <CardContent className="space-y-3">
                            <div className="flex justify-center">
                              {selectedInvite.qr_code_data_url ? (
                                <img
                                  src={selectedInvite.qr_code_data_url}
                                  alt={t('invites.export.qr.alt', 'QR-Code der QR-Code')}
                                  className="h-40 w-40 rounded-2xl border border-[var(--tenant-border-strong)] bg-white p-3 shadow-md"
                                />
                              ) : (
                                <div className="flex h-40 w-40 items-center justify-center rounded-2xl border border-dashed border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-muted)] text-xs text-muted-foreground">
                                  {t('invites.export.qr.placeholder', 'QR-Code wird nach dem Speichern generiert.')}
                                </div>
                              )}
                            </div>
                            <div className="flex flex-col gap-2 sm:flex-row">
                              <Button variant="outline" className="w-full" onClick={() => void handleQrDownload()} disabled={!selectedInvite.qr_code_data_url}>
                                <Download className="mr-2 h-4 w-4" />
                                {t('invites.export.qr.download', 'QR-Code speichern')}
                              </Button>
                              <Button
                                variant="outline"
                                className="w-full"
                                onClick={() => handleCopy(selectedInvite)}
                              >
                                <Copy className="mr-2 h-4 w-4" />
                                {t('invites.export.qr.copyLink', 'Link kopieren')}
                              </Button>
                            </div>
                            <div className="flex items-center justify-between rounded-lg border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface)] px-3 py-2 text-xs text-muted-foreground">
                              <span className="truncate font-mono">{selectedInvite.url}</span>
                            </div>
                          </CardContent>
                        </Card>

                        <Alert className="border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-muted)]/80">
                          <AlertTitle>{t('invites.export.tips.title', 'Tipps für perfekte Ausdrucke')}</AlertTitle>
                          <AlertDescription>
                            <ul className="mt-2 space-y-1 text-sm text-muted-foreground">
                              {exportPreview.tips.map((tip, index) => (
                                <li key={`export-tip-${index}`} className="flex gap-2">
                                  <span>•</span>
                                  <span>{tip}</span>
                                </li>
                              ))}
                            </ul>
                          </AlertDescription>
                        </Alert>
                      </div>
                    </div>
                  </div>
                ) : (
                  <div className="rounded-2xl border border-dashed border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-muted)] p-8 text-sm text-[var(--tenant-foreground-soft)]">
                    {t('invites.export.noLayouts', 'Für diese QR-Code sind aktuell keine Layouts verfügbar.')}
                  </div>
                )
              ) : (
                <div className="rounded-2xl border border-dashed border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-muted)] p-8 text-sm text-[var(--tenant-foreground-soft)]">
                  {t('invites.export.noInviteSelected', 'Wähle zunächst eine QR-Code aus, um Downloads zu starten.')}
                </div>
              )}
            </CardContent>
          </Card>
        </TabsContent>

        <TabsContent value="share" className="space-y-6 focus-visible:outline-hidden">
          {primaryInvite ? (
            <InviteShareSummaryCard
              invite={primaryInvite}
              onCopy={() => handleCopy(primaryInvite)}
              onCreate={handleCreateInvite}
              onOpenLayout={() => handleTabChange('layout')}
              onOpenExport={() => handleTabChange('export')}
              stats={inviteCountSummary}
            />
          ) : null}
          <Card className="border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface)] shadow-xl shadow-primary/10 backdrop-blur-sm transition-colors">
            <CardHeader className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
              <div className="space-y-2">
                <CardTitle className="flex items-center gap-2 text-lg text-foreground">
                  <QrCode className="h-5 w-5 text-primary" />
                  {t('invites.cardTitle', 'QR-QR-Code & Layouts')}
                </CardTitle>
                <CardDescription className="text-sm text-muted-foreground">
                  {t('invites.cardDescription', 'Erzeuge QR-Code, passe Layouts an und stelle druckfertige Vorlagen bereit.')}
                </CardDescription>
                <div className="inline-flex items-center gap-2 rounded-full border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-muted)] px-3 py-1 text-xs text-[var(--tenant-foreground-soft)]">
                  <span>{t('invites.summary.active', 'Aktive QR-Code')}: {inviteCountSummary.active}</span>
                  <span className="text-primary">•</span>
                  <span>{t('invites.summary.total', 'Gesamt')}: {inviteCountSummary.total}</span>
                </div>
              </div>
              <div className="flex flex-wrap items-center gap-2">
                <Button
                  size="sm"
                  variant="outline"
                  onClick={() => void load()}
                  disabled={state.loading}
                >
                  {state.loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
                  {t('invites.actions.refresh', 'Aktualisieren')}
                </Button>
                <Button
                  size="sm"
                  onClick={handleCreateInvite}
                  disabled={creatingInvite || state.event?.limits?.can_add_guests === false}
                  className="bg-primary text-primary-foreground shadow-lg shadow-primary/20 hover:bg-primary/90"
                >
                  {creatingInvite ? <Loader2 className="mr-1 h-4 w-4 animate-spin" /> : <Share2 className="mr-1 h-4 w-4" />}
                  {t('invites.actions.create', 'Neue QR-Code erstellen')}
                </Button>
                {!state.loading && state.event?.limits?.can_add_guests === false && (
                  <p className="w-full text-xs text-amber-600">
                    {tLimits('guestsBlocked')}
                  </p>
                )}
              </div>
            </CardHeader>
            <CardContent className="space-y-4">
              {state.loading ? (
                <InviteSkeleton />
              ) : state.invites.length === 0 ? (
                <EmptyState onCreate={handleCreateInvite} />
              ) : (
                <div className="grid gap-3">
                  {state.invites.map((invite) => (
                    <InviteListCard
                      key={invite.id}
                      invite={invite}
                      onSelect={() => setSelectedInviteId(invite.id)}
                      onCopy={() => handleCopy(invite)}
                      onRevoke={() => handleRevoke(invite)}
                      selected={invite.id === selectedInvite?.id}
                      revoking={revokingId === invite.id}
                      copied={copiedInviteId === invite.id}
                    />
                  ))}
                </div>
              )}
            </CardContent>
          </Card>
        </TabsContent>
      </Tabs>
      </div>
      <FloatingActionBar actions={fabActions} />
    </AdminLayout>
  );
}

type InviteWorkflowStep = {
  key: TabKey;
  title: string;
  description: string;
  status: 'done' | 'active' | 'pending';
};

function InviteWorkflowSteps({ steps, onSelectStep }: { steps: InviteWorkflowStep[]; onSelectStep: (tab: TabKey) => void }) {
  const { t } = useTranslation('management');

  return (
    <Card className="border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface)]/80 shadow-sm shadow-primary/10">
      <CardHeader className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
        <div>
          <CardTitle className="text-base font-semibold text-foreground">
            {t('invites.workflow.title', 'QR-Codes-Workflow')}
          </CardTitle>
          <CardDescription className="text-sm text-muted-foreground">
            {t('invites.workflow.description', 'Durchlaufe die Schritte in Reihenfolge – Layout gestalten, Links teilen, Export starten.')}
          </CardDescription>
        </div>
        <Badge variant="outline" className="border-primary/30 text-xs uppercase tracking-[0.2em] text-primary">
          {t('invites.workflow.badge', 'Setup')}
        </Badge>
      </CardHeader>
      <CardContent className="grid gap-3 lg:grid-cols-3">
        {steps.map((step) => {
          const isDone = step.status === 'done';
          const isActive = step.status === 'active';
          return (
            <button
              key={step.key}
              type="button"
              className={`flex flex-col gap-2 rounded-2xl border px-4 py-3 text-left transition ${
                isActive
                  ? 'border-primary bg-primary/5 text-primary'
                  : isDone
                    ? 'border-emerald-200 bg-emerald-50 text-emerald-700'
                    : 'border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-muted)] text-muted-foreground'
              }`}
              onClick={() => onSelectStep(step.key)}
            >
              <div className="flex items-center gap-2">
                {isDone ? (
                  <CheckCircle2 className="h-4 w-4" />
                ) : (
                  <Circle className="h-4 w-4" />
                )}
                <span className="text-sm font-semibold">{step.title}</span>
              </div>
              <p className="text-xs leading-snug text-current/80">{step.description}</p>
            </button>
          );
        })}
      </CardContent>
    </Card>
  );
}

type InviteShareSummaryProps = {
  invite: EventQrInvite;
  onCopy: () => void;
  onCreate: () => void;
  onOpenLayout: () => void;
  onOpenExport: () => void;
  stats: { active: number; total: number };
};

function InviteShareSummaryCard({ invite, onCopy, onCreate, onOpenLayout, onOpenExport, stats }: InviteShareSummaryProps) {
  const { t } = useTranslation('management');

  return (
    <Card className="border border-[var(--tenant-border-strong)] bg-gradient-to-r from-[var(--tenant-surface-muted)] via-[var(--tenant-surface)] to-[var(--tenant-surface-strong)] shadow-lg shadow-primary/10">
      <CardHeader className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
        <div>
          <CardTitle className="flex items-center gap-2 text-lg text-foreground">
            <Link2 className="h-5 w-5 text-primary" />
            {t('invites.share.title', 'Schnellzugriff auf Gästelink')}
          </CardTitle>
          <CardDescription className="text-sm text-muted-foreground">
            {t('invites.share.description', 'Nutze den Standardlink, um QR-Codes zu teilen oder weitere Karten zu erzeugen.')}
          </CardDescription>
        </div>
        <div className="flex gap-2 text-xs text-muted-foreground">
          <Badge variant="outline" className="border-primary/30 bg-primary/10 text-primary">
            {t('invites.share.stats.active', { defaultValue: '{{count}} aktiv', count: stats.active })}
          </Badge>
          <Badge variant="outline" className="border-[var(--tenant-border-strong)] text-muted-foreground">
            {t('invites.share.stats.total', { defaultValue: '{{count}} gesamt', count: stats.total })}
          </Badge>
        </div>
      </CardHeader>
      <CardContent className="space-y-4">
        <div className="flex flex-col gap-2 rounded-2xl border border-[var(--tenant-border-strong)] bg-white/90 p-4 text-sm text-muted-foreground">
          <span className="text-xs uppercase tracking-[0.3em] text-muted-foreground">{t('invites.share.primaryLabel', 'Hauptlink')}</span>
          <div className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
            <span className="break-all font-mono text-xs text-foreground">{invite.url}</span>
            <div className="flex flex-wrap gap-2">
              <Button size="sm" variant="outline" onClick={onCopy}>
                <Copy className="mr-1 h-3.5 w-3.5" />
                {t('invites.share.actions.copy', 'Link kopieren')}
              </Button>
              {invite.url ? (
                <Button
                  size="sm"
                  variant="ghost"
                  onClick={() => {
                    window.open(invite.url ?? '#', '_blank', 'noopener');
                  }}
                  className="text-primary"
                >
                  <ExternalLink className="mr-1 h-3.5 w-3.5" />
                  {t('invites.share.actions.open', 'Öffnen')}
                </Button>
              ) : null}
            </div>
          </div>
        </div>
        <div className="grid gap-3 md:grid-cols-2">
          <Button variant="secondary" onClick={onOpenLayout} className="justify-between text-left">
            <div>
              <p className="text-sm font-semibold text-foreground">{t('invites.share.actions.editLayout', 'Layout bearbeiten')}</p>
              <p className="text-xs text-muted-foreground">{t('invites.share.actions.editHint', 'Farben & Texte direkt im Editor anpassen.')}</p>
            </div>
            <ArrowLeft className="h-4 w-4 rotate-180" />
          </Button>
          <div className="flex flex-wrap gap-2">
            <Button variant="outline" onClick={onOpenExport} className="flex-1">
              <Printer className="mr-2 h-4 w-4" />
              {t('invites.share.actions.export', 'Drucken/Export')}
            </Button>
            <Button variant="outline" onClick={onCreate} className="flex-1">
              <Share2 className="mr-2 h-4 w-4" />
              {t('invites.share.actions.create', 'Weitere QR-Code')}
            </Button>
          </div>
        </div>
      </CardContent>
    </Card>
  );
}

function InviteCustomizerSkeleton(): React.ReactElement {
  return (
    <div className="space-y-6">
      <div className="h-8 w-56 animate-pulse rounded-full bg-white/70" />
      <div className="grid gap-6 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,1fr)]">
        <div className="space-y-4">
          {Array.from({ length: 3 }).map((_, index) => (
            <div key={`customizer-skeleton-${index}`} className="h-40 animate-pulse rounded-2xl bg-white/70" />
          ))}
        </div>
        <div className="h-[420px] animate-pulse rounded-3xl bg-white/70" />
      </div>
    </div>
  );
}
function InviteListCard({
  invite,
  selected,
  onSelect,
  onCopy,
  onRevoke,
  revoking,
  copied,
}: {
  invite: EventQrInvite;
  selected: boolean;
  onSelect: () => void;
  onCopy: () => void;
  onRevoke: () => void;
  revoking: boolean;
  copied: boolean;
}) {
  const { t } = useTranslation('management');
  const status = getInviteStatus(invite);
  const metadata = (invite.metadata ?? {}) as Record<string, unknown>;
  const customization = (metadata.layout_customization ?? null) as QrLayoutCustomization | null;
  const preferredLayoutId = customization?.layout_id ?? invite.layouts[0]?.id ?? null;
  const isAutoGenerated = Boolean(metadata.auto_generated);
  const usageLabel = invite.usage_limit ? `${invite.usage_count} / ${invite.usage_limit}` : `${invite.usage_count}`;

  const layoutsById = React.useMemo(() => {
    const map = new Map<string, EventQrInviteLayout>();
    invite.layouts.forEach((layout) => map.set(layout.id, layout));
    return map;
  }, [invite.layouts]);

  const layoutName = preferredLayoutId ? layoutsById.get(preferredLayoutId)?.name ?? invite.layouts[0]?.name ?? '' : '';

  return (
    <div
      role="button"
      tabIndex={0}
      onClick={onSelect}
      onKeyDown={(event) => {
        if (event.key === 'Enter' || event.key === ' ') {
          event.preventDefault();
          onSelect();
        }
      }}
      className={`flex flex-col gap-3 rounded-2xl border p-4 transition-shadow ${selected ? 'border-[var(--tenant-border-strong)] bg-[var(--tenant-surface)] shadow-lg shadow-primary/20' : 'border-border bg-[var(--tenant-surface)] hover:border-[var(--tenant-border-strong)]'}`}
    >
      <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
        <div className="flex flex-wrap items-center gap-2">
          <span className="text-sm font-semibold text-foreground">{invite.label?.trim() || `QR-Code #${invite.id}`}</span>
          <Badge variant="outline" className={statusBadgeClass(status)}>
            {status}
          </Badge>
          {isAutoGenerated ? (
            <Badge variant="secondary" className="bg-muted text-muted-foreground">{t('invites.labels.standard', 'Standard')}</Badge>
          ) : null}
          {customization ? (
            <Badge className="bg-emerald-500/15 text-emerald-700">{t('tasks.customizer.badge', 'Angepasst')}</Badge>
          ) : null}
        </div>
        {invite.qr_code_data_url ? (
          <img
            src={invite.qr_code_data_url}
            alt={t('invites.labels.qrAlt', 'QR-Code Vorschau')}
            className="h-16 w-16 rounded-lg border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-strong)] p-2 shadow-sm"
          />
        ) : null}
      </div>

      <div className="flex flex-wrap items-center gap-2">
        <span className="break-all rounded-lg border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-strong)] px-2 py-1 font-mono text-xs text-muted-foreground">
          {invite.url}
        </span>
        <Button
          variant="outline"
          size="sm"
          onClick={(event) => {
            event.stopPropagation();
            onCopy();
          }}
          className={copied ? 'border-emerald-300 text-emerald-700 hover:bg-emerald-50' : 'border-amber-200 text-amber-700 hover:bg-amber-100'}
        >
          <Copy className="mr-1 h-3 w-3" />
          {copied ? t('invites.actions.copied', 'Kopiert!') : t('invites.actions.copy', 'Link kopieren')}
        </Button>
      </div>

      <div className="grid gap-1 text-xs text-muted-foreground sm:grid-cols-2">
        <span>
          {t('invites.labels.usage', 'Nutzung')}: {usageLabel}
        </span>
        <span>
          {t('invites.labels.layout', 'Layout')}: {layoutName || t('invites.labels.layoutFallback', 'Standard')}
        </span>
        {invite.expires_at ? <span>{t('invites.labels.validUntil', 'Gültig bis')} {formatDateTime(invite.expires_at)}</span> : null}
        {invite.created_at ? <span>{t('invites.labels.createdAt', 'Erstellt am')} {formatDateTime(invite.created_at)}</span> : null}
      </div>

      <div className="flex flex-wrap items-center justify-between gap-2">
        {selected ? (
          <Badge variant="outline" className="border-amber-300 bg-amber-100/70 text-amber-700">
            {t('invites.labels.selected', 'Aktuell ausgewählt')}
          </Badge>
        ) : (
          <div className="text-xs text-muted-foreground">{t('invites.labels.tapToEdit', 'Zum Anpassen auswählen')}</div>
        )}
        <Button
          variant="ghost"
          size="sm"
          onClick={(event) => {
            event.stopPropagation();
            onRevoke();
          }}
          disabled={revoking || invite.revoked_at !== null || !invite.is_active}
          className="text-muted-foreground hover:text-destructive disabled:opacity-50"
        >
          {revoking ? <Loader2 className="mr-1 h-4 w-4 animate-spin" /> : <X className="mr-1 h-4 w-4" />}
          {t('invites.actions.deactivate', 'Deaktivieren')}
        </Button>
      </div>
    </div>
  );
}

function InviteSkeleton() {
  return (
    <div className="space-y-3">
      {Array.from({ length: 3 }).map((_, index) => (
        <div key={`invite-skeleton-${index}`} className="h-32 animate-pulse rounded-2xl bg-gradient-to-r from-white/40 via-white/60 to-white/40" />
      ))}
    </div>
  );
}

function EmptyState({ onCreate }: { onCreate: () => void }) {
  const { t } = useTranslation('management');
  return (
    <div className="flex flex-col items-center justify-center gap-3 rounded-xl border border-dashed border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-muted)] p-10 text-center transition-colors">
      <h3 className="text-base font-semibold text-foreground">{t('invites.empty.title', 'Noch keine QR-Code')}</h3>
      <p className="text-sm text-muted-foreground">{t('invites.empty.copy', 'Erstelle eine QR-Code, um druckfertige QR-Layouts zu erhalten.')}</p>
      <Button onClick={onCreate} className="bg-gradient-to-r from-amber-500 via-orange-500 to-rose-500 text-white">
        <Share2 className="mr-1 h-4 w-4" />
        {t('invites.actions.create', 'Neue QR-Code erstellen')}
      </Button>
    </div>
  );
}

function renderEventName(name: TenantEvent['name']): string {
  if (typeof name === 'string') {
    return name;
  }
  if (name && typeof name === 'object') {
    return name.de ?? name.en ?? Object.values(name)[0] ?? 'Event';
  }
  return 'Event';
}

function getInviteStatus(invite: EventQrInvite): 'Aktiv' | 'Deaktiviert' | 'Abgelaufen' {
  if (invite.revoked_at) return 'Deaktiviert';
  if (invite.expires_at) {
    const expiry = new Date(invite.expires_at);
    if (!Number.isNaN(expiry.getTime()) && expiry.getTime() < Date.now()) {
      return 'Abgelaufen';
    }
  }
  return invite.is_active ? 'Aktiv' : 'Deaktiviert';
}

function statusBadgeClass(status: string): string {
  if (status === 'Aktiv') {
    return 'bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-500/20 dark:text-emerald-200 dark:border-emerald-500/40';
  }
  if (status === 'Abgelaufen') {
    return 'bg-orange-100 text-orange-700 border-orange-200 dark:bg-orange-500/20 dark:text-orange-200 dark:border-orange-500/40';
  }
  return 'bg-slate-200 text-slate-700 border-slate-300 dark:bg-slate-600/40 dark:text-slate-200 dark:border-slate-500/40';
}

function formatDateTime(iso: string | null): string {
  if (!iso) return 'unbekannt';
  const date = new Date(iso);
  if (Number.isNaN(date.getTime())) {
    return 'unbekannt';
  }
  return date.toLocaleString('de-DE', {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  });
}
