import { useAuth } from "@/_core/hooks/useAuth";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Slider } from "@/components/ui/slider";
import { Switch } from "@/components/ui/switch";
import { Tabs, TabsContent } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import { Badge } from "@/components/ui/badge";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import { Tooltip as RoleTooltip, TooltipContent as RoleTooltipContent, TooltipTrigger as RoleTooltipTrigger } from "@/components/ui/tooltip";
import { DirectionsMenu } from "@/components/DirectionsMenu";
import { useViewportFlags } from "@/hooks/useMobile";
import { buildRoutePrefetchHandlers } from "@/lib/routePrefetch";
import { trpc } from "@/lib/trpc";
import { cn } from "@/lib/utils";
import { appendReturnToParam, buildMaterialReturnFinalPdfViewerHref } from "@/lib/materialReturnFinalPdf";
import { emptyMaterialEventQuantities, emptyMaterialEventSelection, materialEventItems, parseMaterialEventQuantity } from "@/lib/materialEvent";
import { loadXlsx } from "@/lib/loadXlsx";
import { formatReservationSallePeriod, getReservationSalleScheduleSummary } from "@/lib/reservationSalle";
import { associationCommuneOptions, getAssociationCommuneLabel, type AssociationCommuneFilter } from "@shared/associationCommunes";
import { buildDisplayName, governanceMemberRoleLabels, legalRepresentativeRoleLabels, parseAssociationGovernance } from "@shared/associationGovernance";
import { associationGeoSourceLabels } from "@shared/associationGeo";
import { associationGeoPrecisionLabels } from "@shared/associationGeo";
import { associationThematicOptions, getAssociationThematicLabel, getAssociationThematicLabels, parseAssociationThematics, type AssociationThematic } from "@shared/associationThematics";
import { providerDefaults, type SupportedMailProvider } from "@shared/mailProviders";
import { 
  ArrowLeft, 
  ArrowUpRight,
  Bell,
  CalendarClock,
  Building2, 
  CheckCircle, 
  CheckCircle2,
  ClipboardList, 
  Clock,
  PackageCheck,
  Download,
  Eye, 
  Facebook,
  FileText, 
  Filter,
  Globe,
  Instagram,
  Loader2, 
  Mail,
  MoreHorizontal,
  Copy,
  Pencil,
  Plus,
  RefreshCw,
  RotateCcw,
  Save,
  Search,
  Send,
  Settings,
  Palette,
  ImagePlus,
  Printer,
  Shield, 
  TrendingUp,
  UserCog,
  Users, 
  XCircle,
  AlertTriangle,
  BarChart3,
  Calendar,
  ChevronDown,
  MapPinned,
  Monitor,
  MoonStar,
  SunMedium,
  Trash2,
  Database,
  Upload
} from "lucide-react";
import { useState, useMemo, useEffect, useRef, Suspense, lazy, type ComponentType, type ReactNode } from "react";
import { Link, useLocation, useSearch } from "wouter";
import { toast } from "sonner";
import {
  appearanceButtonStyles,
  appearanceCardDecorationAnchors,
  appearanceDecorationScopes,
  appearanceDecorationLayers,
  appearanceBackgroundModes,
  appearanceBackgroundPositions,
  appearanceBackgroundPresentationModes,
  appearanceBackgroundTargets,
  appearanceCardStyles,
  appearanceDisplayDensities,
  appearanceFontFamilies,
  appearanceInputStyles,
  appearanceSidebarStyles,
  appearanceSkinPresets,
  appearanceTableStyles,
  defaultPortalAppearance,
  type AppearanceAssetField,
  type AppearanceButtonStyle,
  type AppearanceCardDecorationAnchor,
  type AppearanceDecorationScope,
  type AppearanceDecorationLayer,
  type AppearanceBackgroundMode,
  type AppearanceBackgroundPosition,
  type AppearanceBackgroundPresentationMode,
  type AppearanceBackgroundTarget,
  type AppearanceCardStyle,
  type AppearanceDisplayDensity,
  type AppearanceFontFamily,
  type AppearanceInputStyle,
  type AppearanceSidebarStyle,
  type AppearanceTableStyle,
  type AppearanceThemeMode,
  type PortalAppearanceDecoration,
  type PortalAppearance,
} from "@shared/appearance";
import {
  buildDecisionAdministrativeText,
  computeSallePricing,
  CONDITIONS_FINANCIERES_SALLE_TEXT,
  SALLE_FREQUENCY_LABELS,
  SALLE_WORKFLOW_PAYMENT_STATUS_LABELS,
  SALLE_USAGE_TYPE_LABELS,
  type SalleWorkflowPaymentStatus,
  type SalleReservationFrequency,
  type SalleUsageType,
} from "@shared/sallePricing";
import heroIllustrationUrl from "@/assets/hero-ccds-illustration.svg";

const AccountSecurityPanel = lazy(() => import("@/components/AccountSecurityPanel"));
const ComplianceOperationsPanel = lazy(() => import("@/components/ComplianceOperationsPanel"));
const ReservationCalendar = lazy(() => import("@/components/ReservationCalendar"));
const DirectorSignatureAdmin = lazy(() => import("@/components/DirectorSignatureAdmin"));
const CCDSInternalDirectory = lazy(() => import("@/components/CCDSInternalDirectory"));
const AssociationPublicMap = lazy(() => import("@/components/AssociationPublicMap.lazy"));
const AdminAnalyticsCharts = lazy(() => import("@/components/admin/AdminAnalyticsCharts"));
const AdminDashboardCharts = lazy(() => import("@/components/admin/AdminDashboardCharts"));

type MaterialFinancialMode = "gratuite" | "gratuite_avec_caution" | "location_payante";
type SalleBillingSettings = {
  ribDocumentUrl: string;
  ribDocumentName: string;
  ribMimeType: string;
  ribUploadedAt: string;
};

type OperationalTourEnvironment = "production" | "secondary";
type OperationalTourAuthMode = "local" | "oauth";

const statusConfig: Record<string, { label: string; className: string }> = {
  brouillon: { label: 'Brouillon', className: 'bg-gray-100 text-gray-700' },
  soumise: { label: 'Soumise', className: 'bg-blue-100 text-blue-700' },
  en_cours_traitement: { label: 'En cours', className: 'bg-yellow-100 text-yellow-700' },
  information_complementaire: { label: 'Info requise', className: 'bg-orange-100 text-orange-700' },
  validee: { label: 'Validée', className: 'bg-green-100 text-green-700' },
  refusee: { label: 'Refusée', className: 'bg-red-100 text-red-700' },
  annulee: { label: 'Annulée', className: 'bg-gray-100 text-gray-500' },
};

const requestTypeLabels: Record<string, string> = {
  subvention_fonctionnement: 'Subvention fonctionnement',
  subvention_projet: 'Subvention projet',
  agrement_jeunesse_education: 'Agrément JEP',
  agrement_sport: 'Agrément Sport',
  autorisation_occupation: 'Autorisation occupation',
  demande_salle: 'Demande salle',
  demande_materiel_evenementiel: 'Matériel événementiel',
  autre: 'Autre',
};

const internalRoleLabels: Record<"user" | "accueil" | "service_terrain" | "logistique_controle" | "admin" | "directrice" | "super_admin", string> = {
  user: "Utilisateur simple",
  accueil: "Accueil",
  service_terrain: "Service terrain / récupération",
  logistique_controle: "Groupe interne matériel CCDS - logistique et contrôle",
  admin: "Administrateur",
  directrice: "Directrice",
  super_admin: "Super Admin",
};

const internalRoleDescriptions: Record<"user" | "accueil" | "service_terrain" | "logistique_controle" | "admin" | "directrice" | "super_admin", string> = {
  user: "Dépose et suit ses demandes sans accès au back-office interne.",
  accueil: "Gère les demandes de salle, les devis et les envois finaux sans accès aux réglages sensibles.",
  service_terrain: "Voit ses missions de récupération, ouvre le lien terrain et retrouve les retours déjà validés.",
  logistique_controle: "Traite les demandes de matériel CCDS, pilote les restitutions et contrôle les retours avec un accès interne limité.",
  admin: "Pilote le back-office général et l’exploitation courante hors signature Direction.",
  directrice: "Signe les dossiers salle ou les renvoie à l’accueil pour reprise.",
  super_admin: "Garde la maîtrise des comptes, des autorisations et des réglages globaux du portail.",
};

const adminSectionCollapseDefaults = {
  appearancePresets: false,
  appearanceFoundation: false,
  appearanceBranding: false,
  appearanceVisualUniverse: false,
  appearanceFavicon: true,
  appearanceBackground: false,
  appearanceDecor: false,
  appearanceHeroImageSettings: false,
  appearanceHeroTitleSettings: true,
  appearanceHeroTaglineSettings: true,
  dashboardSupervision: false,
  dashboardOperational: false,
  dashboardBilling: false,
  notificationsFeed: false,
  calendarBoard: false,
  auditHistory: false,
  complianceTechnique: false,
  complianceJuridique: true,
  complianceOrganisation: true,
  compliancePublications: true,
  complianceNextSteps: true,
  complianceOperations: true,
  logisticsSettings: true,
  logisticsBoard: false,
  logisticsAssignOverflow: true,
  logisticsConformeOverflow: true,
} satisfies Record<string, boolean>;

function AdminSurface({
  title,
  description,
  badge,
  children,
  className,
}: {
  title: string;
  description?: string;
  badge?: ReactNode;
  children: ReactNode;
  className?: string;
}) {
  return (
    <div className={cn("rounded-2xl border border-slate-200 bg-white/95 p-5 shadow-[var(--portal-panel-shadow)] transition-[transform,box-shadow,border-color,background-color] duration-[var(--portal-motion-base)] hover:shadow-[var(--portal-panel-hover-shadow)]", className)}>
      <div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
        <div className="space-y-1">
          <h3 className="text-base font-semibold text-slate-950">{title}</h3>
          {description ? <p className="text-sm leading-6 text-slate-600">{description}</p> : null}
        </div>
        {badge ? <div className="shrink-0">{badge}</div> : null}
      </div>
      <div className="mt-4">{children}</div>
    </div>
  );
}

function AdminMetricTile({
  label,
  value,
  tone = "default",
  detail,
}: {
  label: string;
  value: React.ReactNode;
  tone?: "default" | "success" | "info" | "warning";
  detail?: React.ReactNode;
}) {
  const toneClassName =
    tone === "success"
      ? "border-emerald-200 bg-emerald-50"
      : tone === "info"
        ? "border-blue-200 bg-blue-50"
        : tone === "warning"
          ? "border-amber-200 bg-amber-50"
          : "border-slate-200 bg-white";

  const textClassName =
    tone === "success"
      ? "text-emerald-700"
      : tone === "info"
        ? "text-blue-700"
        : tone === "warning"
          ? "text-amber-800"
          : "text-slate-900";

  return (
    <div className={cn("rounded-2xl border p-4 shadow-[var(--portal-card-shadow)] transition-[transform,box-shadow,border-color,background-color] duration-[var(--portal-motion-base)] hover:-translate-y-0.5 hover:shadow-[var(--portal-card-hover-shadow)]", toneClassName)}>
      <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">{label}</p>
      <p className={cn("mt-3 text-2xl font-semibold leading-none", textClassName)}>{value}</p>
      {detail ? <div className="mt-2 text-xs text-slate-600">{detail}</div> : null}
    </div>
  );
}

function AdminSectionCollapsible({
  title,
  description,
  open,
  onOpenChange,
  children,
  className,
  headerBadge,
  contentClassName,
}: {
  title: string;
  description?: string;
  open: boolean;
  onOpenChange: (open: boolean) => void;
  children: ReactNode;
  className?: string;
  headerBadge?: ReactNode;
  contentClassName?: string;
}) {
  return (
    <Collapsible open={open} onOpenChange={onOpenChange} className={cn("rounded-2xl border border-slate-200 bg-white/90 shadow-sm", className)}>
      <div className="flex items-start justify-between gap-4 p-5">
        <div className="min-w-0 space-y-1">
          <p className="text-base font-semibold text-slate-950">{title}</p>
          {description ? <p className="text-sm leading-6 text-muted-foreground">{description}</p> : null}
        </div>
        <div className="flex shrink-0 items-center gap-3">
          {headerBadge}
	          <CollapsibleTrigger asChild>
	            <button
	              type="button"
	              className="inline-flex h-10 w-10 items-center justify-center rounded-full border border-slate-200 bg-white text-slate-600 transition hover:bg-slate-50"
	              aria-label={open ? `Replier ${title}` : `Déplier ${title}`}
	            >
	              <ChevronDown className={cn("h-4 w-4 transition-transform duration-200", open ? "rotate-0" : "-rotate-90")} />
	            </button>
	          </CollapsibleTrigger>
	        </div>
	      </div>
	      <CollapsibleContent className={cn("border-t border-slate-200 px-5 pb-5 pt-5", contentClassName)}>
	        {children}
	      </CollapsibleContent>
	    </Collapsible>
  );
}

function AdminActionButtonGroup({
  children,
  className,
}: {
  children: ReactNode;
  className?: string;
}) {
  return <div className={cn("flex flex-col gap-2 sm:flex-row sm:flex-wrap", className)}>{children}</div>;
}

function AdminOverflowMenu({
  label = "Autres actions",
  children,
}: {
  label?: string;
  children: ReactNode;
}) {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="sm" className="h-9 w-9 p-0" aria-label={label}>
          <MoreHorizontal className="h-4 w-4" />
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end" className="w-56">
        {children}
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

function getDirectoryGeoStatus(entry: {
  latitude?: string | null;
  longitude?: string | null;
  geoSource?: string | null;
  geoPrecision?: string | null;
}) {
  const hasCoordinates = Boolean(entry.latitude && entry.longitude);

  if (!hasCoordinates || !entry.geoSource) {
    return {
      label: "Non localisée",
      className: "border-slate-200 bg-slate-50 text-slate-700",
    };
  }

  if (entry.geoSource === "manual") {
    return {
      label: "Position manuelle",
      className: "border-emerald-200 bg-emerald-50 text-emerald-700",
    };
  }

  if (entry.geoPrecision === "commune_center" || entry.geoSource === "commune_center") {
    return {
      label: "Centre de commune",
      className: "border-amber-200 bg-amber-50 text-amber-800",
    };
  }

  return {
    label: "Adresse exacte",
    className: "border-blue-200 bg-blue-50 text-blue-700",
  };
}

function formatAdminMaterialEventPeriod(formData: any): string {
  const formatAdminDate = (value: string | Date | null | undefined) => {
    if (!value) return "-";
    return new Date(value).toLocaleDateString("fr-FR", {
      day: "2-digit",
      month: "2-digit",
      year: "numeric",
    });
  };

  const start = formData?.dateDebutManifestation || formData?.dateManifestation;
  const end = formData?.dateFinManifestation || formData?.dateManifestation;

  if (!start && !end) return "-";
  if (start && end && start !== end) {
    return `${formatAdminDate(start)} au ${formatAdminDate(end)}`;
  }

  return formatAdminDate(start || end);
}

type DirectoryEntryFormState = {
  id?: number;
  reviewId?: number;
  nomAssociation: string;
  emailOfficiel: string;
  siret: string;
  rna: string;
  thematiques: AssociationThematic[];
  adresse: string;
  codePostal: string;
  ville: string;
  telephone: string;
  siteWeb: string;
  facebookUrl: string;
  instagramUrl: string;
  dateCreation: string;
  objetAssociation: string;
  statutJuridique: "association_loi_1901" | "association_reconnue_utilite_publique" | "fondation" | "autre";
  nomRepresentant: string;
  fonctionRepresentant: string;
  isActive: boolean;
};

type DirectoryReviewCandidate = {
  id: number;
  nomAssociation?: string | null;
  ville?: string | null;
  siret?: string | null;
  rna?: string | null;
  emailOfficiel?: string | null;
  telephone?: string | null;
  nomRepresentant?: string | null;
};

type DirectoryReviewItem = {
  id: number;
  sourceType: "portal_signup" | "helloasso";
  proposedNomAssociation?: string | null;
  proposedEmail?: string | null;
  proposedVille?: string | null;
  proposedSiret?: string | null;
  proposedRna?: string | null;
  proposedTelephone?: string | null;
  proposedNomRepresentant?: string | null;
  matchStatus?: "matched" | "ambiguous" | "none" | null;
  reason?: string | null;
  candidates?: DirectoryReviewCandidate[];
};

type DirectoryProvenanceStatus =
  | "auto_linked"
  | "pending_validation"
  | "manual_entry"
  | "imported"
  | "portal_enriched"
  | "helloasso_synced";

function createEmptyDirectoryEntryForm(): DirectoryEntryFormState {
  return {
    nomAssociation: "",
    emailOfficiel: "",
    siret: "",
    rna: "",
    thematiques: [],
    adresse: "",
    codePostal: "",
    ville: "",
    telephone: "",
    siteWeb: "",
    facebookUrl: "",
    instagramUrl: "",
    dateCreation: "",
    objetAssociation: "",
    statutJuridique: "association_loi_1901",
    nomRepresentant: "",
    fonctionRepresentant: "",
    isActive: true,
  };
}

const materialReturnBoardConfig: Record<string, { label: string; className: string; description: string }> = {
  a_attribuer: {
    label: "À attribuer",
    className: "bg-slate-100 text-slate-700 border-slate-200",
    description: "Demandes validées sans service logistique désigné.",
  },
  planifie: {
    label: "Planifié",
    className: "bg-blue-100 text-blue-700 border-blue-200",
    description: "Service désigné et suivi prêt avant la restitution.",
  },
  terrain: {
    label: "Sur le terrain",
    className: "bg-amber-100 text-amber-700 border-amber-200",
    description: "Restitution en cours de constat ou dans le délai configuré.",
  },
  retard: {
    label: "Alerte / retard",
    className: "bg-red-100 text-red-700 border-red-200",
    description: "Le délai de restitution est dépassé, une relance est nécessaire.",
  },
  conforme: {
    label: "Retour conforme",
    className: "bg-green-100 text-green-700 border-green-200",
    description: "Matériel récupéré et dossier clôturé.",
  },
  litige: {
    label: "Litige / dégradation",
    className: "bg-orange-100 text-orange-800 border-orange-200",
    description: "Réserves constatées, dossier à traiter avant clôture complète.",
  },
};

const associationMapStylePresets = [
  {
    id: "bright",
    label: "OSM Bright auto-hébergé",
    url: "https://www.portail-association973.com/map-styles/openmaptiles-positron/style.json",
    description: "Style clair premium servi par le portail et branché sur les tuiles vectorielles auto-hébergées.",
  },
  {
    id: "bright-admin",
    label: "OSM Bright admin dense",
    url: "https://www.portail-association973.com/map-styles/openmaptiles-positron-admin/style.json",
    description: "Variante plus sobre pour le pilotage interne, avec labels resserrés et lecture plus dense côté administration.",
  },
  {
    id: "basic",
    label: "OSM Basic auto-hébergé",
    url: "https://www.portail-association973.com/map-tiles/styles/OSM%20OpenMapTiles/style.json",
    description: "Style serveur OpenMapTiles plus neutre, simple à diagnostiquer et très robuste.",
  },
] as const;

const associationMapPublicDefaultStyleUrl = "https://www.portail-association973.com/map-styles/openmaptiles-positron/style.json";
const associationMapAdminDefaultStyleUrl = "https://www.portail-association973.com/map-styles/openmaptiles-positron-admin/style.json";

type RoleViewMode = "accueil" | "logistique" | "direction" | "full_admin";
type AdminTabId =
  | "dashboard"
  | "analytics"
  | "requests"
  | "associations"
  | "users"
  | "notifications"
  | "calendrier"
  | "ccds-directory"
  | "logistique"
  | "diffusion"
  | "appearance"
  | "audit"
  | "directory"
  | "conformite";

type AdminTabConfig = {
  id: AdminTabId;
  label: string;
  shortLabel?: string;
  icon: any;
  visibleFor: RoleViewMode[];
  showInTabs?: boolean;
  isEnabled?: (input: {
    canAccessAnalytics: boolean;
    canManageLogistics: boolean;
    canAccessInternalDirectory: boolean;
    isAccueilView: boolean;
    isLogisticsView: boolean;
    isFullAdminView: boolean;
    userRole?: string | null;
  }) => boolean;
};

function getRoleViewMode(user: { role?: string | null; canSignSalle?: boolean | null }) : RoleViewMode | null {
  if (user?.role === "accueil") return "accueil";
  if (user?.role === "logistique_controle") return "logistique";
  if (user?.role === "admin" || user?.role === "super_admin") return "full_admin";
  if (user?.role === "directrice" || Boolean(user?.canSignSalle)) return "direction";
  return null;
}

function buildRoleDashboardCopy(roleViewMode: Exclude<RoleViewMode, "direction" | null>) {
  if (roleViewMode === "accueil") {
    return {
      title: "Poste accueil",
      subtitle: "Les demandes de salle, réponses association et confirmations finales qui méritent votre attention maintenant.",
      sectionTitle: "Priorités de l’accueil",
      sectionDescription: "Vue rapide sur les salles, devis, validations et envois finaux.",
      quickActionsTitle: "Actions d’accueil",
      quickActionsDescription: "Les raccourcis utiles pour avancer sans fouiller dans tous les modules.",
      statsTitle: "Indicateurs accueil",
      statsDescription: "Les chiffres utiles pour piloter la journée côté salles.",
      todayFlowTitle: "Réservations de salle à suivre",
      todayFlowDescription: "Les dossiers salle qui avancent aujourd’hui, du devis jusqu’à l’envoi final.",
      notificationsTitle: "Notifications accueil",
      notificationsDescription: "Nouvelles demandes, réponses association et dossiers signés prêts à envoyer.",
    };
  }

  if (roleViewMode === "logistique") {
    return {
      title: "Poste matériel CCDS",
      subtitle: "Les demandes matériel, restitutions, retards et contrôles qui nécessitent une action rapide.",
      sectionTitle: "Tensions matériel et contrôle",
      sectionDescription: "Vue rapide sur les sorties, retours, litiges et demandes à répartir.",
      quickActionsTitle: "Actions logistique & contrôle",
      quickActionsDescription: "Les raccourcis pour traiter, assigner, contrôler et relancer sans détour.",
      statsTitle: "Indicateurs matériel",
      statsDescription: "Les chiffres utiles pour piloter les restitutions et le suivi terrain.",
      todayFlowTitle: "Flux opérationnel matériel",
      todayFlowDescription: "Les sorties et retours du jour avec accès direct aux affectations terrain.",
      notificationsTitle: "Notifications logistique",
      notificationsDescription: "Restitutions attendues, missions clôturées et anomalies à arbitrer.",
    };
  }

  return {
    title: "Administration CCDS",
    subtitle: "La vue complète du portail pour garder la main sur les salles, le matériel, les utilisateurs et les réglages.",
    sectionTitle: "Suivi opérationnel",
    sectionDescription: "Les dossiers et notifications qui méritent une action sans quitter le tableau de bord.",
    quickActionsTitle: "Raccourcis utiles",
    quickActionsDescription: "Les chemins courts vers les modules qui font avancer la journée.",
    statsTitle: "Indicateurs globaux",
    statsDescription: "Vue d’ensemble sur les associations, les demandes et la facturation.",
    todayFlowTitle: "Flux opérationnel du jour",
    todayFlowDescription: "Les sorties et retours du jour, avec assignation et contrôle sans quitter le tableau de bord.",
    notificationsTitle: "Centre de notifications",
    notificationsDescription: "Toutes les notifications internes du portail, filtrables et consultables au fil de l’eau.",
  };
}

function getDirectoryReviewStatusConfig(status?: DirectoryReviewItem["matchStatus"] | null) {
  if (status === "matched") {
    return {
      label: "Correspondance fiable",
      className: "border-emerald-200 bg-emerald-50 text-emerald-700",
    };
  }
  if (status === "ambiguous") {
    return {
      label: "Vérification requise",
      className: "border-amber-200 bg-amber-50 text-amber-800",
    };
  }
  return {
    label: "Nouvelle fiche probable",
    className: "border-slate-200 bg-slate-100 text-slate-700",
  };
}

function buildDirectoryReviewSummary(review: DirectoryReviewItem) {
  return [
    review.proposedEmail,
    review.proposedTelephone,
    review.proposedVille,
    review.proposedNomRepresentant ? `Représentant: ${review.proposedNomRepresentant}` : null,
  ]
    .filter(Boolean)
    .join(" • ");
}

function getDirectoryProvenanceConfig(entry: {
  registered?: boolean;
  sourceFileName?: string | null;
  externalSourceStatus?: string | null;
  externalSourceLabel?: string | null;
}) {
  const status = entry.externalSourceStatus || "";

  if (status === "manual_entry" || status === "manual_entry_updated") {
    return {
      id: "manual_entry" as DirectoryProvenanceStatus,
      label: "Créée manuellement",
      description: "Fiche créée ou reprise directement depuis l’administration.",
      className: "border-violet-200 bg-violet-50 text-violet-700",
    };
  }

  if (status === "helloasso_ambiguous_match") {
    return {
      id: "pending_validation" as DirectoryProvenanceStatus,
      label: "À valider",
      description: "Une vérification humaine reste nécessaire avant consolidation.",
      className: "border-amber-200 bg-amber-50 text-amber-800",
    };
  }

  if (status.startsWith("helloasso_")) {
    return {
      id: "helloasso_synced" as DirectoryProvenanceStatus,
      label: "Synchronisée HelloAsso",
      description: entry.externalSourceLabel || "Données enrichies via HelloAsso.",
      className: "border-sky-200 bg-sky-50 text-sky-700",
    };
  }

  if (entry.registered && entry.sourceFileName) {
    return {
      id: "auto_linked" as DirectoryProvenanceStatus,
      label: "Rattachée automatiquement",
      description: "Le compte portail a été relié à une fiche existante du bordereau.",
      className: "border-emerald-200 bg-emerald-50 text-emerald-700",
    };
  }

  if (!entry.registered && entry.sourceFileName) {
    return {
      id: "imported" as DirectoryProvenanceStatus,
      label: "Importée du bordereau",
      description: "Fiche issue du bordereau initial, pas encore reliée à un compte portail.",
      className: "border-slate-200 bg-slate-100 text-slate-700",
    };
  }

  if (entry.registered) {
    return {
      id: "portal_enriched" as DirectoryProvenanceStatus,
      label: "Enrichie depuis le portail",
      description: "Les informations ont été consolidées ou complétées depuis l’espace association.",
      className: "border-blue-200 bg-blue-50 text-blue-700",
    };
  }

  return {
    id: "pending_validation" as DirectoryProvenanceStatus,
    label: "À valider",
    description: "La fiche existe mais sa provenance ou son niveau de consolidation reste à confirmer.",
    className: "border-amber-200 bg-amber-50 text-amber-800",
  };
}

function RoleHeroPanel({
  icon,
  title,
  subtitle,
  badges,
  alert,
}: {
  icon: ComponentType<{ className?: string }>;
  title: string;
  subtitle: string;
  badges: Array<{ label: string; value: string }>;
  alert?: {
    tone: string;
    icon: ComponentType<{ className?: string }>;
    title: string;
    description: string;
  } | null;
}) {
  const Icon = icon;
  const AlertIcon = alert?.icon;
  return (
    <Card className="border-primary/10 bg-gradient-to-br from-primary/5 via-background to-background">
      <CardContent className="flex flex-col gap-4 p-6 lg:flex-row lg:items-start lg:justify-between">
        <div className="space-y-3">
          <div className="inline-flex items-center gap-2 rounded-full border border-primary/10 bg-primary/10 px-3 py-1 text-xs font-medium text-primary">
            <Icon className="h-3.5 w-3.5" />
            Vue métier
          </div>
          <div className="space-y-1">
            <h2 className="text-2xl font-semibold tracking-tight">{title}</h2>
            <p className="max-w-2xl text-sm text-muted-foreground">{subtitle}</p>
          </div>
          {alert && AlertIcon ? (
            <div className={cn("max-w-2xl rounded-lg border px-4 py-3 shadow-sm", alert.tone)}>
              <div className="flex items-start gap-3">
                <AlertIcon className="mt-0.5 h-4 w-4 shrink-0" />
	                <div className="space-y-1">
	                  <p className="text-sm font-semibold">{alert.title}</p>
	                  <p className="text-sm opacity-90">{alert.description}</p>
	                </div>
	              </div>
	            </div>
	          ) : null}
        </div>
        <div className="grid gap-3 sm:grid-cols-3">
          {badges.map((badge) => (
            <div key={badge.label} className="rounded-lg border bg-background/90 px-4 py-3 shadow-sm">
              <p className="text-[11px] uppercase tracking-wide text-muted-foreground">{badge.label}</p>
              <p className="mt-1 text-xl font-semibold">{badge.value}</p>
            </div>
          ))}
        </div>
      </CardContent>
    </Card>
  );
}

function QuickActionsByRole({
  title,
  description,
  actions,
}: {
  title: string;
  description: string;
  actions: Array<{ label: string; icon: ComponentType<{ className?: string }>; onClick: () => void; variant?: "default" | "outline" | "secondary" | "ghost"; }>;
}) {
  return (
    <Card>
      <CardHeader>
        <CardTitle>{title}</CardTitle>
        <CardDescription>{description}</CardDescription>
      </CardHeader>
      <CardContent className="grid gap-3 sm:grid-cols-2">
        {actions.map((action) => {
          const Icon = action.icon;
          return (
            <Button
              key={action.label}
              type="button"
              variant={action.variant || "outline"}
              className="h-auto justify-between gap-3 px-4 py-4 text-left"
              onClick={action.onClick}
            >
              <span className="text-sm font-medium">{action.label}</span>
              <Icon className="h-4 w-4 shrink-0" />
            </Button>
          );
        })}
      </CardContent>
    </Card>
  );
}

function RoleKpiStrip({
  title,
  description,
  items,
}: {
  title: string;
  description: string;
  items: Array<{ label: string; value: string | number; hint: string; icon: ComponentType<{ className?: string }>; tone?: string }>;
}) {
  return (
    <Card>
      <CardHeader>
        <CardTitle>{title}</CardTitle>
        <CardDescription>{description}</CardDescription>
      </CardHeader>
      <CardContent className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
        {items.map((item) => {
          const Icon = item.icon;
          return (
            <div key={item.label} className={cn("rounded-lg border bg-background px-4 py-4", item.tone)}>
              <div className="flex items-start justify-between gap-3">
                <div>
                  <p className="text-sm font-medium text-muted-foreground">{item.label}</p>
                  <p className="mt-2 text-3xl font-semibold">{item.value}</p>
                </div>
                <Icon className="h-5 w-5 text-primary" />
              </div>
              <p className="mt-3 text-xs text-muted-foreground">{item.hint}</p>
            </div>
          );
        })}
      </CardContent>
    </Card>
  );
}

const materialReturnUploadLinkStateLabels: Record<string, string> = {
  active: "Actif",
  expired: "Expiré",
  closed: "Clôturé",
};

const materialReturnUploadLinkStateClasses: Record<string, string> = {
  active: "bg-emerald-100 text-emerald-800 border-emerald-200",
  expired: "bg-red-100 text-red-700 border-red-200",
  closed: "bg-slate-100 text-slate-700 border-slate-200",
};

const salleBillingStatusClasses: Record<SalleWorkflowPaymentStatus, string> = {
  en_attente_paiement: "bg-amber-100 text-amber-800 border-amber-200",
  paiement_partiel: "bg-orange-100 text-orange-800 border-orange-200",
  paiement_recu: "bg-blue-100 text-blue-700 border-blue-200",
  paiement_valide: "bg-emerald-100 text-emerald-700 border-emerald-200",
};

const appearanceFontFamilyLabels: Record<AppearanceFontFamily, string> = {
  marianne: "Marianne",
  system: "Système",
  rounded: "Arrondie",
  serif: "Sérif",
};

const appearanceCardStyleLabels: Record<AppearanceCardStyle, string> = {
  soft: "Douces",
  outlined: "Contour net",
  elevated: "Élevées",
};

const appearanceTableStyleLabels: Record<AppearanceTableStyle, string> = {
  comfortable: "Confort",
  compact: "Compact",
  striped: "Zébré",
};

const appearanceDisplayDensityLabels: Record<AppearanceDisplayDensity, string> = {
  comfortable: "Équilibré",
  compact: "Compact",
  detailed: "Détaillé",
};

const appearanceSidebarStyleLabels: Record<AppearanceSidebarStyle, string> = {
  soft: "Sobre",
  contrast: "Contrasté",
  floating: "Flottant",
};

const appearanceButtonStyleLabels: Record<AppearanceButtonStyle, string> = {
  rounded: "Arrondi",
  pill: "Pilule",
  square: "Rectiligne",
};

const appearanceInputStyleLabels: Record<AppearanceInputStyle, string> = {
  outline: "Contour",
  soft: "Doux",
  filled: "Rempli",
};

const appearanceBackgroundModeLabels: Record<AppearanceBackgroundMode, string> = {
  cover: "Cover",
  repeat: "Repeat",
  fixed: "Fixed",
};

const appearanceBackgroundPositionLabels: Record<AppearanceBackgroundPosition, string> = {
  center: "Centre",
  top: "Haut",
  bottom: "Bas",
  left: "Gauche",
  right: "Droite",
  top_left: "Haut gauche",
  top_right: "Haut droite",
  bottom_left: "Bas gauche",
  bottom_right: "Bas droite",
};

const appearanceBackgroundPresentationModeLabels: Record<AppearanceBackgroundPresentationMode, string> = {
  full: "Habillage complet",
  edge_frame: "Bords illustrés / centre neutre",
};

const appearanceBackgroundTargetLabels: Record<AppearanceBackgroundTarget, string> = {
  home: "Accueil public",
  public: "Pages d’information",
  public_directory: "Annuaire public",
  public_map: "Carte publique",
  dashboard: "Espace association (global)",
  dashboard_home: "Tableau de bord",
  dashboard_profile: "Mon association",
  dashboard_documents: "Mes documents",
  dashboard_requests: "Mes demandes",
  dashboard_new_request: "Nouvelle demande",
  dashboard_reservation_salle: "Réservation de salle",
  dashboard_material: "Matériel événementiel",
  dashboard_directory: "Annuaire association",
  admin: "Administration (global)",
  admin_pilotage: "Pilotage",
  admin_logistics: "Logistique",
  admin_directory: "Référentiel associations",
  admin_compliance: "Conformité",
};

const appearanceDecorationScopeLabels: Record<AppearanceDecorationScope, string> = {
  all: "Toutes les zones",
  home: "Accueil public",
  public: "Pages d’information",
  public_directory: "Annuaire public",
  public_map: "Carte publique",
  dashboard: "Espace association (global)",
  dashboard_home: "Tableau de bord",
  dashboard_profile: "Mon association",
  dashboard_documents: "Mes documents",
  dashboard_requests: "Mes demandes",
  dashboard_new_request: "Nouvelle demande",
  dashboard_reservation_salle: "Réservation de salle",
  dashboard_material: "Matériel événementiel",
  dashboard_directory: "Annuaire association",
  admin: "Administration (global)",
  admin_pilotage: "Pilotage admin",
  admin_logistics: "Logistique",
  admin_directory: "Référentiel associations",
  admin_compliance: "Conformité / Apparence",
};

const appearanceDecorationLayerLabels: Record<AppearanceDecorationLayer, string> = {
  background: "Fond du portail",
  card: "Ornement de card",
};

const appearanceCardDecorationAnchorLabels: Record<AppearanceCardDecorationAnchor, string> = {
  top_left: "Coin haut gauche",
  top_right: "Coin haut droit",
  bottom_left: "Coin bas gauche",
  bottom_right: "Coin bas droit",
};

const appearanceDecorationPresets: Array<{
  id: string;
  label: string;
  description: string;
  patch: Partial<PortalAppearanceDecoration>;
}> = [
  {
    id: "card-top-right",
    label: "Coin haut droit",
    description: "Ornement discret pour souligner le haut de la card.",
    patch: { layer: "card", cardAnchor: "top_right", cardInsetPercent: 5, widthPercent: 14, opacity: 12, blurRadius: 1 },
  },
  {
    id: "card-bottom-left",
    label: "Coin bas gauche",
    description: "Accent plus organique sur le bas de la card.",
    patch: { layer: "card", cardAnchor: "bottom_left", cardInsetPercent: 6, widthPercent: 16, opacity: 12, blurRadius: 2 },
  },
  {
    id: "card-soft-relief",
    label: "Relief discret",
    description: "Très léger, pour garder une lecture institutionnelle.",
    patch: { layer: "card", cardAnchor: "top_right", cardInsetPercent: 8, widthPercent: 12, opacity: 9, blurRadius: 1 },
  },
  {
    id: "page-edge-accent",
    label: "Fond latéral",
    description: "Décor de page discret, placé sur les bords.",
    patch: { layer: "background", topPercent: 18, leftPercent: 8, widthPercent: 18, opacity: 12, blurRadius: 2 },
  },
];

const appearanceBackgroundTargetGroups: Array<{
  id: string;
  label: string;
  description: string;
  targets: AppearanceBackgroundTarget[];
}> = [
  {
    id: "home-public",
    label: "Univers public",
    description: "Accueil, pages d’information, annuaire et carte visibles sans connexion.",
    targets: ["home", "public", "public_directory", "public_map"],
  },
  {
    id: "association",
    label: "Univers association",
    description: "Toutes les pages du parcours association connecté, avec possibilité de fond dédié écran par écran.",
    targets: [
      "dashboard",
      "dashboard_home",
      "dashboard_profile",
      "dashboard_documents",
      "dashboard_requests",
      "dashboard_new_request",
      "dashboard_reservation_salle",
      "dashboard_material",
      "dashboard_directory",
    ],
  },
  {
    id: "admin",
    label: "Univers administration",
    description: "Pilotage, logistique, référentiels et conformité, avec réglages indépendants si besoin.",
    targets: ["admin", "admin_pilotage", "admin_logistics", "admin_directory", "admin_compliance"],
  },
];

const appearanceBackgroundPositionCssValues: Record<AppearanceBackgroundPosition, string> = {
  center: "center",
  top: "center top",
  bottom: "center bottom",
  left: "left center",
  right: "right center",
  top_left: "left top",
  top_right: "right top",
  bottom_left: "left bottom",
  bottom_right: "right bottom",
};

function resolveManualBackgroundTargets(targets: AppearanceBackgroundTarget[]) {
  const uniqueTargets = Array.from(new Set(targets));
  const coversAllTargets = appearanceBackgroundTargets.every((target) => uniqueTargets.includes(target));

  if (uniqueTargets.length === 0 || coversAllTargets) {
    return ["home"] as AppearanceBackgroundTarget[];
  }

  return uniqueTargets;
}

const mailProviderLabels: Record<SupportedMailProvider, string> = {
  custom: "SMTP personnalisé",
  ovh: "OVH / Zimbra",
  gmail: "Gmail",
  outlook: "Outlook / Microsoft 365",
};

const defaultMailSettingsForm = {
  smtpProvider: "custom" as SupportedMailProvider,
  smtpHost: "",
  smtpPort: "",
  smtpUser: "",
  smtpFrom: "",
  smtpSecure: false,
  smtpRequireTls: false,
  smtpPass: "",
  testRecipient: "",
};

const defaultLogisticsSettingsForm = {
  materialReturnLeadDays: 4,
  materialReturnGraceDays: 3,
  inventory: {
    ...materialEventItems.reduce<Record<string, string>>((acc, item) => {
      acc[item.key] = "";
      return acc;
    }, {}),
  },
  replacementValues: {
    ...materialEventItems.reduce<Record<string, string>>((acc, item) => {
      acc[item.key] = "";
      return acc;
    }, {}),
  },
};

function formatCentsAsEuroInput(cents: number | null | undefined) {
  if (cents === null || cents === undefined) return "";
  return String((cents / 100).toFixed(2));
}

function parseEuroInputToCents(value: string) {
  const normalized = String(value || "").trim().replace(",", ".");
  if (!normalized) return null;
  const parsed = Number.parseFloat(normalized);
  if (!Number.isFinite(parsed) || parsed < 0) return null;
  return Math.round(parsed * 100);
}

function getFinancialModeLabel(mode: MaterialFinancialMode | string | null | undefined) {
  switch (mode) {
    case 'gratuite':
      return 'Mise à disposition gratuite';
    case 'gratuite_avec_caution':
      return 'Mise à disposition gratuite avec caution';
    case 'location_payante':
      return 'Location payante';
    default:
      return '-';
  }
}

function getSalleWorkflowStageFromFormData(formData: any) {
  const workflow = formData?.salleWorkflow;
  if (!workflow || typeof workflow !== "object") return null;
  if (workflow.finalNotificationSentAt) {
    return {
      label: "Demande finalisée",
      className: "bg-green-100 text-green-700 border-green-200",
    };
  }
  if (workflow.directorStatus === "signee") {
    return {
      label: "Signé par la Direction",
      className: "bg-emerald-100 text-emerald-700 border-emerald-200",
    };
  }
  if (workflow.directorStatus === "en_attente_signature") {
    return {
      label: "En signature Direction",
      className: "bg-amber-100 text-amber-800 border-amber-200",
    };
  }
  if (workflow.quoteStatus === "accepte") {
    return {
      label: "Devis accepté",
      className: "bg-blue-100 text-blue-700 border-blue-200",
    };
  }
  if (workflow.quoteStatus === "en_attente_association") {
    return {
      label: "Réponse attendue",
      className: "bg-violet-100 text-violet-700 border-violet-200",
    };
  }
  return {
    label: "Analyse hôtesse",
    className: "bg-slate-100 text-slate-700 border-slate-200",
  };
}

function getAdminRequestStatusMeta(request: { status: string; type: string; formData?: string | null }) {
  const defaultStatus = statusConfig[request.status] || {
    label: request.status,
    className: "bg-slate-100 text-slate-700",
  };

  if (request.type !== "demande_salle") {
    return defaultStatus;
  }

  let formData: any = {};
  try {
    formData = request.formData ? JSON.parse(request.formData) : {};
  } catch {
    formData = {};
  }
  const workflow = formData?.salleWorkflow && typeof formData.salleWorkflow === "object"
    ? formData.salleWorkflow
    : null;

  if (workflow?.quoteStatus === "en_attente_association") {
    return {
      label: "En attente de réponse de l'association",
      className: "bg-violet-100 text-violet-700 border-violet-200",
    };
  }

  return defaultStatus;
}

type MiniCalendarCell = {
  key: string;
  day: number | null;
  iso: string | null;
  isToday: boolean;
  tension: boolean;
  litige: boolean;
  load: number;
};

function ThemeModeIcon({ mode }: { mode: AppearanceThemeMode }) {
  if (mode === "dark") {
    return <MoonStar className="h-4 w-4" />;
  }
  if (mode === "system") {
    return <Monitor className="h-4 w-4" />;
  }
  return <SunMedium className="h-4 w-4" />;
}

function AdminPanelLoader({ compact = false }: { compact?: boolean }) {
  return (
    <div className={compact ? "flex min-h-[220px] items-center justify-center py-10" : "flex min-h-[320px] items-center justify-center py-12"}>
      <Loader2 className="h-6 w-6 animate-spin text-primary" />
    </div>
  );
}

export default function Admin() {
  const { user } = useAuth();
  const [location] = useLocation();
  const roleViewMode = getRoleViewMode(user || {});
  const requestedTab = useMemo(() => {
    if (typeof window === "undefined") return "dashboard";
    return new URLSearchParams(window.location.search).get("tab") || "dashboard";
  }, [location]);
  const canAccessFullAdmin = roleViewMode === "accueil" || roleViewMode === "logistique" || roleViewMode === "full_admin";
  const canAccessDirectorSignature = roleViewMode === "direction";
  const canAccessInternalDirectory =
    user?.role === "accueil"
    || user?.role === "directrice"
    || user?.role === "admin"
    || user?.role === "super_admin";

  if (requestedTab === "ccds-directory" && canAccessInternalDirectory && !canAccessFullAdmin) {
    return (
      <Suspense fallback={<AdminPanelLoader />}>
        <CCDSInternalDirectory
          backHref="/admin"
          backLabel="Retour à la signature"
          canEdit={false}
        />
      </Suspense>
    );
  }
  if (canAccessDirectorSignature && !canAccessFullAdmin) {
    return (
      <Suspense fallback={<AdminPanelLoader />}>
        <DirectorSignatureAdmin user={user || {}} />
      </Suspense>
    );
  }

  return <AdminFull />;
}

function AdminFull() {
  const { user } = useAuth();
  const { isMobile, isTablet } = useViewportFlags();
  const [location, setLocation] = useLocation();
  const search = useSearch();
  const roleViewMode = getRoleViewMode(user || {});
  const canAccessFullAdmin = roleViewMode === "accueil" || roleViewMode === "logistique" || roleViewMode === "full_admin";
  const isAccueil = roleViewMode === "accueil";
  const isLogisticsControl = roleViewMode === "logistique";
  const isFullAdminView = roleViewMode === "full_admin";
  const canAccessAnalytics = user?.role === "accueil" || user?.role === "admin" || user?.role === "super_admin";
  const canAccessInternalDirectory =
    user?.role === "accueil"
    || user?.role === "admin"
    || user?.role === "super_admin";
  const canManageLogistics = user?.role === "super_admin" || Boolean(user?.canManageLogistics);
  const adminTabConfig: AdminTabConfig[] = useMemo(() => [
    { id: "dashboard", label: "Tableau de bord", icon: BarChart3, visibleFor: ["accueil", "logistique", "full_admin"] },
    {
      id: "analytics",
      label: "Statistiques",
      icon: TrendingUp,
      visibleFor: ["accueil", "full_admin"],
      isEnabled: ({ canAccessAnalytics }) => canAccessAnalytics,
    },
    { id: "requests", label: "Demandes", icon: ClipboardList, visibleFor: ["accueil", "logistique", "full_admin"] },
    {
      id: "associations",
      label: "Associations",
      icon: Building2,
      visibleFor: ["full_admin"],
    },
    {
      id: "users",
      label: "Utilisateurs",
      icon: UserCog,
      visibleFor: ["full_admin"],
      showInTabs: false,
    },
    {
      id: "notifications",
      label: "Notifications",
      icon: Bell,
      visibleFor: ["accueil", "logistique", "full_admin"],
    },
    {
      id: "calendrier",
      label: "Calendrier",
      icon: Calendar,
      visibleFor: ["accueil", "logistique", "full_admin"],
    },
    {
      id: "ccds-directory",
      label: "Annuaire CCDS",
      icon: Users,
      visibleFor: ["accueil", "full_admin"],
      isEnabled: ({ canAccessInternalDirectory }) => canAccessInternalDirectory,
    },
    {
      id: "logistique",
      label: "Logistique",
      icon: PackageCheck,
      visibleFor: ["logistique", "full_admin"],
      isEnabled: ({ canManageLogistics, isAccueilView }) => canManageLogistics && !isAccueilView,
    },
    {
      id: "diffusion",
      label: "Services matériel",
      icon: Mail,
      visibleFor: ["logistique", "full_admin"],
      isEnabled: ({ isAccueilView, userRole }) => !isAccueilView && userRole !== "directrice",
    },
    {
      id: "directory",
      label: "Bordereau",
      icon: Database,
      visibleFor: ["full_admin"],
      isEnabled: ({ userRole, isLogisticsView }) =>
        !isLogisticsView && (userRole === "admin" || userRole === "super_admin"),
    },
    {
      id: "appearance",
      label: "Apparence",
      icon: Palette,
      visibleFor: ["full_admin"],
      showInTabs: false,
      isEnabled: ({ userRole }) => userRole === "admin" || userRole === "super_admin",
    },
    {
      id: "audit",
      label: "Journal d'audit",
      icon: FileText,
      visibleFor: ["full_admin"],
      showInTabs: false,
      isEnabled: ({ userRole }) => userRole === "admin" || userRole === "super_admin",
    },
    {
      id: "conformite",
      label: "Conformité",
      icon: Shield,
      visibleFor: ["full_admin"],
      isEnabled: ({ userRole }) => userRole === "admin" || userRole === "super_admin",
    },
  ], []);
  const availableTabs = useMemo(() => {
    return adminTabConfig.filter((tab) => {
      if (!roleViewMode) return false;
      if (!tab.visibleFor.includes(roleViewMode)) return false;
      if (!tab.isEnabled) return true;
      return tab.isEnabled({
        canAccessAnalytics,
        canManageLogistics,
        canAccessInternalDirectory,
        isAccueilView: isAccueil,
        isLogisticsView: isLogisticsControl,
        isFullAdminView,
        userRole: user?.role,
      });
    });
  }, [adminTabConfig, canAccessAnalytics, canAccessInternalDirectory, canManageLogistics, isAccueil, isFullAdminView, isLogisticsControl, roleViewMode, user?.role]);
  const visibleTabs = useMemo(
    () => availableTabs.filter((tab) => tab.showInTabs !== false),
    [availableTabs]
  );
  const allowedTabIds = useMemo(() => new Set(availableTabs.map((tab) => tab.id)), [availableTabs]);
  const dashboardCopy = useMemo(
    () => buildRoleDashboardCopy(isAccueil ? "accueil" : isLogisticsControl ? "logistique" : "full_admin"),
    [isAccueil, isLogisticsControl]
  );
  
  const getTabFromUrl = () => {
    if (typeof window === "undefined") return "dashboard";
    return new URLSearchParams(window.location.search).get("tab") || "dashboard";
  };
  const getLogisticsStateFromUrl = () => {
    if (typeof window === "undefined") return "all";
    const value = new URLSearchParams(window.location.search).get("logisticsState") || "all";
    return ["all", "a_attribuer", "planifie", "terrain", "retard", "conforme", "litige"].includes(value) ? value : "all";
  };

  const [activeTab, setActiveTab] = useState(() => getTabFromUrl());
  const [selectedLogisticsBoardState, setSelectedLogisticsBoardState] = useState<
    "all" | "a_attribuer" | "planifie" | "terrain" | "retard" | "conforme" | "litige"
  >(getLogisticsStateFromUrl() as "all" | "a_attribuer" | "planifie" | "terrain" | "retard" | "conforme" | "litige");
  const [selectedLogisticsMapRequestId, setSelectedLogisticsMapRequestId] = useState<number | null>(null);
  const [showUnreadNotificationsOnly, setShowUnreadNotificationsOnly] = useState(false);

  const buildAdminTabHref = (nextTab: string, options?: { logisticsState?: string | null }) => {
    const params = new URLSearchParams();

    if (nextTab !== "dashboard") {
      params.set("tab", nextTab);
    }

    if (nextTab === "logistique") {
      const logisticsState = options?.logisticsState;
      if (logisticsState && logisticsState !== "all") {
        params.set("logisticsState", logisticsState);
      }
    }

    const query = params.toString();
    return query ? `/admin?${query}` : "/admin";
  };

  const openAdminTab = (nextTab: string) => {
    setActiveTab(nextTab);
    if (nextTab !== "logistique") {
      setSelectedLogisticsBoardState("all");
    }
    const target = buildAdminTabHref(nextTab);
    if (location !== target) {
      setLocation(target);
    }
  };

  const openLogisticsBoardState = (nextState: "all" | "a_attribuer" | "planifie" | "terrain" | "retard" | "conforme" | "litige") => {
    setActiveTab("logistique");
    setSelectedLogisticsBoardState(nextState);
    const target = buildAdminTabHref("logistique", { logisticsState: nextState });
    if (location !== target) {
      setLocation(target);
    }
  };
  
  // Update tab when URL changes
  useEffect(() => {
    const tab = getTabFromUrl();
    if (tab !== activeTab) {
      setActiveTab(tab);
    }
    const logisticsState = getLogisticsStateFromUrl();
    if (logisticsState !== selectedLogisticsBoardState) {
      setSelectedLogisticsBoardState(logisticsState as typeof selectedLogisticsBoardState);
    }
  }, [location, search]);

  // Queries
  const { data: stats, isLoading: statsLoading, refetch: refetchStats } = trpc.stats.getDashboard.useQuery();
  const { data: requestsPerMonth } = trpc.stats.getRequestsPerMonth.useQuery({ months: 12 });
  const { data: associationsPerMonth } = trpc.stats.getAssociationsPerMonth.useQuery({ months: 12 });
  const { data: requestsByType } = trpc.stats.getRequestsByType.useQuery();
  const { data: avgProcessingTime } = trpc.stats.getAverageProcessingTime.useQuery();
  const { data: pendingRequests } = trpc.request.getPending.useQuery(undefined, {
    refetchInterval: 30_000,
  });
  const { data: overdueRequests } = trpc.request.getOverdue.useQuery(undefined, {
    refetchInterval: 30_000,
  });
  const { data: notifications } = trpc.notifications.getAll.useQuery({
    unreadOnly: showUnreadNotificationsOnly,
  });
  const { data: unreadCount } = trpc.notifications.getUnreadCount.useQuery();
  const { data: adminUsers } = trpc.adminUsers.listAll.useQuery(undefined, {
    enabled: user?.role === "super_admin",
  });
  const { data: logisticsGroup } = trpc.settings.getLogisticsGroup.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const { data: auditLogs } = trpc.auditLog.list.useQuery({ limit: 20 }, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
    refetchInterval: 30_000,
  });
  const { data: complianceSnapshot } = trpc.compliance.getSnapshot.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const { data: appearanceSettings } = trpc.settings.getAppearance.useQuery();
  const { data: mailSettings } = trpc.settings.getMail.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const { data: associationMapSettings } = trpc.settings.getAssociationMap.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const { data: userOperatingTourSettings } = trpc.settings.getUserOperatingTour.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const { data: salleBillingSettings } = trpc.settings.getSalleBilling.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const { data: logisticsSettings } = trpc.settings.getLogistics.useQuery(undefined, {
    enabled: canManageLogistics,
  });
  const { data: operationalRecapServices } = trpc.operationalRecapService.listAll.useQuery(undefined, {
    enabled: canManageLogistics,
  });
  useEffect(() => {
    if (!allowedTabIds.has(activeTab as AdminTabId)) {
      openAdminTab("dashboard");
    }
  }, [activeTab, allowedTabIds]);
  useEffect(() => {
    if (!logisticsGroup) return;
    setLogisticsGroupForm({
      label: logisticsGroup.label,
      memberUserIds: logisticsGroup.memberUserIds,
    });
  }, [logisticsGroup]);
  const normalizedOperationalRecapServices = useMemo(() => {
    return (operationalRecapServices || [])
      .filter((service): service is NonNullable<typeof service> => Boolean(service && typeof service === "object"))
      .map((service) => {
        const rawRecipientEmails = (service as { recipientEmails?: unknown }).recipientEmails;
        return {
          ...service,
          label: typeof service.label === "string" ? service.label : "",
          description: typeof service.description === "string" ? service.description : "",
          usage: service.usage === "controle" ? "controle" : "terrain",
          actif: Boolean(service.actif),
          recipientEmails: Array.isArray(rawRecipientEmails)
            ? rawRecipientEmails.map((entry) => String(entry || "").trim().toLowerCase()).filter(Boolean)
            : [],
        };
      });
  }, [operationalRecapServices]);
  const terrainOperationalRecapServices = useMemo(
    () => normalizedOperationalRecapServices.filter((service) => service.usage === "terrain"),
    [normalizedOperationalRecapServices]
  );
  const controlOperationalRecapServices = useMemo(
    () => normalizedOperationalRecapServices.filter((service) => service.usage === "controle"),
    [normalizedOperationalRecapServices]
  );
  const { data: materialReturnBoard, isLoading: materialReturnBoardLoading } = trpc.materialReturn.listAdminBoard.useQuery(undefined, {
    enabled: canManageLogistics,
    refetchInterval: 30_000,
  });
  useEffect(() => {
    setMaterialReturnLinkOverrides({});
  }, [materialReturnBoard]);
  const { data: materialCalendar } = trpc.request.getMaterialCalendar.useQuery(undefined, {
    enabled: canManageLogistics,
    refetchInterval: 30_000,
  });
  const { data: logisticsMapAssociations } = trpc.associationDirectory.listMap.useQuery({
    registrationStatus: "all",
    limit: 1000,
  }, {
    enabled: canManageLogistics && activeTab === "logistique",
    refetchInterval: 60_000,
  });
  const { data: directorySummary } = trpc.associationDirectory.getSummary.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const { data: associationCommuneCounts } = trpc.association.getCommuneCounts.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const [directorySearch, setDirectorySearch] = useState("");
  const [directoryStatusFilter, setDirectoryStatusFilter] = useState<"all" | "registered" | "unregistered">("all");
  const [directoryCommuneFilter, setDirectoryCommuneFilter] = useState<AssociationCommuneFilter>("all");
  const [directoryThematiqueFilter, setDirectoryThematiqueFilter] = useState<AssociationThematic | "all">("all");
  const [directoryProvenanceFilter, setDirectoryProvenanceFilter] = useState<"all" | DirectoryProvenanceStatus>("all");
  const { data: directoryEntries } = trpc.associationDirectory.listLatest.useQuery({
    search: directorySearch || undefined,
    registrationStatus: directoryStatusFilter,
    commune: directoryCommuneFilter,
    thematique: directoryThematiqueFilter,
    limit: 500,
  }, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const [selectedDirectoryEntryId, setSelectedDirectoryEntryId] = useState<number | null>(null);
  const { data: selectedDirectoryEntry, isLoading: selectedDirectoryEntryLoading } =
    trpc.associationDirectory.getById.useQuery(
      { id: selectedDirectoryEntryId || 0 },
      { enabled: selectedDirectoryEntryId !== null }
    );
  const { data: pendingReferenceProposal } = trpc.associationDirectory.getPendingUpdateProposal.useQuery(
    { directoryEntryId: selectedDirectoryEntryId || 0, sourceType: "official_registry" },
    { enabled: selectedDirectoryEntryId !== null }
  );
  const { data: directoryReviews } = trpc.associationDirectory.listReviewQueue.useQuery(
    { status: "pending", limit: 20 },
    { enabled: user?.role === "admin" || user?.role === "super_admin" }
  );
  const selectedAssociationGovernance = useMemo(() => {
    const rawGovernance = selectedDirectoryEntry?.association?.gouvernance || selectedDirectoryEntry?.entry?.gouvernance;
    return rawGovernance ? parseAssociationGovernance(rawGovernance) : null;
  }, [selectedDirectoryEntry]);
  const filteredDirectoryEntries = useMemo(() => {
    const items = directoryEntries?.data || [];
    if (directoryProvenanceFilter === "all") {
      return items;
    }

    return items.filter((entry) => getDirectoryProvenanceConfig(entry).id === directoryProvenanceFilter);
  }, [directoryEntries, directoryProvenanceFilter]);

  const directoryProvenanceSummary = useMemo(() => {
    const buckets = {
      auto_linked: 0,
      pending_validation: 0,
      manual_entry: 0,
      imported: 0,
      portal_enriched: 0,
      helloasso_synced: 0,
    } satisfies Record<DirectoryProvenanceStatus, number>;

    for (const entry of directoryEntries?.data || []) {
      buckets[getDirectoryProvenanceConfig(entry).id] += 1;
    }

    return buckets;
  }, [directoryEntries]);
  const [editingDirectoryEntry, setEditingDirectoryEntry] = useState<DirectoryEntryFormState | null>(null);

  // Search states
  const [associationSearch, setAssociationSearch] = useState('');
  const [associationCommuneFilter, setAssociationCommuneFilter] = useState<AssociationCommuneFilter>("all");
  const [requestSearch, setRequestSearch] = useState('');
  const [requestStatusFilter, setRequestStatusFilter] = useState('all');
  const [requestTypeFilter, setRequestTypeFilter] = useState('all');
  const [billingFilter, setBillingFilter] = useState<"all" | "en_attente" | "en_retard" | "recues" | "validees">("all");
  const [analyticsPeriod, setAnalyticsPeriod] = useState<"7d" | "month" | "quarter" | "semester" | "year">("month");
  const [analyticsAssociationSearch, setAnalyticsAssociationSearch] = useState("");
  const [statsAutomationForm, setStatsAutomationForm] = useState({
    enabled: true,
    recipientEmails: "",
    frequencies: {
      weekly: true,
      month: true,
      quarter: true,
      semester: true,
      year: true,
    },
  });
  const [operationalTourEnvironment, setOperationalTourEnvironment] = useState<OperationalTourEnvironment>("production");
  const [selectedOperationalTourReportId, setSelectedOperationalTourReportId] = useState<string | null>(null);
  const [operationalTourSettingsForm, setOperationalTourSettingsForm] = useState({
    defaultEnvironment: "production" as OperationalTourEnvironment,
    targets: {
      production: {
        baseUrl: "https://www.portail-association973.com",
        associationEmail: "",
        associationPassword: "",
        associationAuthMode: "local" as OperationalTourAuthMode,
        accueilEmail: "",
        accueilPassword: "",
        accueilAuthMode: "local" as OperationalTourAuthMode,
        adminEmail: "",
        adminPassword: "",
        adminAuthMode: "local" as OperationalTourAuthMode,
      },
      secondary: {
        baseUrl: "",
        associationEmail: "",
        associationPassword: "",
        associationAuthMode: "local" as OperationalTourAuthMode,
        accueilEmail: "",
        accueilPassword: "",
        accueilAuthMode: "local" as OperationalTourAuthMode,
        adminEmail: "",
        adminPassword: "",
        adminAuthMode: "local" as OperationalTourAuthMode,
      },
    },
  });

  // Association search
  const { data: associationsData, isLoading: associationsLoading } = trpc.association.search.useQuery({
    search: associationSearch || undefined,
    ville: associationCommuneFilter === "all" ? undefined : associationCommuneFilter,
    limit: 500,
  }, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });

  // Request search  
  const { data: requestsData, isLoading: requestsLoading, refetch: refetchRequests } = trpc.request.search.useQuery({
    search: requestSearch || undefined,
    status: requestStatusFilter === 'all' ? undefined : requestStatusFilter,
    type: requestTypeFilter === 'all' ? undefined : requestTypeFilter,
    limit: 20,
  });
  const { data: billingRequestsData, isLoading: billingRequestsLoading } = trpc.request.search.useQuery({
    type: "demande_salle",
    limit: 200,
  }, {
    enabled: !isLogisticsControl,
    refetchInterval: 30_000,
  });
  const { data: analyticsData, isLoading: analyticsLoading } = trpc.stats.getReservationAnalytics.useQuery(
    { period: analyticsPeriod },
    {
      enabled: canAccessAnalytics,
      refetchInterval: 60_000,
    }
  );
  const { data: statsAutomationSettings } = trpc.stats.getAutomationSettings.useQuery(undefined, {
    enabled: canAccessAnalytics,
    refetchInterval: 60_000,
  });
  const { data: statsReportHistory } = trpc.stats.getReportHistory.useQuery(undefined, {
    enabled: canAccessAnalytics,
    refetchInterval: 60_000,
  });
  const { data: operationalTourSettings } = trpc.operationalTour.getSettings.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const { data: operationalTourHistory } = trpc.operationalTour.getHistory.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
    refetchInterval: 60_000,
  });
  const { data: helloAssoSettings } = trpc.settings.getHelloAsso.useQuery(undefined, {
    enabled: user?.role === "admin" || user?.role === "super_admin",
  });
  const isHelloAssoConfigured = Boolean(helloAssoSettings?.clientId && helloAssoSettings?.clientSecretConfigured);

  const utils = trpc.useUtils();

  // Mutations
  const [selectedRequest, setSelectedRequest] = useState<any>(null);
  const [selectedAssociation, setSelectedAssociation] = useState<any>(null);
  const [processData, setProcessData] = useState({
    status: '',
    commentaireAdmin: '',
    montantAccorde: '',
    recapServiceId: '',
    recapRecipients: '',
    recapServiceLabel: '',
    supervisionServiceId: '',
    supervisionRecipients: '',
    supervisionServiceLabel: '',
  });
  const [dsuProcessData, setDsuProcessData] = useState({
    dateReception: new Date().toISOString().split('T')[0],
    dateDecision: new Date().toISOString().split('T')[0],
    responsableDSU: '',
    observationsDSU: '',
    salleReservation: {
      usageType: 'conventionne' as SalleUsageType,
      frequency: 'journee' as SalleReservationFrequency,
    },
    materielEvent: {
      itemsAccordes: emptyMaterialEventSelection(),
      quantitesAccordees: emptyMaterialEventQuantities(),
      autresPrecisions: '',
      financialDecision: {
        financialMode: 'gratuite' as MaterialFinancialMode,
        depositRequired: false,
        depositAmountEuros: '',
        rentalAmountEuros: '',
        pricingNotes: '',
      },
    },
  });
  const [editingAdminUser, setEditingAdminUser] = useState<null | {
    id?: number;
    name: string;
    email: string;
    role: "user" | "accueil" | "service_terrain" | "logistique_controle" | "admin" | "directrice" | "super_admin";
    canManageLogistics: boolean;
    isActive: boolean;
    password: string;
  }>(null);
  const [logisticsGroupForm, setLogisticsGroupForm] = useState<{
    label: string;
    memberUserIds: number[];
  }>({
    label: "Groupe logistique",
    memberUserIds: [],
  });
  const [editingOperationalRecapService, setEditingOperationalRecapService] = useState<null | {
    id?: number;
    label: string;
    usage: "terrain" | "controle";
    description: string;
    recipientEmails: string;
    actif: boolean;
  }>(null);
  const [logisticsAssignment, setLogisticsAssignment] = useState<null | {
    requestId: number;
    title: string;
    associationName: string;
    boardState?: string;
    recapServiceId: string;
    recapServiceLabel: string;
    recapRecipients: string;
    supervisionServiceId: string;
    supervisionServiceLabel: string;
    supervisionRecipients: string;
  }>(null);
  const [materialReturnLinkOverrides, setMaterialReturnLinkOverrides] = useState<Record<number, {
    uploadLink: string;
    uploadTokenExpiresAt: Date | string;
    uploadLinkState: "active";
    lastReminderSentAt: Date;
    reactivatedAt: Date;
  }>>({});
  const [logisticsDetailVisible, setLogisticsDetailVisible] = useState(false);
  const [mailSettingsForm, setMailSettingsForm] = useState(defaultMailSettingsForm);
  const [helloAssoSettingsForm, setHelloAssoSettingsForm] = useState({
    enabled: true,
    clientId: "",
    clientSecret: "",
  });
  const [associationMapSettingsForm, setAssociationMapSettingsForm] = useState({
    publicStyleUrl: associationMapPublicDefaultStyleUrl,
    adminStyleUrl: associationMapAdminDefaultStyleUrl,
  });
  const [logisticsSettingsForm, setLogisticsSettingsForm] = useState(defaultLogisticsSettingsForm);
  const [appearanceForm, setAppearanceForm] = useState<PortalAppearance>(defaultPortalAppearance);
  const [appearancePreviewEnabled, setAppearancePreviewEnabled] = useState(true);
  const [adminCollapsedSections, setAdminCollapsedSections] = useState<Record<string, boolean>>(adminSectionCollapseDefaults);
  const [appearanceCollapsedPanels, setAppearanceCollapsedPanels] = useState({
    beforeAfterPreview: false,
    targetPreview: true,
  });
  const [appearancePreviewFamily, setAppearancePreviewFamily] = useState<"all" | "public" | "association" | "admin">("all");
  const [appearanceCollapsedTargetGroups, setAppearanceCollapsedTargetGroups] = useState<Record<string, boolean>>(() =>
    Object.fromEntries(appearanceBackgroundTargetGroups.map((group) => [group.id, false])),
  );
  const [manualAppearanceBackgroundTargets, setManualAppearanceBackgroundTargets] = useState<AppearanceBackgroundTarget[]>(
    () => resolveManualBackgroundTargets(defaultPortalAppearance.backgroundTargets),
  );
  const [primaryColorInput, setPrimaryColorInput] = useState(defaultPortalAppearance.primaryColor);
  const [secondaryColorInput, setSecondaryColorInput] = useState(defaultPortalAppearance.secondaryColor);
  const [accentColorInput, setAccentColorInput] = useState(defaultPortalAppearance.accentColor);
  const [buttonColorInput, setButtonColorInput] = useState(defaultPortalAppearance.buttonColor);
  const [alertColorInput, setAlertColorInput] = useState(defaultPortalAppearance.alertColor);
  const [isHeroUploadBusy, setIsHeroUploadBusy] = useState(false);
  const [isAppearanceAssetUploadBusy, setIsAppearanceAssetUploadBusy] = useState<AppearanceAssetField | null>(null);
  const [isAppearanceTargetAssetUploadBusy, setIsAppearanceTargetAssetUploadBusy] = useState<AppearanceBackgroundTarget | null>(null);
  const [isAppearanceDecorationUploadBusy, setIsAppearanceDecorationUploadBusy] = useState<string | null>(null);
  const [isSalleRibUploadBusy, setIsSalleRibUploadBusy] = useState(false);
  const logoImageInputRef = useRef<HTMLInputElement>(null);
  const faviconImageInputRef = useRef<HTMLInputElement>(null);
  const heroImageInputRef = useRef<HTMLInputElement>(null);
  const backgroundImageInputRef = useRef<HTMLInputElement>(null);
  const backgroundTargetImageInputRef = useRef<HTMLInputElement>(null);
  const decorationImageInputRef = useRef<HTMLInputElement>(null);
  const salleRibInputRef = useRef<HTMLInputElement>(null);
  const associationImportInputRef = useRef<HTMLInputElement>(null);
  const [selectedDirectoryFileName, setSelectedDirectoryFileName] = useState("");
  const [directoryPreview, setDirectoryPreview] = useState<null | {
    fileName: string;
    totalRows: number;
    validRows: number;
    missingEmailRows: number;
    duplicateEmailRows: number;
    duplicateGroups: Array<{
      emailOfficielNormalise: string;
      emailOfficiel: string;
      rowNumbers: number[];
      options: Array<{
        sheetName: string;
        rowNumber: number;
        nomAssociation: string;
        ville: string | null;
      }>;
    }>;
    previewRows: Array<{
      sheetName: string;
      rowNumber: number;
      nomAssociation: string;
      emailOfficiel: string | null;
      ville: string | null;
      status: "valid" | "missing_email" | "duplicate_email";
      message: string;
    }>;
  }>(null);
  const [directoryDuplicateSelections, setDirectoryDuplicateSelections] = useState<Record<string, number>>({});
  const [directoryFilePayload, setDirectoryFilePayload] = useState<null | {
    fileData: string;
    fileName: string;
    mimeType: string;
  }>(null);
  const [pendingBackgroundTargetUpload, setPendingBackgroundTargetUpload] = useState<AppearanceBackgroundTarget | null>(null);
  const [pendingDecorationUploadId, setPendingDecorationUploadId] = useState<string | null>(null);
  const [manualGeoForm, setManualGeoForm] = useState({
    latitude: "",
    longitude: "",
    geoPrecision: "exact_address" as "exact_address" | "commune_center" | "hidden",
  });

  const setAdminSectionExpanded = (key: keyof typeof adminSectionCollapseDefaults, open: boolean) => {
    setAdminCollapsedSections((prev) => ({
      ...prev,
      [key]: !open,
    }));
  };

  const buildRecommendedDuplicateSelections = (duplicateGroups: NonNullable<typeof directoryPreview>["duplicateGroups"]) => {
    return Object.fromEntries(
      duplicateGroups
        .map((group) => [group.emailOfficielNormalise, group.options[0]?.rowNumber])
        .filter((entry) => entry[1])
    ) as Record<string, number>;
  };

  const processMutation = trpc.request.process.useMutation({
    onSuccess: () => {
      toast.success(
        selectedRequest?.type === 'demande_salle' && processData.status === 'validee'
          ? "Demande traitée et devis envoyé à l'association"
          : 'Demande traitée avec succès'
      );
      setSelectedRequest(null);
      utils.request.search.invalidate();
      utils.request.getPending.invalidate();
      utils.stats.getDashboard.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || 'Erreur lors du traitement');
    },
  });

  const markNotificationReadMutation = trpc.notifications.markAsRead.useMutation({
    onSuccess: () => {
      utils.notifications.getAll.invalidate();
      utils.notifications.getUnreadCount.invalidate();
    },
  });

  const markAllReadMutation = trpc.notifications.markAllAsRead.useMutation({
    onSuccess: () => {
      toast.success('Toutes les notifications marquées comme lues');
      utils.notifications.getAll.invalidate();
      utils.notifications.getUnreadCount.invalidate();
    },
  });

  const updateStatsAutomationMutation = trpc.stats.updateAutomationSettings.useMutation({
    onSuccess: () => {
      toast.success("Automatisation des rapports mise à jour");
      utils.stats.getAutomationSettings.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre à jour l’automatisation");
    },
  });

  const generateStatsReportNowMutation = trpc.stats.generateReportNow.useMutation({
    onSuccess: () => {
      toast.success("Rapport généré et archivé");
      utils.stats.getReportHistory.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de générer le rapport");
    },
  });

  const saveOperationalTourSettingsMutation = trpc.operationalTour.saveSettings.useMutation({
    onSuccess: () => {
      toast.success("Configuration du tour opérationnel mise à jour");
      utils.operationalTour.getSettings.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de sauvegarder la configuration");
    },
  });

  const setUserOperatingTourMutation = trpc.settings.setUserOperatingTour.useMutation({
    onSuccess: () => {
      toast.success("Tour guide utilisateur mis à jour");
      utils.settings.getUserOperatingTour.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre a jour le tour guide utilisateur");
    },
  });

  const clearOperationalTourHistoryMutation = trpc.operationalTour.clearHistory.useMutation({
    onSuccess: () => {
      toast.success("Historique ancien nettoyé");
      utils.operationalTour.getHistory.invalidate();
      utils.operationalTour.getLatest.invalidate();
      setSelectedOperationalTourReportId(null);
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de nettoyer l’historique");
    },
  });

  const runOperationalTourMutation = trpc.operationalTour.run.useMutation({
    onSuccess: (report) => {
      toast.success(
        report.status === "ok"
          ? "Tour opérationnel terminé : site opérationnel"
          : report.status === "warning"
            ? "Tour opérationnel terminé avec avertissements"
            : "Tour opérationnel terminé avec erreurs"
      );
      utils.operationalTour.getHistory.invalidate();
      utils.operationalTour.getLatest.invalidate();
      setSelectedOperationalTourReportId(report.id);
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de lancer le tour opérationnel");
    },
  });

  const createAdminUserMutation = trpc.adminUsers.create.useMutation({
    onSuccess: (result) => {
      toast.success(
        result.reusedExistingAccount
          ? "Compte interne rattaché à une adresse déjà existante"
          : "Compte interne créé"
      );
      setEditingAdminUser(null);
      utils.adminUsers.listAll.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de créer le compte administrateur");
    },
  });

  const updateAdminUserMutation = trpc.adminUsers.update.useMutation({
    onSuccess: () => {
      toast.success("Compte administrateur mis à jour");
      setEditingAdminUser(null);
      utils.adminUsers.listAll.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre à jour le compte");
    },
  });

  const toggleAdminUserActiveMutation = trpc.adminUsers.toggleActive.useMutation({
    onSuccess: () => {
      toast.success("Statut du compte mis à jour");
      utils.adminUsers.listAll.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de modifier l’état du compte");
    },
  });

  const deleteAdminUserMutation = trpc.adminUsers.delete.useMutation({
    onSuccess: () => {
      toast.success("Compte interne supprimé");
      utils.adminUsers.listAll.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de supprimer ce compte");
    },
  });

  // Query for selected association documents
  const { data: associationDocuments, isLoading: documentsLoading } = trpc.document.getByAssociationId.useQuery(
    { associationId: selectedAssociation?.id || 0 },
    { enabled: !!selectedAssociation }
  );
  const { data: logisticsAssignmentRequest, isLoading: logisticsAssignmentRequestLoading } = trpc.request.getById.useQuery(
    { id: logisticsAssignment?.requestId || 0 },
    { enabled: Boolean(logisticsAssignment?.requestId) }
  );

  const toggleAssociationMutation = trpc.association.toggleStatus.useMutation({
    onSuccess: () => {
      toast.success('Statut mis à jour');
      utils.association.search.invalidate();
    },
  });

  const setAppearanceMutation = trpc.settings.setAppearance.useMutation({
    onSuccess: (savedAppearance) => {
      toast.success("Apparence du portail mise à jour");
      setAppearanceForm(savedAppearance);
      utils.settings.getAppearance.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d'enregistrer l'apparence");
    },
  });

  const uploadHeroImageMutation = trpc.settings.uploadHeroImage.useMutation({
    onSuccess: (savedAppearance) => {
      toast.success("Illustration d’accueil mise à jour");
      setAppearanceForm(savedAppearance);
      utils.settings.getAppearance.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre à jour l’illustration");
    },
  });

  const uploadAppearanceAssetMutation = trpc.settings.uploadAppearanceAsset.useMutation({
    onSuccess: (savedAppearance, variables) => {
      const assetLabels: Record<AppearanceAssetField, string> = {
        logoUrl: "Logo mis à jour",
        faviconUrl: "Favicon mis à jour",
        heroImageUrl: "Illustration d’accueil mise à jour",
        backgroundImageUrl: "Arrière-plan global mis à jour",
      };
      toast.success(assetLabels[variables.field]);
      setAppearanceForm(savedAppearance);
      utils.settings.getAppearance.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre à jour ce visuel");
    },
  });

  const uploadAppearanceTargetAssetMutation = trpc.settings.uploadAppearanceTargetAsset.useMutation({
    onSuccess: (savedAppearance, variables) => {
      toast.success(`Skin mis a jour pour ${appearanceBackgroundTargetLabels[variables.target]}`);
      setAppearanceForm(savedAppearance);
      utils.settings.getAppearance.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre a jour ce skin cible");
    },
  });

  const uploadAppearanceDecorationAssetMutation = trpc.settings.uploadAppearanceDecorationAsset.useMutation({
    onSuccess: (savedAppearance) => {
      toast.success("Élément décoratif mis à jour");
      setAppearanceForm(savedAppearance);
      utils.settings.getAppearance.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre à jour cet élément décoratif");
    },
  });

  const uploadSalleRibMutation = trpc.settings.uploadSalleRib.useMutation({
    onSuccess: () => {
      toast.success("RIB MJS enregistré");
      utils.settings.getSalleBilling.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d’enregistrer le RIB MJS");
    },
  });

  const clearSalleRibMutation = trpc.settings.clearSalleRib.useMutation({
    onSuccess: () => {
      toast.success("RIB MJS retiré");
      utils.settings.getSalleBilling.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de retirer le RIB MJS");
    },
  });

  const setMailSettingsMutation = trpc.settings.setMail.useMutation({
    onSuccess: (savedSettings) => {
      toast.success("Configuration mail enregistrée");
      setMailSettingsForm((prev) => ({
        ...prev,
        smtpProvider: savedSettings.smtpProvider,
        smtpHost: savedSettings.smtpHost,
        smtpPort: savedSettings.smtpPort,
        smtpUser: savedSettings.smtpUser,
        smtpFrom: savedSettings.smtpFrom,
        smtpSecure: savedSettings.smtpSecure,
        smtpRequireTls: savedSettings.smtpRequireTls,
        smtpPass: "",
      }));
      utils.settings.getMail.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d’enregistrer la configuration mail");
    },
  });

  const sendTestMailMutation = trpc.settings.sendTestMail.useMutation({
    onSuccess: () => {
      toast.success("Email de test envoyé");
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d’envoyer l’email de test");
    },
  });

  const setHelloAssoSettingsMutation = trpc.settings.setHelloAsso.useMutation({
    onSuccess: () => {
      toast.success("Réglages HelloAsso enregistrés");
      setHelloAssoSettingsForm((prev) => ({ ...prev, clientSecret: "" }));
      utils.settings.getHelloAsso.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d’enregistrer les réglages HelloAsso");
    },
  });

  const setAssociationMapSettingsMutation = trpc.settings.setAssociationMap.useMutation({
    onSuccess: () => {
      toast.success("Réglages cartographiques enregistrés");
      utils.settings.getAssociationMap.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d’enregistrer les réglages cartographiques");
    },
  });

  const setLogisticsSettingsMutation = trpc.settings.setLogistics.useMutation({
    onSuccess: (savedSettings) => {
      toast.success("Réglages logistiques enregistrés");
      setLogisticsSettingsForm({
        materialReturnLeadDays: savedSettings.materialReturnLeadDays,
        materialReturnGraceDays: savedSettings.materialReturnGraceDays,
        inventory: Object.fromEntries(
          Object.entries(savedSettings.inventory).map(([key, value]) => [key, value === null ? "" : String(value)])
        ),
        replacementValues: Object.fromEntries(
          Object.entries(savedSettings.replacementValues).map(([key, value]) => [key, formatCentsAsEuroInput(value)])
        ),
      });
      utils.settings.getLogistics.invalidate();
      utils.materialReturn.listAdminBoard.invalidate();
      utils.request.getMaterialCalendar.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d’enregistrer les réglages logistiques");
    },
  });
  const setLogisticsGroupMutation = trpc.settings.setLogisticsGroup.useMutation({
    onSuccess: (savedGroup) => {
      toast.success("Groupe logistique mis à jour");
      setLogisticsGroupForm({
        label: savedGroup.label,
        memberUserIds: savedGroup.memberUserIds,
      });
      utils.settings.getLogisticsGroup.invalidate();
      utils.auth.me.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d'enregistrer le groupe logistique");
    },
  });

  const createOperationalRecapServiceMutation = trpc.operationalRecapService.create.useMutation({
    onSuccess: () => {
      toast.success("Service interne créé");
      setEditingOperationalRecapService(null);
      utils.operationalRecapService.listAll.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de créer le service");
    },
  });

  const updateOperationalRecapServiceMutation = trpc.operationalRecapService.update.useMutation({
    onSuccess: () => {
      toast.success("Service interne mis à jour");
      setEditingOperationalRecapService(null);
      utils.operationalRecapService.listAll.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre à jour le service");
    },
  });

  const deleteOperationalRecapServiceMutation = trpc.operationalRecapService.delete.useMutation({
    onSuccess: () => {
      toast.success("Service interne supprimé");
      utils.operationalRecapService.listAll.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de supprimer le service");
    },
  });

  const assignMaterialReturnMutation = trpc.materialReturn.assign.useMutation({
    onSuccess: (result, variables) => {
      toast.success(
        result.emailSent
          ? "Récupération assignée et email envoyé"
          : "Récupération assignée, mais l'email n'a pas pu partir automatiquement"
      );
      setLogisticsAssignment(null);
      utils.materialReturn.listAdminBoard.invalidate();
      utils.materialReturn.getByRequestId.invalidate({ requestId: variables.requestId });
      utils.request.search.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d'assigner la récupération");
    },
  });

  const reactivateMaterialReturnMutation = trpc.materialReturn.reactivate.useMutation({
    onSuccess: (result, variables) => {
      const now = new Date();
      setMaterialReturnLinkOverrides((prev) => ({
        ...prev,
        [variables.requestId]: {
          uploadLink: result.uploadLink,
          uploadTokenExpiresAt: result.uploadTokenExpiresAt,
          uploadLinkState: "active",
          lastReminderSentAt: now,
          reactivatedAt: now,
        },
      }));
      toast.success(
        result.emailSent
          ? "Lien de restitution réactivé et envoyé"
          : "Lien de restitution réactivé, mais l'email n'a pas pu partir automatiquement"
      );
      utils.materialReturn.listAdminBoard.invalidate();
      utils.materialReturn.getByRequestId.invalidate({ requestId: variables.requestId });
      utils.request.search.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de réactiver la restitution");
    },
  });

  const parseRecipientText = (value: string) =>
    value
      .split(/[,;\n]/)
      .map((entry) => entry.trim())
      .filter(Boolean);

  const handleSaveAdminUser = () => {
    if (!editingAdminUser) return;

    const name = editingAdminUser.name.trim();
    const email = editingAdminUser.email.trim();
    const password = editingAdminUser.password.trim();

    if (!name) {
      toast.error("Le nom est obligatoire");
      return;
    }

    if (!email) {
      toast.error("L’adresse email est obligatoire");
      return;
    }

    if (editingAdminUser.id) {
      updateAdminUserMutation.mutate({
        id: editingAdminUser.id,
        name,
        email,
        role: editingAdminUser.role,
        canManageLogistics:
          editingAdminUser.role === "super_admin"
          || editingAdminUser.role === "logistique_controle"
            ? true
            : editingAdminUser.role === "admin"
              ? editingAdminUser.canManageLogistics
              : false,
        isActive: editingAdminUser.isActive,
        ...(password ? { password } : {}),
      });
      return;
    }

    if (
      editingAdminUser.role !== "accueil"
      && editingAdminUser.role !== "service_terrain"
      && editingAdminUser.role !== "logistique_controle"
      && editingAdminUser.role !== "admin"
      && editingAdminUser.role !== "directrice"
      && editingAdminUser.role !== "super_admin"
    ) {
      toast.error("Choisis un rôle administrateur valide");
      return;
    }

    createAdminUserMutation.mutate({
      name,
      email,
      role: editingAdminUser.role,
      canManageLogistics:
        editingAdminUser.role === "super_admin"
        || editingAdminUser.role === "logistique_controle"
          ? true
          : editingAdminUser.canManageLogistics,
    });
  };

  const toggleLogisticsGroupMember = (userId: number, checked: boolean) => {
    setLogisticsGroupForm((prev) => ({
      ...prev,
      memberUserIds: checked
        ? Array.from(new Set([...prev.memberUserIds, userId]))
        : prev.memberUserIds.filter((entry) => entry !== userId),
    }));
  };

  const handleSaveLogisticsGroup = () => {
    setLogisticsGroupMutation.mutate({
      label: logisticsGroupForm.label.trim() || "Groupe logistique",
      memberUserIds: logisticsGroupForm.memberUserIds,
    });
  };

  const handleSaveHelloAssoSettings = () => {
    if (!helloAssoSettingsForm.clientId.trim()) {
      toast.error("Le client ID HelloAsso est obligatoire");
      return;
    }

    if (!helloAssoSettings?.clientSecretConfigured && !helloAssoSettingsForm.clientSecret.trim()) {
      toast.error("Le client secret HelloAsso est obligatoire lors de la première configuration");
      return;
    }

    setHelloAssoSettingsMutation.mutate({
      enabled: helloAssoSettingsForm.enabled,
      clientId: helloAssoSettingsForm.clientId.trim(),
      clientSecret: helloAssoSettingsForm.clientSecret.trim(),
    });
  };

  const handleSaveAssociationMapSettings = () => {
    if (!associationMapSettingsForm.publicStyleUrl.trim()) {
      toast.error("L’URL du style public est obligatoire");
      return;
    }

    if (!associationMapSettingsForm.adminStyleUrl.trim()) {
      toast.error("L’URL du style administration est obligatoire");
      return;
    }

    setAssociationMapSettingsMutation.mutate({
      publicStyleUrl: associationMapSettingsForm.publicStyleUrl.trim(),
      adminStyleUrl: associationMapSettingsForm.adminStyleUrl.trim(),
    });
  };

  const handleSyncHelloAssoBatch = () => {
    syncDirectoryHelloAssoBatchMutation.mutate({
      search: directorySearch || undefined,
      registrationStatus: directoryStatusFilter,
      commune: directoryCommuneFilter,
      thematique: directoryThematiqueFilter,
      limit: 50,
    });
  };

  const openDirectoryEntryCreation = (review?: DirectoryReviewItem) => {
    setEditingDirectoryEntry({
      ...createEmptyDirectoryEntryForm(),
      reviewId: review?.id,
      nomAssociation: review?.proposedNomAssociation || "",
      emailOfficiel: review?.proposedEmail || "",
      siret: review?.proposedSiret || "",
      rna: review?.proposedRna || "",
      ville: review?.proposedVille || "",
      telephone: review?.proposedTelephone || "",
      nomRepresentant: review?.proposedNomRepresentant || "",
    });
  };

  const openDirectoryEntryEdition = () => {
    if (!selectedDirectoryEntry) return;

    setEditingDirectoryEntry({
      id: selectedDirectoryEntry.entry.id,
      nomAssociation: selectedDirectoryEntry.entry.nomAssociation || "",
      emailOfficiel: selectedDirectoryEntry.entry.emailOfficiel || "",
      siret: selectedDirectoryEntry.entry.siret || "",
      rna: selectedDirectoryEntry.entry.rna || "",
      thematiques: parseAssociationThematics(selectedDirectoryEntry.entry.thematique),
      adresse: selectedDirectoryEntry.entry.adresse || "",
      codePostal: selectedDirectoryEntry.entry.codePostal || "",
      ville: selectedDirectoryEntry.entry.ville || "",
      telephone: selectedDirectoryEntry.entry.telephone || "",
      siteWeb: selectedDirectoryEntry.entry.siteWeb || "",
      facebookUrl: selectedDirectoryEntry.entry.facebookUrl || "",
      instagramUrl: selectedDirectoryEntry.entry.instagramUrl || "",
      dateCreation: selectedDirectoryEntry.entry.dateCreation
        ? new Date(selectedDirectoryEntry.entry.dateCreation).toISOString().split("T")[0]
        : "",
      objetAssociation: selectedDirectoryEntry.entry.objetAssociation || "",
      statutJuridique: selectedDirectoryEntry.entry.statutJuridique || "association_loi_1901",
      nomRepresentant: selectedDirectoryEntry.entry.nomRepresentant || "",
      fonctionRepresentant: selectedDirectoryEntry.entry.fonctionRepresentant || "",
      isActive: selectedDirectoryEntry.entry.isActive,
    });
  };

  const handleSaveDirectoryEntry = () => {
    if (!editingDirectoryEntry) return;

    const payload = {
      reviewId: editingDirectoryEntry.reviewId,
      nomAssociation: editingDirectoryEntry.nomAssociation.trim(),
      emailOfficiel: editingDirectoryEntry.emailOfficiel.trim(),
      siret: editingDirectoryEntry.siret.trim(),
      rna: editingDirectoryEntry.rna.trim(),
      thematiques: editingDirectoryEntry.thematiques,
      adresse: editingDirectoryEntry.adresse.trim(),
      codePostal: editingDirectoryEntry.codePostal.trim(),
      ville: editingDirectoryEntry.ville.trim(),
      telephone: editingDirectoryEntry.telephone.trim(),
      siteWeb: editingDirectoryEntry.siteWeb.trim(),
      facebookUrl: editingDirectoryEntry.facebookUrl.trim(),
      instagramUrl: editingDirectoryEntry.instagramUrl.trim(),
      dateCreation: editingDirectoryEntry.dateCreation,
      objetAssociation: editingDirectoryEntry.objetAssociation.trim(),
      statutJuridique: editingDirectoryEntry.statutJuridique,
      nomRepresentant: editingDirectoryEntry.nomRepresentant.trim(),
      fonctionRepresentant: editingDirectoryEntry.fonctionRepresentant.trim(),
      isActive: editingDirectoryEntry.isActive,
    };

    if (!payload.nomAssociation) {
      toast.error("Le nom de l'association est obligatoire");
      return;
    }

    if (editingDirectoryEntry.id) {
      updateManualDirectoryEntryMutation.mutate({
        id: editingDirectoryEntry.id,
        ...payload,
      });
      return;
    }

    createManualDirectoryEntryMutation.mutate(payload);
  };

  const handleSaveManualCoordinates = () => {
    if (!selectedDirectoryEntryId) return;

    const latitude = manualGeoForm.latitude.trim();
    const longitude = manualGeoForm.longitude.trim();

    if (!latitude && !longitude) {
      setDirectoryManualCoordinatesMutation.mutate({
        id: selectedDirectoryEntryId,
        latitude: null,
        longitude: null,
        geoPrecision: manualGeoForm.geoPrecision,
      });
      return;
    }

    const latNumber = Number(latitude);
    const lngNumber = Number(longitude);
    if (Number.isNaN(latNumber) || Number.isNaN(lngNumber)) {
      toast.error("Les coordonnées doivent être numériques");
      return;
    }

    setDirectoryManualCoordinatesMutation.mutate({
      id: selectedDirectoryEntryId,
      latitude: latNumber,
      longitude: lngNumber,
      geoPrecision: manualGeoForm.geoPrecision,
    });
  };

  const applyOperationalRecapServiceToProcessData = (serviceId: string) => {
    const service = normalizedOperationalRecapServices.find((entry) => String(entry?.id) === serviceId);
    if (!service) {
      setProcessData((prev) => ({
        ...prev,
        recapServiceId: '',
      }));
      return;
    }

    setProcessData((prev) => ({
      ...prev,
      recapServiceId: String(service.id),
      recapServiceLabel: service.label,
      recapRecipients: service.recipientEmails.join(", "),
    }));
  };
  const applyOperationalSupervisionServiceToProcessData = (serviceId: string) => {
    const service = normalizedOperationalRecapServices.find((entry) => String(entry?.id) === serviceId);
    if (!service) {
      setProcessData((prev) => ({
        ...prev,
        supervisionServiceId: '',
      }));
      return;
    }

    setProcessData((prev) => ({
      ...prev,
      supervisionServiceId: String(service.id),
      supervisionServiceLabel: service.label,
      supervisionRecipients: service.recipientEmails.join(", "),
    }));
  };

  const applyOperationalRecapServiceToLogisticsAssignment = (serviceId: string) => {
    const service = normalizedOperationalRecapServices.find((entry) => String(entry?.id) === serviceId);
    if (!service) {
      setLogisticsAssignment((prev) => prev ? { ...prev, recapServiceId: "" } : prev);
      return;
    }

    setLogisticsAssignment((prev) =>
      prev
        ? {
            ...prev,
            recapServiceId: String(service.id),
            recapServiceLabel: service.label,
            recapRecipients: service.recipientEmails.join(", "),
          }
        : prev
    );
  };

  const applyOperationalSupervisionServiceToLogisticsAssignment = (serviceId: string) => {
    const service = normalizedOperationalRecapServices.find((entry) => String(entry?.id) === serviceId);
    if (!service) {
      setLogisticsAssignment((prev) => prev ? { ...prev, supervisionServiceId: "" } : prev);
      return;
    }

    setLogisticsAssignment((prev) =>
      prev
        ? {
            ...prev,
            supervisionServiceId: String(service.id),
            supervisionServiceLabel: service.label,
            supervisionRecipients: service.recipientEmails.join(", "),
          }
        : prev
    );
  };

  const openLogisticsAssignmentDialog = (entry: {
    requestId: number;
    title: string;
    associationName: string;
    boardState?: string;
    serviceLabel?: string;
    recipientEmails?: string[];
    supervisionServiceLabel?: string;
    supervisionRecipientEmails?: string[];
  }) => {
    if (entry.boardState === "conforme") {
      toast.info("Cette restitution est déjà clôturée. Le service terrain peut la consulter, mais plus la réassigner.");
      return;
    }
    setLogisticsAssignment({
      requestId: entry.requestId,
      title: entry.title,
      associationName: entry.associationName,
      boardState: entry.boardState,
      recapServiceId: "",
      recapServiceLabel: entry.serviceLabel || "",
      recapRecipients: Array.isArray(entry.recipientEmails) ? entry.recipientEmails.join(", ") : "",
      supervisionServiceId: "",
      supervisionServiceLabel: entry.supervisionServiceLabel || "",
      supervisionRecipients: Array.isArray(entry.supervisionRecipientEmails) ? entry.supervisionRecipientEmails.join(", ") : "",
    });
    setLogisticsDetailVisible(false);
  };

  const handleAssignMaterialReturn = () => {
    if (!logisticsAssignment) return;
    const recipients = parseRecipientText(logisticsAssignment.recapRecipients);
    if (!logisticsAssignment.recapServiceLabel.trim() && !logisticsAssignment.recapServiceId) {
      toast.error("Choisis un service ou renseigne un libellé de mission");
      return;
    }
    if (recipients.length === 0) {
      toast.error("Ajoute au moins une adresse email pour la récupération");
      return;
    }

    assignMaterialReturnMutation.mutate({
      requestId: logisticsAssignment.requestId,
      recapServiceId: logisticsAssignment.recapServiceId ? parseInt(logisticsAssignment.recapServiceId, 10) : undefined,
      recapServiceLabel: logisticsAssignment.recapServiceLabel.trim() || undefined,
      recapRecipients: recipients,
      supervisionServiceId: logisticsAssignment.supervisionServiceId ? parseInt(logisticsAssignment.supervisionServiceId, 10) : undefined,
      supervisionServiceLabel: logisticsAssignment.supervisionServiceLabel.trim() || undefined,
      supervisionRecipients: parseRecipientText(logisticsAssignment.supervisionRecipients),
    });
  };

  const handleReactivateMaterialReturn = (entry: {
    requestId: number;
    serviceLabel?: string;
    recipientEmails?: string[];
    supervisionServiceLabel?: string;
    supervisionRecipientEmails?: string[];
  }, reason: string) => {
    reactivateMaterialReturnMutation.mutate({
      requestId: entry.requestId,
      recapServiceLabel: entry.serviceLabel || undefined,
      recapRecipients: Array.isArray(entry.recipientEmails) && entry.recipientEmails.length > 0 ? entry.recipientEmails : undefined,
      supervisionServiceLabel: entry.supervisionServiceLabel || undefined,
      supervisionRecipients:
        Array.isArray(entry.supervisionRecipientEmails) && entry.supervisionRecipientEmails.length > 0
          ? entry.supervisionRecipientEmails
          : undefined,
      reason,
    });
  };

  const handleSaveOperationalRecapService = () => {
    if (!editingOperationalRecapService) return;

    const recipientEmails = parseRecipientText(editingOperationalRecapService.recipientEmails);
    if (!editingOperationalRecapService.label.trim()) {
      toast.error("Le nom du service est obligatoire");
      return;
    }
    if (recipientEmails.length === 0) {
      toast.error("Ajoute au moins une adresse email");
      return;
    }

    const payload = {
      label: editingOperationalRecapService.label.trim(),
      usage: editingOperationalRecapService.usage,
      description: editingOperationalRecapService.description.trim() || undefined,
      recipientEmails,
      actif: editingOperationalRecapService.actif,
    };

    if (editingOperationalRecapService.id) {
      updateOperationalRecapServiceMutation.mutate({
        id: editingOperationalRecapService.id,
        ...payload,
      });
      return;
    }

    createOperationalRecapServiceMutation.mutate(payload);
  };

  const previewDirectoryImportMutation = trpc.associationDirectory.previewImport.useMutation({
    onSuccess: (preview) => {
      setDirectoryPreview(preview);
      const defaultSelections = buildRecommendedDuplicateSelections(preview.duplicateGroups || []);
      setDirectoryDuplicateSelections(defaultSelections);
      toast.success("Aperçu du bordereau prêt");
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de lire le bordereau");
    },
  });

  const importDirectoryMutation = trpc.associationDirectory.importWorkbook.useMutation({
    onSuccess: (result) => {
      toast.success(`Import terminé : ${result.created} création(s), ${result.updated} mise(s) à jour`);
      setDirectoryPreview(null);
      setDirectoryDuplicateSelections({});
      setDirectoryFilePayload(null);
      setSelectedDirectoryFileName("");
      utils.associationDirectory.getSummary.invalidate();
      utils.associationDirectory.listLatest.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d'importer le bordereau");
    },
  });

  const sendDirectoryInvitationMutation = trpc.associationInvitation.send.useMutation({
    onSuccess: (result) => {
      toast.success(
        result.emailSent
          ? "Invitation envoyée à l'adresse officielle."
          : "Invitation créée, mais l'email n'a pas pu être envoyé."
      );
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d'envoyer l'invitation");
    },
  });

  const secureDirectoryInvitationLinkMutation = trpc.associationInvitation.getSecureLink.useMutation({
    onError: (error) => {
      toast.error(error.message || "Impossible de récupérer le lien sécurisé");
    },
  });

  const syncDirectoryGeoMutation = trpc.associationDirectory.syncExternalData.useMutation({
    onSuccess: (result) => {
      toast.success(
        result.geoSource
          ? `Géolocalisation mise à jour via ${associationGeoSourceLabels[result.geoSource]}`
          : "Aucune position exploitable n'a été trouvée"
      );
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getById.invalidate({ id: selectedDirectoryEntryId });
      }
      utils.associationDirectory.listMap.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de synchroniser cette association");
    },
  });

  const syncDirectoryReferenceMutation = trpc.associationDirectory.syncReferenceData.useMutation({
    onSuccess: (result) => {
      toast.success(
        result.hasChanges
          ? "Proposition d’enrichissement officielle prête à valider"
          : result.summary || "Aucune différence utile détectée"
      );
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getById.invalidate({ id: selectedDirectoryEntryId });
        utils.associationDirectory.getPendingUpdateProposal.invalidate({
          directoryEntryId: selectedDirectoryEntryId,
          sourceType: "official_registry",
        });
      }
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre à jour les données de référence");
    },
  });

  const syncDirectoryReferenceBatchMutation = trpc.associationDirectory.syncReferenceDataBatch.useMutation({
    onSuccess: (result) => {
      toast.success(
        `Sources officielles : ${result.proposalsCreated} proposition(s), ${result.unchanged} fiche(s) inchangée(s), ${result.failed} échec(s).`
      );
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getPendingUpdateProposal.invalidate({
          directoryEntryId: selectedDirectoryEntryId,
          sourceType: "official_registry",
        });
      }
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de rechercher les mises à jour officielles");
    },
  });

  const applyDirectoryUpdateProposalMutation = trpc.associationDirectory.applyUpdateProposal.useMutation({
    onSuccess: () => {
      toast.success("Mise à jour validée et appliquée au bordereau");
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getById.invalidate({ id: selectedDirectoryEntryId });
        utils.associationDirectory.getPendingUpdateProposal.invalidate({
          directoryEntryId: selectedDirectoryEntryId,
          sourceType: "official_registry",
        });
      }
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d’appliquer cette proposition");
    },
  });

  const dismissDirectoryUpdateProposalMutation = trpc.associationDirectory.dismissUpdateProposal.useMutation({
    onSuccess: () => {
      toast.success("Proposition ignorée");
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getPendingUpdateProposal.invalidate({
          directoryEntryId: selectedDirectoryEntryId,
          sourceType: "official_registry",
        });
      }
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d’ignorer cette proposition");
    },
  });

  const syncDirectoryHelloAssoMutation = trpc.associationDirectory.syncHelloAssoById.useMutation({
    onSuccess: (result) => {
      toast.success(
        result.matched
          ? `Fiche mise à jour depuis HelloAsso${result.slug ? ` (${result.slug})` : ""}`
          : result.reason
      );
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getById.invalidate({ id: selectedDirectoryEntryId });
      }
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de synchroniser avec HelloAsso");
    },
  });

  const syncDirectoryHelloAssoBatchMutation = trpc.associationDirectory.syncHelloAssoBatch.useMutation({
    onSuccess: (result) => {
      toast.success(
        `HelloAsso : ${result.updated} fiche(s) mise(s) à jour, ${result.skipped} fiche(s) laissée(s) inchangée(s).`
      );
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getById.invalidate({ id: selectedDirectoryEntryId });
      }
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de lancer la synchronisation HelloAsso");
    },
  });

  const createManualDirectoryEntryMutation = trpc.associationDirectory.createManual.useMutation({
    onSuccess: (result) => {
      toast.success("Association ajoutée au bordereau");
      setEditingDirectoryEntry(null);
      setSelectedDirectoryEntryId(result.id);
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      utils.associationDirectory.listReviewQueue.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d'ajouter cette association au bordereau");
    },
  });

  const updateManualDirectoryEntryMutation = trpc.associationDirectory.updateManual.useMutation({
    onSuccess: () => {
      toast.success("Fiche du bordereau mise à jour");
      setEditingDirectoryEntry(null);
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      utils.associationDirectory.listReviewQueue.invalidate();
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getById.invalidate({ id: selectedDirectoryEntryId });
      }
      utils.associationDirectory.listMap.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de mettre à jour cette fiche");
    },
  });

  const resolveDirectoryReviewLinkMutation = trpc.associationDirectory.resolveReviewLink.useMutation({
    onSuccess: () => {
      toast.success("Correspondance validée et fiche liée");
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      utils.associationDirectory.listReviewQueue.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de valider ce rapprochement");
    },
  });

  const ignoreDirectoryReviewMutation = trpc.associationDirectory.ignoreReview.useMutation({
    onSuccess: () => {
      toast.success("Correspondance écartée");
      utils.associationDirectory.listReviewQueue.invalidate();
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d'ignorer cette proposition");
    },
  });

  const setDirectoryManualCoordinatesMutation = trpc.associationDirectory.setManualCoordinates.useMutation({
    onSuccess: () => {
      toast.success("Coordonnées manuelles enregistrées");
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.listMap.invalidate();
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getById.invalidate({ id: selectedDirectoryEntryId });
      }
    },
    onError: (error) => {
      toast.error(error.message || "Impossible d’enregistrer les coordonnées");
    },
  });

  const syncDirectoryGeoBatchMutation = trpc.associationDirectory.syncExternalDataBatch.useMutation({
    onSuccess: (result) => {
      toast.success(`Synchronisation terminée : ${result.updated} position(s) calculée(s), ${result.unresolved} sans résultat.`);
      utils.associationDirectory.listLatest.invalidate();
      utils.associationDirectory.getSummary.invalidate();
      utils.associationDirectory.listMap.invalidate();
      if (selectedDirectoryEntryId) {
        utils.associationDirectory.getById.invalidate({ id: selectedDirectoryEntryId });
      }
    },
    onError: (error) => {
      toast.error(error.message || "Impossible de lancer la synchronisation géographique");
    },
  });

  useEffect(() => {
    if (appearanceSettings) {
      setAppearanceForm(appearanceSettings);
      setManualAppearanceBackgroundTargets(resolveManualBackgroundTargets(appearanceSettings.backgroundTargets));
    }
  }, [appearanceSettings]);

  useEffect(() => {
    if (typeof window === "undefined") return;

    const storageKey = `portal-admin-collapsed-sections:${user?.id || "anonymous"}`;
    const rawValue = window.localStorage.getItem(storageKey);
    if (!rawValue) {
      setAdminCollapsedSections(adminSectionCollapseDefaults);
      return;
    }

    try {
      const parsedValue = JSON.parse(rawValue) as Record<string, boolean>;
      setAdminCollapsedSections({
        ...adminSectionCollapseDefaults,
        ...parsedValue,
      });
    } catch {
      setAdminCollapsedSections(adminSectionCollapseDefaults);
    }
  }, [user?.id]);

  useEffect(() => {
    if (typeof window === "undefined") return;
    const storageKey = `portal-admin-collapsed-sections:${user?.id || "anonymous"}`;
    window.localStorage.setItem(storageKey, JSON.stringify(adminCollapsedSections));
  }, [adminCollapsedSections, user?.id]);

  useEffect(() => {
    if (!mailSettings) return;

    setMailSettingsForm((prev) => ({
      smtpProvider: mailSettings.smtpProvider,
      smtpHost: mailSettings.smtpHost,
      smtpPort: mailSettings.smtpPort,
      smtpUser: mailSettings.smtpUser,
      smtpFrom: mailSettings.smtpFrom,
      smtpSecure: mailSettings.smtpSecure,
      smtpRequireTls: mailSettings.smtpRequireTls,
      smtpPass: "",
      testRecipient: prev.testRecipient || user?.email || "",
    }));
  }, [mailSettings, user?.email]);

  useEffect(() => {
    if (!helloAssoSettings) return;
    setHelloAssoSettingsForm((prev) => ({
      ...prev,
      enabled: helloAssoSettings.enabled,
      clientId: helloAssoSettings.clientId || "",
      clientSecret: "",
    }));
  }, [helloAssoSettings]);

  useEffect(() => {
    if (!associationMapSettings) return;
    setAssociationMapSettingsForm({
      publicStyleUrl: associationMapSettings.publicStyleUrl || associationMapSettings.styleUrl || associationMapPublicDefaultStyleUrl,
      adminStyleUrl: associationMapSettings.adminStyleUrl || associationMapAdminDefaultStyleUrl,
    });
  }, [associationMapSettings]);

  useEffect(() => {
    if (!selectedDirectoryEntry) return;
    setManualGeoForm({
      latitude: selectedDirectoryEntry.entry.latitude || "",
      longitude: selectedDirectoryEntry.entry.longitude || "",
      geoPrecision: (selectedDirectoryEntry.entry.geoPrecision as "exact_address" | "commune_center" | "hidden") || "exact_address",
    });
  }, [selectedDirectoryEntry]);

  useEffect(() => {
    if (!logisticsSettings) return;
    setLogisticsSettingsForm({
      materialReturnLeadDays: logisticsSettings.materialReturnLeadDays,
      materialReturnGraceDays: logisticsSettings.materialReturnGraceDays,
      inventory: Object.fromEntries(
        Object.entries(logisticsSettings.inventory).map(([key, value]) => [key, value === null ? "" : String(value)])
      ),
      replacementValues: Object.fromEntries(
        Object.entries(logisticsSettings.replacementValues).map(([key, value]) => [key, formatCentsAsEuroInput(value)])
      ),
    });
  }, [logisticsSettings]);

  useEffect(() => {
    setPrimaryColorInput(appearanceForm.primaryColor);
    setSecondaryColorInput(appearanceForm.secondaryColor);
    setAccentColorInput(appearanceForm.accentColor);
    setButtonColorInput(appearanceForm.buttonColor);
    setAlertColorInput(appearanceForm.alertColor);
  }, [
    appearanceForm.primaryColor,
    appearanceForm.secondaryColor,
    appearanceForm.accentColor,
    appearanceForm.buttonColor,
    appearanceForm.alertColor,
  ]);

  useEffect(() => {
    if (!statsAutomationSettings) return;
    setStatsAutomationForm({
      enabled: Boolean(statsAutomationSettings.enabled),
      recipientEmails: Array.isArray(statsAutomationSettings.recipientEmails)
        ? statsAutomationSettings.recipientEmails.join(", ")
        : "",
      frequencies: {
        weekly: Boolean(statsAutomationSettings.frequencies?.weekly),
        month: Boolean(statsAutomationSettings.frequencies?.month),
        quarter: Boolean(statsAutomationSettings.frequencies?.quarter),
        semester: Boolean(statsAutomationSettings.frequencies?.semester),
        year: Boolean(statsAutomationSettings.frequencies?.year),
      },
    });
  }, [statsAutomationSettings]);

  useEffect(() => {
    if (!operationalTourSettings) return;
    setOperationalTourEnvironment(operationalTourSettings.defaultEnvironment);
    setOperationalTourSettingsForm({
      defaultEnvironment: operationalTourSettings.defaultEnvironment,
      targets: {
        production: {
          baseUrl: operationalTourSettings.targets.production.baseUrl || "https://www.portail-association973.com",
          associationEmail: operationalTourSettings.targets.production.associationEmail || "",
          associationPassword: "",
          associationAuthMode: (operationalTourSettings.targets.production.associationAuthMode || "local") as OperationalTourAuthMode,
          accueilEmail: operationalTourSettings.targets.production.accueilEmail || "",
          accueilPassword: "",
          accueilAuthMode: (operationalTourSettings.targets.production.accueilAuthMode || "local") as OperationalTourAuthMode,
          adminEmail: operationalTourSettings.targets.production.adminEmail || "",
          adminPassword: "",
          adminAuthMode: (operationalTourSettings.targets.production.adminAuthMode || "local") as OperationalTourAuthMode,
        },
        secondary: {
          baseUrl: operationalTourSettings.targets.secondary.baseUrl || "",
          associationEmail: operationalTourSettings.targets.secondary.associationEmail || "",
          associationPassword: "",
          associationAuthMode: (operationalTourSettings.targets.secondary.associationAuthMode || "local") as OperationalTourAuthMode,
          accueilEmail: operationalTourSettings.targets.secondary.accueilEmail || "",
          accueilPassword: "",
          accueilAuthMode: (operationalTourSettings.targets.secondary.accueilAuthMode || "local") as OperationalTourAuthMode,
          adminEmail: operationalTourSettings.targets.secondary.adminEmail || "",
          adminPassword: "",
          adminAuthMode: (operationalTourSettings.targets.secondary.adminAuthMode || "local") as OperationalTourAuthMode,
        },
      },
    });
  }, [operationalTourSettings]);

  // Prepare chart data - MUST be before any conditional return to respect React hooks rules
  const monthlyChartData = useMemo(() => {
    if (!requestsPerMonth) return [];
    return (requestsPerMonth as any[]).map((item: any) => ({
      month: item.month,
      total: Number(item.total) || 0,
      validated: Number(item.validated) || 0,
      rejected: Number(item.rejected) || 0,
    }));
  }, [requestsPerMonth]);

  const typeChartData = useMemo(() => {
    if (!requestsByType) return [];
    return (requestsByType as any[]).map((item: any) => ({
      name: requestTypeLabels[item.type] || item.type,
      value: Number(item.total) || 0,
    }));
  }, [requestsByType]);

  const analyticsTimelineData = useMemo(() => {
    return (analyticsData?.timeline || []).map((item) => ({
      label: item.label,
      salles: Number(item.salle) || 0,
      materiel: Number(item.materiel) || 0,
      total: Number(item.total) || 0,
    }));
  }, [analyticsData]);

  const analyticsRoomOccupancyData = useMemo(() => {
    return (analyticsData?.roomOccupancy || []).map((item) => ({
      name: item.roomName,
      taux: Number(item.occupancyRate) || 0,
      jours: Number(item.bookedDays) || 0,
    }));
  }, [analyticsData]);

  const analyticsMaterialUsageData = useMemo(() => {
    return (analyticsData?.materialUsage || []).map((item) => ({
      name: item.label,
      quantite: Number(item.quantity) || 0,
      demandes: Number(item.requests) || 0,
    }));
  }, [analyticsData]);

  const analyticsCategoryData = useMemo(() => {
    return (analyticsData?.categoryUsage || []).map((item) => ({
      name: item.label,
      value: Number(item.count) || 0,
    }));
  }, [analyticsData]);

  const filteredAnalyticsAssociationUsage = useMemo(() => {
    const search = analyticsAssociationSearch.trim().toLowerCase();
    const entries = analyticsData?.associationUsage || [];
    if (!search) return entries;
    return entries.filter((entry) =>
      entry.associationName.toLowerCase().includes(search)
      || entry.thematics.some((label) => label.toLowerCase().includes(search))
    );
  }, [analyticsAssociationSearch, analyticsData]);

  const selectedOperationalTourReport = useMemo(() => {
    if (runOperationalTourMutation.data && (!selectedOperationalTourReportId || runOperationalTourMutation.data.id === selectedOperationalTourReportId)) {
      return runOperationalTourMutation.data;
    }
    if (!selectedOperationalTourReportId) {
      return operationalTourHistory?.[0] || null;
    }
    return operationalTourHistory?.find((entry) => entry.id === selectedOperationalTourReportId) || null;
  }, [operationalTourHistory, runOperationalTourMutation.data, selectedOperationalTourReportId]);

  const operationalTourLatestByEnvironment = useMemo(() => {
    const history = operationalTourHistory || [];
    return {
      production: history.find((entry: any) => entry.environment === "production") || null,
      secondary: history.find((entry: any) => entry.environment === "secondary") || null,
    };
  }, [operationalTourHistory]);

  const selectedOperationalTourSections = useMemo(() => {
    if (!selectedOperationalTourReport) {
      return {
        technical: { total: 0, ok: 0, warning: 0, error: 0, steps: [] as any[] },
        user: { total: 0, ok: 0, warning: 0, error: 0, steps: [] as any[] },
      };
    }
    const buildSection = (kind: "technical" | "user") => {
      const steps = (selectedOperationalTourReport.steps || []).filter((step: any) => (step.kind || "technical") === kind);
      return {
        total: steps.length,
        ok: steps.filter((step: any) => step.status === "ok").length,
        warning: steps.filter((step: any) => step.status === "warning").length,
        error: steps.filter((step: any) => step.status === "error").length,
        steps,
      };
    };
    return {
      technical: buildSection("technical"),
      user: buildSection("user"),
    };
  }, [selectedOperationalTourReport]);

  const exportOperationalTourReport = (report: any) => {
    const blob = new Blob([JSON.stringify(report, null, 2)], { type: "application/json;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const anchor = document.createElement("a");
    anchor.href = url;
    anchor.download = `tour-operationnel-${report.environment}-${report.finishedAt?.slice(0, 19).replace(/[:T]/g, "-") || "rapport"}.json`;
    document.body.appendChild(anchor);
    anchor.click();
    anchor.remove();
    URL.revokeObjectURL(url);
  };

  const getOperationalTourStatusMeta = (status: "ok" | "warning" | "error") => {
    if (status === "ok") {
      return { label: "Opérationnel", className: "bg-green-100 text-green-700 border-green-200" };
    }
    if (status === "warning") {
      return { label: "Partiellement opérationnel", className: "bg-amber-100 text-amber-800 border-amber-200" };
    }
    return { label: "En erreur", className: "bg-red-100 text-red-700 border-red-200" };
  };

  const getOperationalTourKindMeta = (kind: "technical" | "user") => {
    return kind === "technical"
      ? { label: "Tour technique", className: "bg-sky-100 text-sky-800 border-sky-200" }
      : { label: "Tour utilisateur", className: "bg-violet-100 text-violet-800 border-violet-200" };
  };

  const exportAnalyticsCsv = () => {
    if (!analyticsData) return;

    const sections: string[] = [];
    const escapeCell = (value: string | number) => `"${String(value ?? "").replaceAll('"', '""')}"`;

    sections.push("Rapport statistiques CCDS");
    sections.push(`${analyticsData.periodLabel} (${analyticsData.startDate} au ${analyticsData.endDate})`);
    sections.push("");

    sections.push("Indicateurs clés");
    sections.push("Indicateur,Valeur");
    sections.push(`Réservations totales,${analyticsData.summary.totalReservations}`);
    sections.push(`Réservations de salles,${analyticsData.summary.salleReservations}`);
    sections.push(`Réservations de matériel,${analyticsData.summary.materialReservations}`);
    sections.push(`Taux d'occupation des salles,${analyticsData.summary.occupancyRate}%`);
    sections.push(`Associations utilisatrices,${analyticsData.summary.activeAssociations}`);
    sections.push(`Unités matérielles demandées,${analyticsData.summary.totalMaterialUnits}`);
    sections.push(`Fréquence moyenne par association,${analyticsData.summary.averageReservationsPerAssociation}`);
    sections.push("");

    sections.push("Historique des réservations");
    sections.push("Type,Association,Dossier,Début,Fin,Ressources,Statut");
    (analyticsData.history || []).forEach((item) => {
      sections.push([
        requestTypeLabels[item.type] || item.type,
        item.associationName,
        item.title,
        item.startDate,
        item.endDate,
        item.resources,
        statusConfig[item.status]?.label || item.status,
      ].map(escapeCell).join(","));
    });
    sections.push("");

    sections.push("Top associations");
    sections.push("Association,Total,Salles,Matériel,Catégories,Dernière réservation");
    (analyticsData.associationUsage || []).forEach((item) => {
      sections.push([
        item.associationName,
        item.totalReservations,
        item.salleReservations,
        item.materialReservations,
        item.thematics.join(" / "),
        item.lastReservationAt,
      ].map(escapeCell).join(","));
    });
    sections.push("");

    sections.push("Occupation des salles");
    sections.push("Salle,Jours réservés,Taux d'occupation");
    (analyticsData.roomOccupancy || []).forEach((item) => {
      sections.push([
        item.roomName,
        item.bookedDays,
        `${item.occupancyRate}%`,
      ].map(escapeCell).join(","));
    });
    sections.push("");

    sections.push("Matériel le plus demandé");
    sections.push("Matériel,Quantité,Demandes");
    (analyticsData.materialUsage || []).forEach((item) => {
      sections.push([
        item.label,
        item.quantity,
        item.requests,
      ].map(escapeCell).join(","));
    });

    const blob = new Blob([sections.join("\n")], { type: "text/csv;charset=utf-8;" });
    const url = URL.createObjectURL(blob);
    const anchor = document.createElement("a");
    anchor.href = url;
    anchor.download = `statistiques-ccds-${analyticsData.period}-${analyticsData.endDate}.csv`;
    document.body.appendChild(anchor);
    anchor.click();
    document.body.removeChild(anchor);
    URL.revokeObjectURL(url);
  };

  const exportAnalyticsExcel = async () => {
    if (!analyticsData) return;

    try {
      const XLSX = await loadXlsx();
      const workbook = XLSX.utils.book_new();

      const summaryRows = [
        ["Rapport statistiques CCDS", ""],
        ["Période", `${analyticsData.periodLabel} (${analyticsData.startDate} au ${analyticsData.endDate})`],
        ["Comparaison", analyticsData.comparisonLabel],
        [],
        ["Indicateur", "Valeur"],
        ["Réservations totales", analyticsData.summary.totalReservations],
        ["Réservations de salles", analyticsData.summary.salleReservations],
        ["Réservations de matériel", analyticsData.summary.materialReservations],
        ["Taux d'occupation des salles", `${analyticsData.summary.occupancyRate}%`],
        ["Associations utilisatrices", analyticsData.summary.activeAssociations],
        ["Unités matérielles demandées", analyticsData.summary.totalMaterialUnits],
        ["Fréquence moyenne par association", analyticsData.summary.averageReservationsPerAssociation],
      ];

      const timelineRows = [
        ["Période", "Salles", "Matériel", "Total"],
        ...(analyticsData.timeline || []).map((item) => [item.label, item.salle, item.materiel, item.total]),
      ];

      const associationRows = [
        ["Association", "Catégories", "Total", "Salles", "Matériel", "Dernière réservation"],
        ...(analyticsData.associationUsage || []).map((item) => [
          item.associationName,
          item.thematics.join(" / "),
          item.totalReservations,
          item.salleReservations,
          item.materialReservations,
          item.lastReservationAt,
        ]),
      ];

      const roomRows = [
        ["Salle", "Jours réservés", "Taux d'occupation"],
        ...(analyticsData.roomOccupancy || []).map((item) => [
          item.roomName,
          item.bookedDays,
          `${item.occupancyRate}%`,
        ]),
      ];

      const materialRows = [
        ["Matériel", "Quantité", "Dossiers"],
        ...(analyticsData.materialUsage || []).map((item) => [
          item.label,
          item.quantity,
          item.requests,
        ]),
      ];

      const historyRows = [
        ["Type", "Association", "Dossier", "Début", "Fin", "Ressources", "Statut"],
        ...(analyticsData.history || []).map((item) => [
          requestTypeLabels[item.type] || item.type,
          item.associationName,
          item.title,
          item.startDate,
          item.endDate,
          item.resources,
          statusConfig[item.status]?.label || item.status,
        ]),
      ];

      XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(summaryRows), "Synthèse");
      XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(timelineRows), "Évolution");
      XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(associationRows), "Associations");
      XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(roomRows), "Salles");
      XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(materialRows), "Matériel");
      XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(historyRows), "Historique");

      XLSX.writeFile(workbook, `statistiques-ccds-${analyticsData.period}-${analyticsData.endDate}.xlsx`);
    } catch (error) {
      console.error(error);
      toast.error("Impossible de générer le fichier Excel");
    }
  };

  const openAnalyticsPrintableReport = () => {
    if (!analyticsData) return;

    const escapeHtml = (value: string | number) =>
      String(value ?? "")
        .replaceAll("&", "&amp;")
        .replaceAll("<", "&lt;")
        .replaceAll(">", "&gt;")
        .replaceAll('"', "&quot;");

    const renderTableRows = (rows: string[][]) =>
      rows.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`).join("");

    const summaryCards = [
      ["Réservations totales", String(analyticsData.summary.totalReservations)],
      ["Réservations de salles", String(analyticsData.summary.salleReservations)],
      ["Réservations de matériel", String(analyticsData.summary.materialReservations)],
      ["Taux d’occupation", `${analyticsData.summary.occupancyRate}%`],
      ["Associations utilisatrices", String(analyticsData.summary.activeAssociations)],
      ["Unités matérielles", String(analyticsData.summary.totalMaterialUnits)],
    ];

    const historyRows = (analyticsData.history || []).map((item) => [
      requestTypeLabels[item.type] || item.type,
      item.associationName,
      item.title,
      item.startDate === item.endDate ? formatDate(item.startDate) : `${formatDate(item.startDate)} au ${formatDate(item.endDate)}`,
      item.resources || "-",
      statusConfig[item.status]?.label || item.status,
    ]);

    const associationRows = (analyticsData.associationUsage || []).map((item) => [
      item.associationName,
      item.thematics.join(" / "),
      String(item.totalReservations),
      String(item.salleReservations),
      String(item.materialReservations),
      formatDate(item.lastReservationAt),
    ]);

    const roomRows = (analyticsData.roomOccupancy || []).map((item) => [
      item.roomName,
      String(item.bookedDays),
      `${item.occupancyRate}%`,
    ]);

    const materialRows = (analyticsData.materialUsage || []).map((item) => [
      item.label,
      String(item.quantity),
      String(item.requests),
    ]);

    const html = `<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="utf-8" />
  <title>Rapport statistiques CCDS</title>
  <style>
    :root { color-scheme: light; }
    body { font-family: Arial, sans-serif; margin: 0; color: #0f172a; background: #f8fafc; }
    .toolbar { position: sticky; top: 0; z-index: 20; display: flex; gap: 12px; justify-content: space-between; align-items: center; padding: 16px 24px; background: #ffffff; border-bottom: 1px solid #e2e8f0; }
    .toolbar button { border: 0; border-radius: 8px; padding: 10px 14px; font-size: 14px; cursor: pointer; }
    .toolbar .primary { background: #000091; color: white; }
    .toolbar .secondary { background: #eef2ff; color: #000091; }
    main { max-width: 1120px; margin: 0 auto; padding: 24px; }
    h1, h2 { margin: 0; }
    h1 { font-size: 28px; color: #000091; }
    h2 { font-size: 18px; margin-bottom: 12px; }
    .meta { margin-top: 8px; color: #475569; font-size: 14px; }
    .grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin: 24px 0; }
    .card { background: white; border: 1px solid #e2e8f0; border-radius: 10px; padding: 16px; }
    .card .label { color: #64748b; font-size: 13px; }
    .card .value { margin-top: 8px; font-size: 28px; font-weight: 700; }
    section { margin-top: 28px; }
    table { width: 100%; border-collapse: collapse; background: white; border: 1px solid #e2e8f0; }
    th, td { border-bottom: 1px solid #e2e8f0; padding: 10px 12px; text-align: left; vertical-align: top; font-size: 14px; }
    th { background: #f8fafc; color: #475569; font-weight: 600; }
    .small { font-size: 12px; color: #64748b; }
    @media print {
      body { background: white; }
      .toolbar { display: none; }
      main { padding: 0; max-width: none; }
      .grid { page-break-inside: avoid; }
      section, .card, table { page-break-inside: avoid; }
    }
  </style>
</head>
<body>
  <div class="toolbar">
    <div>
      <strong>Rapport statistiques CCDS</strong>
      <div class="small">${escapeHtml(analyticsData.periodLabel)} · ${escapeHtml(analyticsData.startDate)} au ${escapeHtml(analyticsData.endDate)}</div>
    </div>
    <div>
      <button class="secondary" onclick="window.close()">Fermer</button>
      <button class="primary" onclick="window.print()">Imprimer / Enregistrer en PDF</button>
    </div>
  </div>
  <main>
    <h1>Rapport statistiques CCDS</h1>
    <div class="meta">${escapeHtml(analyticsData.periodLabel)} · ${escapeHtml(analyticsData.startDate)} au ${escapeHtml(analyticsData.endDate)} · comparaison : ${escapeHtml(analyticsData.comparisonLabel)}</div>

    <div class="grid">
      ${summaryCards.map(([label, value]) => `<div class="card"><div class="label">${escapeHtml(label)}</div><div class="value">${escapeHtml(value)}</div></div>`).join("")}
    </div>

    <section>
      <h2>Top associations et structures utilisatrices</h2>
      <table>
        <thead><tr><th>Association</th><th>Catégorie</th><th>Total</th><th>Salles</th><th>Matériel</th><th>Dernière réservation</th></tr></thead>
        <tbody>${renderTableRows(associationRows)}</tbody>
      </table>
    </section>

    <section>
      <h2>Occupation des salles</h2>
      <table>
        <thead><tr><th>Salle</th><th>Jours réservés</th><th>Taux d'occupation</th></tr></thead>
        <tbody>${renderTableRows(roomRows)}</tbody>
      </table>
    </section>

    <section>
      <h2>Matériels les plus demandés</h2>
      <table>
        <thead><tr><th>Matériel</th><th>Quantité</th><th>Dossiers</th></tr></thead>
        <tbody>${renderTableRows(materialRows)}</tbody>
      </table>
    </section>

    <section>
      <h2>Historique des réservations</h2>
      <table>
        <thead><tr><th>Type</th><th>Association</th><th>Dossier</th><th>Période</th><th>Ressources</th><th>Statut</th></tr></thead>
        <tbody>${renderTableRows(historyRows)}</tbody>
      </table>
    </section>
  </main>
</body>
</html>`;

    const reportWindow = window.open("", "_blank", "noopener,noreferrer");
    if (!reportWindow) {
      toast.error("Le navigateur a bloqué l’ouverture du rapport imprimable");
      return;
    }
    reportWindow.document.open();
    reportWindow.document.write(html);
    reportWindow.document.close();
  };

  const openArchivedPrintableReport = (url: string, shouldPrint = false) => {
    const reportWindow = window.open(url, "_blank");

    if (!reportWindow) {
      toast.error("Impossible d’ouvrir le rapport");
      return;
    }

    if (!shouldPrint) return;

    const triggerPrint = () => {
      try {
        reportWindow.focus();
        reportWindow.print();
      } catch (error) {
        console.error(error);
      }
    };

    setTimeout(triggerPrint, 1200);
    reportWindow.addEventListener?.("load", () => {
      setTimeout(triggerPrint, 150);
    });
  };

  const buildArchivedReportViewHref = (entry: {
    pdfUrl: string;
    pdfName: string;
    periodLabel: string;
    generatedAt: string;
  }) =>
    `/admin/stats-report?url=${encodeURIComponent(entry.pdfUrl)}&name=${encodeURIComponent(
      entry.pdfName || "rapport-statistiques.pdf"
    )}&period=${encodeURIComponent(entry.periodLabel)}&generatedAt=${encodeURIComponent(entry.generatedAt)}`;

  const saveStatsAutomation = () => {
    const recipientEmails = statsAutomationForm.recipientEmails
      .split(",")
      .map((entry) => entry.trim().toLowerCase())
      .filter(Boolean);

    updateStatsAutomationMutation.mutate({
      enabled: statsAutomationForm.enabled,
      recipientEmails,
      frequencies: statsAutomationForm.frequencies,
    });
  };

  const salleBillingEntries = useMemo(() => {
    const today = new Date().toISOString().slice(0, 10);

    return (billingRequestsData?.data || [])
      .map((request) => {
        const formData = parseRequestFormData(request.formData);
        const workflow = formData?.salleWorkflow && typeof formData.salleWorkflow === "object"
          ? formData.salleWorkflow
          : {};
        const financialDecision = formData?.cadreDSU?.materielEvent?.financialDecision;
        if (financialDecision?.financialMode !== "location_payante") return null;

        const paymentStatus = (
          typeof workflow.paymentStatus === "string"
          && Object.prototype.hasOwnProperty.call(SALLE_WORKFLOW_PAYMENT_STATUS_LABELS, workflow.paymentStatus)
            ? workflow.paymentStatus
            : "en_attente_paiement"
        ) as SalleWorkflowPaymentStatus;
        const paymentDueDate = typeof workflow.paymentDueDate === "string" && workflow.paymentDueDate
          ? workflow.paymentDueDate
          : typeof formData?.dateReservation === "string"
            ? formData.dateReservation
            : "";
        const amountDueCents = Number.isFinite(Number(workflow?.pricing?.totalAmountCents))
          ? Number(workflow.pricing.totalAmountCents)
          : Number.isFinite(Number(financialDecision?.rentalAmountCents))
            ? Number(financialDecision.rentalAmountCents)
            : 0;
        const amountReceivedCents = Number.isFinite(Number(workflow.amountReceivedCents))
          ? Number(workflow.amountReceivedCents)
          : 0;
        const isLate = Boolean(
          paymentDueDate
          && paymentDueDate < today
          && paymentStatus !== "paiement_recu"
          && paymentStatus !== "paiement_valide"
        );
        const bucket = isLate
          ? "en_retard"
          : paymentStatus === "paiement_valide"
            ? "validees"
            : paymentStatus === "paiement_recu"
              ? "recues"
              : "en_attente";

        return {
          id: request.id,
          titre: request.titre,
          createdAt: request.createdAt,
          status: request.status,
          associationName: String(formData?.nomAssociation || `Association #${request.associationId}`),
          salles: Array.isArray(formData?.sallesSelectionnees) ? formData.sallesSelectionnees.filter(Boolean) : [],
          dateReservation: typeof formData?.dateReservation === "string" ? formData.dateReservation : "",
          paymentStatus,
          paymentDueDate,
          amountDueCents,
          amountReceivedCents,
          paymentReceivedAt: typeof workflow.paymentReceivedAt === "string" ? workflow.paymentReceivedAt : "",
          bucket,
          isLate,
        };
      })
      .filter((entry): entry is NonNullable<typeof entry> => Boolean(entry))
      .sort((left, right) => {
        const leftDue = left.paymentDueDate ? new Date(left.paymentDueDate).getTime() : 0;
        const rightDue = right.paymentDueDate ? new Date(right.paymentDueDate).getTime() : 0;
        return leftDue - rightDue;
      });
  }, [billingRequestsData]);

  const salleBillingCounts = useMemo(() => {
    return {
      all: salleBillingEntries.length,
      en_attente: salleBillingEntries.filter((entry) => entry.bucket === "en_attente").length,
      en_retard: salleBillingEntries.filter((entry) => entry.bucket === "en_retard").length,
      recues: salleBillingEntries.filter((entry) => entry.bucket === "recues").length,
      validees: salleBillingEntries.filter((entry) => entry.bucket === "validees").length,
    };
  }, [salleBillingEntries]);

  const filteredSalleBillingEntries = useMemo(() => {
    if (billingFilter === "all") return salleBillingEntries;
    return salleBillingEntries.filter((entry) => entry.bucket === billingFilter);
  }, [billingFilter, salleBillingEntries]);

  const materialReturnBoardWithOverrides = useMemo(() => {
    const entries = materialReturnBoard || [];
    return entries.map((entry) => {
      const override = materialReturnLinkOverrides[entry.requestId];
      return override ? { ...entry, ...override } : entry;
    });
  }, [materialReturnBoard, materialReturnLinkOverrides]);

  const logisticsBoardColumns = useMemo(() => {
    const entries = materialReturnBoardWithOverrides;
    return {
      a_attribuer: entries.filter((entry) => entry.boardState === "a_attribuer"),
      planifie: entries.filter((entry) => entry.boardState === "planifie"),
      terrain: entries.filter((entry) => entry.boardState === "terrain"),
      retard: entries.filter((entry) => entry.boardState === "retard"),
      conforme: entries.filter((entry) => entry.boardState === "conforme"),
      litige: entries.filter((entry) => entry.boardState === "litige"),
    };
  }, [materialReturnBoardWithOverrides]);
  const visibleLogisticsBoardStates = useMemo(() => {
    if (selectedLogisticsBoardState === "all") {
      return ["a_attribuer", "planifie", "terrain", "retard", "litige", "conforme"] as const;
    }

    return [selectedLogisticsBoardState] as const;
  }, [selectedLogisticsBoardState]);

  const logisticsOverview = useMemo(() => {
    const entries = materialReturnBoardWithOverrides;
    const today = new Date().toISOString().split("T")[0];
    const sameDay = (value?: string | null) => {
      if (!value) return false;
      return String(value).slice(0, 10) === today;
    };
    const latestRestitutionEntry = [...entries]
      .filter((entry) => entry.restitutionDate)
      .sort((a, b) => new Date(String(b.restitutionDate)).getTime() - new Date(String(a.restitutionDate)).getTime())[0];

    return {
      delayedCount: entries.filter((entry) => entry.boardState === "retard").length,
      issueCount: entries.filter((entry) => entry.boardState === "litige").length,
      unassignedCount: entries.filter((entry) => entry.boardState === "a_attribuer").length,
      pickupsToday: entries.filter((entry: any) => sameDay(entry.pickupDate)).length,
      returnsToday: entries.filter((entry) => sameDay(entry.restitutionDate)).length,
      latestRestitutionEntry,
      pendingValidationCount: pendingRequests?.length || 0,
      overdueProcessingCount: overdueRequests?.length || 0,
    };
  }, [materialReturnBoardWithOverrides, overdueRequests, pendingRequests]);

  const salleWorkflowEntries = useMemo(() => {
    const today = new Date().toISOString().slice(0, 10);
    return (billingRequestsData?.data || [])
      .map((request) => {
        const formData = parseRequestFormData(request.formData);
        const workflow = formData?.salleWorkflow && typeof formData.salleWorkflow === "object"
          ? formData.salleWorkflow
          : {};
        const createdAt = String(request.createdAt || "");
        const reservationStart = typeof formData?.dateReservation === "string" ? formData.dateReservation : "";
        const reservationEnd = typeof formData?.dateFinReservation === "string" ? formData.dateFinReservation : reservationStart;
        const directorStatus = typeof workflow.directorStatus === "string" ? workflow.directorStatus : "a_transmettre";
        const quoteStatus = typeof workflow.quoteStatus === "string" ? workflow.quoteStatus : "a_preparer";
        const finalNotificationSentAt = typeof workflow.finalNotificationSentAt === "string" ? workflow.finalNotificationSentAt : "";
        const quoteRespondedAt = typeof workflow.quoteRespondedAt === "string" ? workflow.quoteRespondedAt : "";
        const quoteSentAt = typeof workflow.quoteSentAt === "string" ? workflow.quoteSentAt : "";
        const todayInRange = Boolean(
          reservationStart
          && reservationStart <= today
          && (!reservationEnd || reservationEnd >= today)
        );

        return {
          id: request.id,
          titre: request.titre,
          associationName: String(formData?.nomAssociation || `Association #${request.associationId}`),
          createdAt,
          reservationStart,
          reservationEnd,
          quoteStatus,
          quoteSentAt,
          quoteRespondedAt,
          directorStatus,
          directorTransmissionAt: typeof workflow.directorTransmissionAt === "string" ? workflow.directorTransmissionAt : "",
          finalNotificationSentAt,
          todayInRange,
        };
      })
      .sort((left, right) => String(right.createdAt).localeCompare(String(left.createdAt)));
  }, [billingRequestsData]);

  const accueilWorkflowCounts = useMemo(() => {
    const today = new Date().toISOString().slice(0, 10);
    return {
      toHandleToday: salleWorkflowEntries.filter(
        (entry) =>
          String(entry.createdAt).slice(0, 10) === today
          && (entry.quoteStatus === "a_preparer" || entry.quoteStatus === "en_attente_association" || entry.directorStatus === "a_transmettre")
      ).length,
      waitingForAssociation: salleWorkflowEntries.filter((entry) => entry.quoteStatus === "en_attente_association").length,
      backFromAssociation: salleWorkflowEntries.filter(
        (entry) => entry.quoteStatus === "accepte" && entry.directorStatus === "a_transmettre"
      ).length,
      signedReadyToSend: salleWorkflowEntries.filter(
        (entry) => entry.directorStatus === "signee" && !entry.finalNotificationSentAt
      ).length,
      reservedToday: salleWorkflowEntries.filter((entry) => entry.todayInRange).length,
    };
  }, [salleWorkflowEntries]);

  const roleHeroBadges = useMemo(() => {
    if (isAccueil) {
      return [
        { label: "À traiter aujourd’hui", value: String(accueilWorkflowCounts.toHandleToday) },
        { label: "Réponses association", value: String(accueilWorkflowCounts.waitingForAssociation) },
        { label: "Prêtes à envoyer", value: String(accueilWorkflowCounts.signedReadyToSend) },
      ];
    }

    if (isLogisticsControl) {
      return [
        { label: "Demandes matériel", value: String(logisticsOverview.pendingValidationCount) },
        { label: "Retards", value: String(logisticsOverview.delayedCount) },
        { label: "Contrôles terrain", value: String(logisticsOverview.returnsToday) },
      ];
    }

    return [
      { label: "Demandes en attente", value: String(stats?.pendingRequests || 0) },
      { label: "Locations payantes", value: String(salleBillingCounts.all) },
      { label: "Notifications non lues", value: String(unreadCount || 0) },
    ];
  }, [accueilWorkflowCounts.signedReadyToSend, accueilWorkflowCounts.toHandleToday, accueilWorkflowCounts.waitingForAssociation, isAccueil, isLogisticsControl, logisticsOverview.delayedCount, logisticsOverview.pendingValidationCount, logisticsOverview.returnsToday, salleBillingCounts.all, stats?.pendingRequests, unreadCount]);

  const roleHeroMonitoringAlert = useMemo(() => {
    if (user?.role !== "admin" && user?.role !== "super_admin") {
      return null;
    }

    const production = operationalTourLatestByEnvironment.production;
    const secondary = operationalTourLatestByEnvironment.secondary;
    const trouble = [production, secondary].filter((entry: any) => entry && entry.status !== "ok");

    if (trouble.length === 0) {
      return null;
    }

    const hasError = trouble.some((entry: any) => entry.status === "error");
    const labels = trouble.map((entry: any) => entry.environment === "production" ? "Production" : "Préprod");

    return {
      tone: hasError
        ? "border-red-200 bg-red-50 text-red-900"
        : "border-amber-200 bg-amber-50 text-amber-900",
      icon: hasError ? AlertTriangle : Shield,
      title: hasError
        ? "Un environnement demande une attention immédiate"
        : "Un contrôle mérite un coup d'œil",
      description: `${labels.join(" et ")} ${labels.length > 1 ? "présentent" : "présente"} un dernier contrôle ${hasError ? "en erreur" : "avec avertissement"}.`,
    };
  }, [operationalTourLatestByEnvironment.production, operationalTourLatestByEnvironment.secondary, user?.role]);

  const roleQuickActions = useMemo(() => {
    if (isAccueil) {
      return [
        { label: "Créer une réservation", icon: Calendar, onClick: () => setLocation("/dashboard/reservation-salle"), variant: "default" as const },
        {
          label: "Traiter une demande",
          icon: ClipboardList,
          onClick: () => {
            setRequestStatusFilter("soumise");
            setRequestTypeFilter("demande_salle");
            openAdminTab("requests");
          },
        },
        {
          label: "Transmettre à la Direction",
          icon: Send,
          onClick: () => {
            setRequestTypeFilter("demande_salle");
            setRequestStatusFilter("en_cours_traitement");
            openAdminTab("requests");
          },
        },
        {
          label: "Envoyer confirmation finale",
          icon: CheckCircle,
          onClick: () => {
            setRequestTypeFilter("demande_salle");
            openAdminTab("requests");
          },
        },
      ];
    }

    if (isLogisticsControl) {
      return [
        {
          label: "Traiter une demande matériel",
          icon: ClipboardList,
          onClick: () => {
            setRequestTypeFilter("demande_materiel_evenementiel");
            setRequestStatusFilter("soumise");
            openAdminTab("requests");
          },
          variant: "default" as const,
        },
        { label: "Assigner le service terrain", icon: PackageCheck, onClick: () => openAdminTab("logistique") },
        { label: "Suivre une restitution", icon: RefreshCw, onClick: () => openAdminTab("logistique") },
        { label: "Contrôler un retour", icon: Shield, onClick: () => openAdminTab("logistique") },
      ];
    }

    return [
      { label: "Voir les statistiques", icon: TrendingUp, onClick: () => openAdminTab("analytics"), variant: "default" as const },
      { label: "Valider une demande", icon: ClipboardList, onClick: () => openAdminTab("requests") },
      { label: "Ouvrir l’annuaire CCDS", icon: Users, onClick: () => openAdminTab("ccds-directory") },
      {
        label: canManageLogistics ? "Consulter la logistique" : "Voir le calendrier",
        icon: canManageLogistics ? PackageCheck : Calendar,
        onClick: () => openAdminTab(canManageLogistics ? "logistique" : "calendrier"),
      },
      { label: "Consulter les notifications", icon: Bell, onClick: () => openAdminTab("notifications") },
    ];
  }, [canManageLogistics, isAccueil, isLogisticsControl, location, setLocation]);

  const roleKpiItems = useMemo(() => {
    if (isAccueil) {
      return [
        { label: "Devis en attente", value: accueilWorkflowCounts.waitingForAssociation, hint: "Associations qui doivent encore répondre.", icon: Mail },
        { label: "Revenus après accord", value: accueilWorkflowCounts.backFromAssociation, hint: "Dossiers revenus vers l’accueil pour transmission Direction.", icon: RotateCcw },
        { label: "Signés prêts à envoyer", value: accueilWorkflowCounts.signedReadyToSend, hint: "Dossiers signés qui attendent la confirmation finale.", icon: CheckCircle },
        { label: "Salles occupées aujourd’hui", value: accueilWorkflowCounts.reservedToday, hint: "Réservations de salle couvrant la journée en cours.", icon: CalendarClock },
      ];
    }

    if (isLogisticsControl) {
      return [
        { label: "À attribuer", value: logisticsOverview.unassignedCount, hint: "Demandes validées sans service terrain affecté.", icon: Users },
        { label: "Retours du jour", value: logisticsOverview.returnsToday, hint: "Restitutions attendues ou à contrôler aujourd’hui.", icon: RefreshCw },
        { label: "Retards", value: logisticsOverview.delayedCount, hint: "Dossiers matériel non revenus dans les délais.", icon: AlertTriangle, tone: "border-red-200 bg-red-50/70" },
        { label: "Litiges", value: logisticsOverview.issueCount, hint: "Anomalies ou dégradations en attente d’arbitrage.", icon: Shield, tone: "border-orange-200 bg-orange-50/70" },
      ];
    }

    return [
      { label: "Associations", value: stats?.totalAssociations || 0, hint: "Structures actuellement visibles dans le portail.", icon: Building2 },
      { label: "Demandes totales", value: stats?.totalRequests || 0, hint: "Toutes les demandes enregistrées sur le portail.", icon: ClipboardList },
      { label: "En attente", value: stats?.pendingRequests || 0, hint: "Demandes qui attendent encore une action interne.", icon: Clock },
      { label: "Taux d’acceptation", value: `${stats?.acceptanceRate || 0}%`, hint: `${stats?.validatedRequests || 0} demandes validées à ce jour.`, icon: TrendingUp },
    ];
  }, [accueilWorkflowCounts.backFromAssociation, accueilWorkflowCounts.reservedToday, accueilWorkflowCounts.signedReadyToSend, accueilWorkflowCounts.waitingForAssociation, isAccueil, isLogisticsControl, logisticsOverview.delayedCount, logisticsOverview.issueCount, logisticsOverview.returnsToday, logisticsOverview.unassignedCount, stats?.acceptanceRate, stats?.pendingRequests, stats?.totalAssociations, stats?.totalRequests, stats?.validatedRequests]);

  const filteredNotifications = useMemo(() => {
    const entries = notifications || [];
    if (isAccueil) {
      return entries.filter((notif) => {
        const haystack = `${notif.type} ${notif.titre} ${notif.message} ${notif.lien || ""}`.toLowerCase();
        return haystack.includes("salle")
          || haystack.includes("devis")
          || haystack.includes("association")
          || haystack.includes("signature")
          || haystack.includes("accueil");
      });
    }
    if (isLogisticsControl) {
      return entries.filter((notif) => {
        const haystack = `${notif.type} ${notif.titre} ${notif.message} ${notif.lien || ""}`.toLowerCase();
        return haystack.includes("matériel")
          || haystack.includes("materiel")
          || haystack.includes("restitution")
          || haystack.includes("terrain")
          || haystack.includes("litige")
          || haystack.includes("retour");
      });
    }
    return entries;
  }, [isAccueil, isLogisticsControl, notifications]);

  const dashboardUrgentAlerts = useMemo(() => {
    return [
      {
        key: "retard",
        label: "Restitutions en retard",
        count: logisticsOverview.delayedCount,
        tone: "border-red-200 bg-red-50 text-red-900",
        accent: "text-red-700",
        actionLabel: "Relancer les agents",
        onClick: () => openAdminTab("logistique"),
      },
      {
        key: "litige",
        label: "Litiges / dégradations",
        count: logisticsOverview.issueCount,
        tone: "border-orange-200 bg-orange-50 text-orange-900",
        accent: "text-orange-700",
        actionLabel: "Ouvrir les dossiers",
        onClick: () => openAdminTab("logistique"),
      },
      {
        key: "attente",
        label: "Demandes en attente",
        count: logisticsOverview.pendingValidationCount,
        tone: "border-blue-200 bg-blue-50 text-blue-900",
        accent: "text-blue-700",
        actionLabel: "Lancer l'instruction",
        onClick: () => {
          setRequestStatusFilter("soumise");
          openAdminTab("requests");
        },
      },
    ];
  }, [location, logisticsOverview, setLocation]);

  const dashboardOperationalAlerts = useMemo(() => {
    if (!isAccueil && canManageLogistics) {
      return dashboardUrgentAlerts;
    }

    if (isAccueil) {
      return [
        {
          key: "devis",
          label: "Réponses devis attendues",
          count: accueilWorkflowCounts.waitingForAssociation,
          tone: "border-violet-200 bg-violet-50 text-violet-900",
          accent: "text-violet-700",
          actionLabel: "Suivre les devis transmis",
          onClick: () => {
            setRequestTypeFilter("demande_salle");
            openAdminTab("requests");
          },
        },
        {
          key: "retour",
          label: "Dossiers revenus à l’accueil",
          count: accueilWorkflowCounts.backFromAssociation,
          tone: "border-blue-200 bg-blue-50 text-blue-900",
          accent: "text-blue-700",
          actionLabel: "Transmettre à la Direction",
          onClick: () => {
            setRequestTypeFilter("demande_salle");
            openAdminTab("requests");
          },
        },
        {
          key: "final",
          label: "Confirmations finales prêtes",
          count: accueilWorkflowCounts.signedReadyToSend,
          tone: "border-emerald-200 bg-emerald-50 text-emerald-900",
          accent: "text-emerald-700",
          actionLabel: "Envoyer les confirmations finales",
          onClick: () => {
            setRequestTypeFilter("demande_salle");
            openAdminTab("requests");
          },
        },
      ];
    }

    return [
      {
        key: "attente",
        label: "Demandes à instruire",
        count: logisticsOverview.pendingValidationCount,
        tone: "border-blue-200 bg-blue-50 text-blue-900",
        accent: "text-blue-700",
        actionLabel: "Ouvrir les demandes en attente",
        onClick: () => {
          setRequestStatusFilter("soumise");
          if (isAccueil) {
            setRequestTypeFilter("demande_salle");
          }
          openAdminTab("requests");
        },
      },
      {
        key: "suivi",
        label: "Dossiers à suivre",
        count: logisticsOverview.overdueProcessingCount,
        tone: "border-amber-200 bg-amber-50 text-amber-900",
        accent: "text-amber-700",
        actionLabel: "Reprendre les dossiers en cours",
        onClick: () => {
          setRequestStatusFilter("en_cours_traitement");
          if (isAccueil) {
            setRequestTypeFilter("demande_salle");
          }
          openAdminTab("requests");
        },
      },
      {
        key: "notifications",
        label: "Notifications non lues",
        count: unreadCount || 0,
        tone: "border-slate-200 bg-slate-50 text-slate-900",
        accent: "text-slate-700",
        actionLabel: "Consulter les notifications",
        onClick: () => openAdminTab("notifications"),
      },
    ];
  }, [accueilWorkflowCounts.backFromAssociation, accueilWorkflowCounts.signedReadyToSend, accueilWorkflowCounts.waitingForAssociation, canManageLogistics, dashboardUrgentAlerts, isAccueil, location, logisticsOverview.overdueProcessingCount, logisticsOverview.pendingValidationCount, setLocation, unreadCount]);

  const dailyOperations = useMemo(() => {
    const entries = materialReturnBoardWithOverrides;
    const today = new Date().toISOString().slice(0, 10);
    const byDay = (value?: string | null) => String(value || "").slice(0, 10) === today;
    const sortByClock = (left?: string | null, right?: string | null) =>
      String(left || "").localeCompare(String(right || ""));

    const departures = entries
      .filter((entry) => byDay(entry.pickupDate))
      .sort((a, b) => sortByClock(a.pickupDate, b.pickupDate));

    const returns = entries
      .filter((entry) => byDay(entry.restitutionDate))
      .sort((a, b) => sortByClock(a.restitutionDate, b.restitutionDate));

    return { departures, returns };
  }, [materialReturnBoardWithOverrides]);

  const miniCalendarTensionDays = useMemo(() => {
    const currentMonth = new Date();
    const year = currentMonth.getFullYear();
    const month = currentMonth.getMonth();
    const firstDay = new Date(year, month, 1);
    const lastDay = new Date(year, month + 1, 0);
    const startWeekday = (firstDay.getDay() + 6) % 7;
    const daysInMonth = lastDay.getDate();
    const today = new Date().toISOString().slice(0, 10);
    const events = materialCalendar?.events || [];
    const conflicts = materialCalendar?.conflicts || [];

    const conflictRequestIds = new Set(conflicts.map((conflict) => conflict.requestId));
    const busyDays = new Map<string, { tension: boolean; litige: boolean; load: number }>();

    events.forEach((event) => {
      const start = new Date(event.useStartDate);
      const end = new Date(event.useEndDate);
      for (
        let cursor = new Date(start.getFullYear(), start.getMonth(), start.getDate());
        cursor <= end;
        cursor.setDate(cursor.getDate() + 1)
      ) {
        const iso = cursor.toISOString().slice(0, 10);
        const previous = busyDays.get(iso) || { tension: false, litige: false, load: 0 };
        busyDays.set(iso, {
          tension: previous.tension || conflictRequestIds.has(event.requestId),
          litige: previous.litige || event.boardState === "litige",
          load: previous.load + 1,
        });
      }
    });

    const cells: MiniCalendarCell[] = Array.from({ length: startWeekday }, (_, index) => ({
      key: `pad-${index}`,
      day: null,
      iso: null,
      isToday: false,
      tension: false,
      litige: false,
      load: 0,
    }));

    for (let day = 1; day <= daysInMonth; day += 1) {
      const date = new Date(year, month, day);
      const iso = date.toISOString().slice(0, 10);
      const dayMeta = busyDays.get(iso);
      cells.push({
        key: iso,
        day,
        iso,
        isToday: iso === today,
        tension: Boolean(dayMeta?.tension),
        litige: Boolean(dayMeta?.litige),
        load: dayMeta?.load || 0,
      });
    }

    while (cells.length % 7 !== 0) {
      cells.push({
        key: `tail-${cells.length}`,
        day: null,
        iso: null,
        isToday: false,
        tension: false,
        litige: false,
        load: 0,
      });
    }

    return cells;
  }, [materialCalendar]);

  const usageByCommuneData = useMemo(() => {
    const events = materialCalendar?.events || [];
    if (!events.length) return [];

    const monthStart = new Date();
    monthStart.setDate(1);
    monthStart.setHours(0, 0, 0, 0);
    const monthEnd = new Date(monthStart.getFullYear(), monthStart.getMonth() + 1, 0);
    monthEnd.setHours(23, 59, 59, 999);

    const totals = new Map<string, { commune: string; dossiers: number; quantite: number }>();
    events.forEach((event) => {
      const start = new Date(event.useStartDate);
      const end = new Date(event.useEndDate);
      if (end < monthStart || start > monthEnd) return;
      const commune = event.commune || "Non renseignée";
      const previous = totals.get(commune) || { commune, dossiers: 0, quantite: 0 };
      totals.set(commune, {
        commune,
        dossiers: previous.dossiers + 1,
        quantite: previous.quantite + event.requestedItems.reduce((sum, item) => sum + (item.quantity || 0), 0),
      });
    });

    return Array.from(totals.values()).sort((a, b) => b.quantite - a.quantite);
  }, [materialCalendar]);

  const adminLogisticsMapEvents = useMemo(() => {
    const events = materialCalendar?.events || [];
    const filteredEvents = events.filter((event) => (
      selectedLogisticsBoardState === "all" ? true : event.boardState === selectedLogisticsBoardState
    ));

    return filteredEvents
      .flatMap((event) => {
        if (typeof event.requestId !== "number") {
          return [];
        }

        return [{
          requestId: event.requestId,
          title: event.title,
          associationName: event.associationName,
          commune: event.commune,
          manifestationStart: event.manifestationStart,
          manifestationEnd: event.manifestationEnd,
          pickupDate: event.pickupDate,
          restitutionDate: event.restitutionDate,
          useStartDate: event.useStartDate,
          useEndDate: event.useEndDate,
          requestedItems: event.requestedItems,
          boardState: event.boardState,
          boardStateLabel: event.boardStateLabel,
          logisticServiceLabel: event.logisticServiceLabel,
          issueFlag: event.issueFlag,
          finalPdfUrl: event.finalPdfUrl,
          uploadLink: event.uploadLink,
        }];
      })
      .sort((left, right) => String(left.useStartDate || "").localeCompare(String(right.useStartDate || "")));
  }, [materialCalendar, selectedLogisticsBoardState]);

  const selectedAdminLogisticsMapEvent = useMemo(
    () => adminLogisticsMapEvents.find((event) => event.requestId === selectedLogisticsMapRequestId) || null,
    [adminLogisticsMapEvents, selectedLogisticsMapRequestId]
  );

  const selectedAdminLogisticsDirections = useMemo(() => {
    if (!selectedAdminLogisticsMapEvent) {
      return null;
    }

    const associationNameKey = selectedAdminLogisticsMapEvent.associationName.trim().toLowerCase();
    const communeKey = selectedAdminLogisticsMapEvent.commune?.trim().toLowerCase();
    const matchedAssociation = (logisticsMapAssociations?.data || []).find((entry) => {
      if (entry.latitude === null || entry.longitude === null) return false;
      if (entry.nomAssociation.trim().toLowerCase() !== associationNameKey) return false;
      if (!communeKey || !entry.ville) return true;
      return entry.ville.trim().toLowerCase() === communeKey;
    }) || (logisticsMapAssociations?.data || []).find((entry) => {
      if (entry.latitude === null || entry.longitude === null) return false;
      return entry.nomAssociation.trim().toLowerCase() === associationNameKey;
    });

    return {
      latitude: matchedAssociation?.latitude,
      longitude: matchedAssociation?.longitude,
      destinationLabel: [selectedAdminLogisticsMapEvent.associationName, selectedAdminLogisticsMapEvent.commune || "Guyane"].filter(Boolean).join(", "),
    };
  }, [logisticsMapAssociations?.data, selectedAdminLogisticsMapEvent]);

  useEffect(() => {
    if (adminLogisticsMapEvents.length === 0) {
      if (selectedLogisticsMapRequestId !== null) {
        setSelectedLogisticsMapRequestId(null);
      }
      return;
    }

    const stillVisible = adminLogisticsMapEvents.some((event) => event.requestId === selectedLogisticsMapRequestId);
    if (!stillVisible) {
      setSelectedLogisticsMapRequestId(adminLogisticsMapEvents[0]?.requestId ?? null);
    }
  }, [adminLogisticsMapEvents, selectedLogisticsMapRequestId]);

  const dashboardRecentAuditLogs = useMemo(() => (auditLogs?.data || []).slice(0, 5), [auditLogs]);

  const pdfMutation = trpc.request.generatePdf.useMutation({
    onSuccess: (data) => {
      const link = document.createElement('a');
      link.href = data.url;
      link.download = data.fileName;
      link.target = '_blank';
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      toast.success('Document généré avec succès');
    },
    onError: (error) => {
      toast.error(error.message || 'Erreur lors de la génération du document');
    },
  });

  const formatDate = (date: Date | string | null) => {
    if (!date) return '-';
    return new Date(date).toLocaleDateString('fr-FR', {
      day: '2-digit',
      month: '2-digit',
      year: 'numeric',
    });
  };

  const formatDateTime = (date: Date | string | null) => {
    if (!date) return '-';
    return new Date(date).toLocaleString('fr-FR', {
      day: '2-digit',
      month: '2-digit',
      year: 'numeric',
      hour: '2-digit',
      minute: '2-digit',
    });
  };

  const formatTime = (date: Date | string | null) => {
    if (!date) return "--:--";
    return new Date(date).toLocaleTimeString("fr-FR", {
      hour: "2-digit",
      minute: "2-digit",
    });
  };

  const formatAuditActionLabel = (action: string) => {
    switch (action) {
      case "modification":
        return "Mise à jour";
      case "creation":
        return "Création";
      case "suppression":
        return "Suppression";
      case "process_request":
        return "Traitement";
      case "assign_material_return":
        return "Assignation logistique";
      default:
        return action.replace(/_/g, " ");
    }
  };

  const formatAmount = (cents: number | null) => {
    if (!cents) return '-';
    return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(cents / 100);
  };

  function parseRequestFormData(value: string | null | undefined) {
    if (!value) return {};
    try {
      return JSON.parse(value);
    } catch {
      return {};
    }
  }

  const selectedRequestFormData = parseRequestFormData(selectedRequest?.formData);
  const selectedRequestIsMateriel = selectedRequest?.type === 'demande_materiel_evenementiel';
  const selectedRequestIsSalle = selectedRequest?.type === 'demande_salle';
  const selectedRequestNeedsFinancialDecision = selectedRequestIsSalle || selectedRequestIsMateriel;
  const selectedRequestCadreDSU = selectedRequestFormData?.cadreDSU || {};
  const selectedSallePricingPreview = useMemo(() => {
    if (!selectedRequestIsSalle) return null;
    const sallesIds = Array.isArray(selectedRequestFormData?.sallesIds)
      ? selectedRequestFormData.sallesIds.filter((value: unknown): value is string => typeof value === "string" && value.trim().length > 0)
      : [];
    if (sallesIds.length === 0) return null;
    return computeSallePricing({
      sallesIds,
      usageType: dsuProcessData.salleReservation.usageType,
      frequency: dsuProcessData.salleReservation.frequency,
      dateReservation: String(selectedRequestFormData?.dateReservation || ""),
      dateFinReservation: String(selectedRequestFormData?.dateFinReservation || ""),
    });
  }, [
    dsuProcessData.salleReservation.frequency,
    dsuProcessData.salleReservation.usageType,
    selectedRequestFormData,
    selectedRequestIsSalle,
  ]);
  const selectedSalleDecisionAdministrativeText = useMemo(() => {
    if (!selectedRequestIsSalle) return "";
    return buildDecisionAdministrativeText({
      espace: Array.isArray(selectedRequestFormData?.sallesSelectionnees) && selectedRequestFormData.sallesSelectionnees.length > 0
        ? selectedRequestFormData.sallesSelectionnees.join(", ")
        : "-",
      dates: formatReservationSallePeriod({
        dateReservation: String(selectedRequestFormData?.dateReservation || ""),
        dateFinReservation: String(selectedRequestFormData?.dateFinReservation || ""),
      }),
      horaires: getReservationSalleScheduleSummary({
        useDetailedSchedule: Boolean(selectedRequestFormData?.useDetailedSchedule),
        horairesParJour: Array.isArray(selectedRequestFormData?.horairesParJour)
          ? selectedRequestFormData.horairesParJour.filter((slot: any) => slot?.date)
          : [],
        heureDebut: String(selectedRequestFormData?.heureDebut || ""),
        heureFin: String(selectedRequestFormData?.heureFin || ""),
      }),
      association: String(selectedRequestFormData?.nomAssociation || "-"),
    });
  }, [selectedRequestFormData, selectedRequestIsSalle]);
  const selectedRequestedMaterialItems = selectedRequestIsMateriel
    ? materialEventItems.filter((item) => selectedRequestFormData?.materielsDemandes?.[item.key])
    : [];
  const selectedNonRequestedMaterialItems = selectedRequestIsMateriel
    ? materialEventItems.filter((item) => !selectedRequestFormData?.materielsDemandes?.[item.key])
    : [];

  const logisticsAssignmentFormData = parseRequestFormData(logisticsAssignmentRequest?.formData);
  const logisticsAssignmentIsSalle = logisticsAssignmentRequest?.type === "demande_salle";
  const logisticsAssignmentIsMateriel = logisticsAssignmentRequest?.type === "demande_materiel_evenementiel";
  const logisticsAssignmentReservationDailySlots = Array.isArray(logisticsAssignmentFormData?.horairesParJour)
    ? logisticsAssignmentFormData.horairesParJour.filter((slot: any) => slot?.date)
    : [];
  const logisticsAssignmentReservationPeriodSummary = formatReservationSallePeriod({
    dateReservation: String(logisticsAssignmentFormData?.dateReservation || ""),
    dateFinReservation: String(logisticsAssignmentFormData?.dateFinReservation || ""),
  });
  const logisticsAssignmentReservationScheduleSummary = getReservationSalleScheduleSummary({
    useDetailedSchedule: Boolean(logisticsAssignmentFormData?.useDetailedSchedule),
    horairesParJour: logisticsAssignmentReservationDailySlots,
    heureDebut: String(logisticsAssignmentFormData?.heureDebut || ""),
    heureFin: String(logisticsAssignmentFormData?.heureFin || ""),
  });
  const logisticsAssignmentRequestedMaterialItems = logisticsAssignmentIsMateriel
    ? materialEventItems.filter((item) => logisticsAssignmentFormData?.materielsDemandes?.[item.key])
    : [];

  const getInvitationStatusLabel = (entry: NonNullable<typeof directoryEntries>["data"][number]) => {
    if (entry.registered) return "Acceptée";
    switch (entry.invitationStatus?.status) {
      case "sent":
        return "Invitation envoyée";
      case "expired":
        return "Expirée";
      case "accepted":
        return "Acceptée";
      default:
        return "Jamais invitée";
    }
  };

  const getDirectoryIdentifierMeta = (details: NonNullable<typeof selectedDirectoryEntry>) => {
    const resolvedSiret = details.association?.siret || details.entry.siret || null;
    const resolvedRna = details.association?.rna || details.entry.rna || null;

    return {
      siret: {
        value: resolvedSiret,
        source: details.association?.siret ? "Portail" : details.entry.siret ? "Bordereau" : null,
        secondaryValue:
          details.association?.siret && details.entry.siret && details.association.siret !== details.entry.siret
            ? details.entry.siret
            : null,
      },
      rna: {
        value: resolvedRna,
        source: details.association?.rna ? "Portail" : details.entry.rna ? "Bordereau" : null,
        secondaryValue:
          details.association?.rna && details.entry.rna && details.association.rna !== details.entry.rna
            ? details.entry.rna
            : null,
      },
    };
  };

  const getAssociationStatusLabel = (status: string | null | undefined) => {
    if (status === "active") return "Active";
    if (status === "inactive") return "Inactive";
    return "Non déterminé";
  };

  const copyDirectorySecureLink = async (directoryEntryId: number) => {
    try {
      const result = await secureDirectoryInvitationLinkMutation.mutateAsync({ directoryEntryId });
      await navigator.clipboard.writeText(result.invitationLink);
      toast.success("Lien sécurisé copié");
      await utils.associationDirectory.listLatest.invalidate();
      await utils.associationDirectory.getSummary.invalidate();
    } catch {
      toast.error("Impossible de copier le lien sécurisé");
    }
  };

  const openRequestProcessDialog = (request: any) => {
    const formData = parseRequestFormData(request?.formData);
    const workflow = formData?.salleWorkflow && typeof formData.salleWorkflow === "object" ? formData.salleWorkflow : null;
    if (request?.type === "demande_salle" && workflow?.quoteStatus === "en_attente_association") {
      toast.info("Cette demande est en attente de réponse de l’association. L’accueil ne peut plus la traiter tant que le devis n’est pas accepté.");
      return;
    }
    const existingCadreDSU = formData?.cadreDSU || {};
    const existingFinancialDecision = existingCadreDSU?.materielEvent?.financialDecision || {};

    setSelectedRequest(request);
    setProcessData({
      status: '',
      commentaireAdmin: request?.commentaireAdmin || '',
      montantAccorde: request?.montantDemande ? String(request.montantDemande / 100) : '',
      recapServiceId:
        typeof formData?.notificationTrace?.serviceId === 'number'
          ? String(formData.notificationTrace.serviceId)
          : '',
      recapRecipients: Array.isArray(formData?.notificationTrace?.recipients)
        ? formData.notificationTrace.recipients.join(', ')
        : '',
      recapServiceLabel: formData?.notificationTrace?.serviceLabel || '',
      supervisionServiceId:
        typeof formData?.notificationTrace?.supervisionServiceId === 'number'
          ? String(formData.notificationTrace.supervisionServiceId)
          : '',
      supervisionRecipients: Array.isArray(formData?.notificationTrace?.supervisionRecipients)
        ? formData.notificationTrace.supervisionRecipients.join(', ')
        : '',
      supervisionServiceLabel: formData?.notificationTrace?.supervisionServiceLabel || '',
    });
    setDsuProcessData({
      dateReception: existingCadreDSU?.dateReception || new Date().toISOString().split('T')[0],
      dateDecision: existingCadreDSU?.dateDecision || new Date().toISOString().split('T')[0],
      responsableDSU: existingCadreDSU?.responsableDSU || '',
      observationsDSU: existingCadreDSU?.observationsDSU || '',
      salleReservation: {
        usageType: existingCadreDSU?.salleReservation?.usageType === 'occasionnel'
          ? 'occasionnel'
          : (formData?.typeUsage === 'occasionnel' ? 'occasionnel' : 'conventionne'),
        frequency: existingCadreDSU?.salleReservation?.frequency === 'demi_journee' || existingCadreDSU?.salleReservation?.frequency === 'mensuel'
          ? existingCadreDSU.salleReservation.frequency
          : (formData?.frequence === 'demi_journee' || formData?.frequence === 'mensuel' ? formData.frequence : 'journee'),
      },
      materielEvent: {
        itemsAccordes: { ...emptyMaterialEventSelection(), ...(existingCadreDSU?.materielEvent?.itemsAccordes || {}) },
        quantitesAccordees: { ...emptyMaterialEventQuantities(), ...(existingCadreDSU?.materielEvent?.quantitesAccordees || {}) },
        autresPrecisions: existingCadreDSU?.materielEvent?.autresPrecisions || '',
        financialDecision: {
          financialMode: (existingFinancialDecision?.financialMode || 'gratuite') as MaterialFinancialMode,
          depositRequired: Boolean(existingFinancialDecision?.depositRequired),
          depositAmountEuros: formatCentsAsEuroInput(existingFinancialDecision?.depositAmountCents ?? null),
          rentalAmountEuros: formatCentsAsEuroInput(existingFinancialDecision?.rentalAmountCents ?? null),
          pricingNotes: existingFinancialDecision?.pricingNotes || '',
        },
      },
    });
  };

  const handleProcess = (generateContract = false) => {
    if (!selectedRequest || !processData.status) {
      toast.error('Veuillez sélectionner un statut');
      return;
    }

    if ((selectedRequestIsSalle || selectedRequestIsMateriel) && !dsuProcessData.responsableDSU.trim()) {
      toast.error('Le responsable DSU est obligatoire pour traiter cette demande');
      return;
    }

    if (selectedRequestNeedsFinancialDecision && processData.status === 'validee' && !dsuProcessData.materielEvent.financialDecision.financialMode) {
      toast.error(`Le mode financier est obligatoire pour valider une ${selectedRequestIsSalle ? 'demande de salle' : 'demande de matériel'}`);
      return;
    }
    if (
      selectedRequestIsSalle
      && dsuProcessData.materielEvent.financialDecision.financialMode === 'location_payante'
      && (!selectedSallePricingPreview || selectedSallePricingPreview.unsupportedSalles.length > 0)
    ) {
      toast.error(
        selectedSallePricingPreview?.unsupportedSalles?.length
          ? `La tarification automatique n'est pas disponible pour : ${selectedSallePricingPreview.unsupportedSalles.map((item) => item.salleNom).join(', ')}`
          : "Le devis ne peut pas être calculé automatiquement pour cette réservation"
      );
      return;
    }
    if (selectedRequestIsMateriel && processData.status === 'validee') {
      const invalidItem = materialEventItems.find((item) => {
        if (!dsuProcessData.materielEvent.itemsAccordes?.[item.key]) return false;
        return parseMaterialEventQuantity(dsuProcessData.materielEvent.quantitesAccordees?.[item.key]) <= 0;
      });
      if (invalidItem) {
        toast.error(`Renseigne une quantité accordée valide pour ${invalidItem.label}`);
        return;
      }
    }

    processMutation.mutate({
      id: selectedRequest.id,
      status: processData.status as any,
      commentaireAdmin: processData.commentaireAdmin || undefined,
      montantAccorde: processData.montantAccorde ? parseInt(processData.montantAccorde) * 100 : undefined,
      recapServiceId: processData.recapServiceId ? parseInt(processData.recapServiceId, 10) : undefined,
      recapRecipients: processData.recapRecipients
        ? processData.recapRecipients.split(/[,;\n]/).map(v => v.trim()).filter(Boolean)
        : undefined,
      recapServiceLabel: processData.recapServiceLabel || undefined,
      supervisionServiceId: processData.supervisionServiceId ? parseInt(processData.supervisionServiceId, 10) : undefined,
      supervisionRecipients: processData.supervisionRecipients
        ? processData.supervisionRecipients.split(/[,;\n]/).map(v => v.trim()).filter(Boolean)
        : undefined,
      supervisionServiceLabel: processData.supervisionServiceLabel || undefined,
      dsuData: (selectedRequestIsSalle || selectedRequestIsMateriel) ? {
        dateReception: dsuProcessData.dateReception,
        dateDecision: dsuProcessData.dateDecision,
        responsableDSU: dsuProcessData.responsableDSU,
        observationsDSU: dsuProcessData.observationsDSU || undefined,
        ...(selectedRequestNeedsFinancialDecision ? {
          cautionRequise: dsuProcessData.materielEvent.financialDecision.depositRequired,
          montantCaution: dsuProcessData.materielEvent.financialDecision.depositRequired
            ? dsuProcessData.materielEvent.financialDecision.depositAmountEuros
            : undefined,
          ...(selectedRequestIsSalle ? {
            salleReservation: {
              usageType: dsuProcessData.salleReservation.usageType,
              frequency: dsuProcessData.salleReservation.frequency,
            },
          } : {}),
          materielEvent: {
            itemsAccordes: dsuProcessData.materielEvent.itemsAccordes,
            quantitesAccordees: dsuProcessData.materielEvent.quantitesAccordees,
            autresPrecisions: dsuProcessData.materielEvent.autresPrecisions || undefined,
            financialDecision: {
              financialMode: dsuProcessData.materielEvent.financialDecision.financialMode,
              depositRequired: dsuProcessData.materielEvent.financialDecision.depositRequired,
              depositAmountCents: parseEuroInputToCents(dsuProcessData.materielEvent.financialDecision.depositAmountEuros) || 0,
              rentalAmountCents: parseEuroInputToCents(dsuProcessData.materielEvent.financialDecision.rentalAmountEuros) || 0,
              pricingNotes: dsuProcessData.materielEvent.financialDecision.pricingNotes || undefined,
              contractStatus: generateContract ? 'generee' : 'a_generer',
            },
          },
        } : {}),
      } : undefined,
      generateContract,
    });
  };

  const handleAppearanceSave = () => {
    setAppearanceMutation.mutate(appearanceForm);
  };

  useEffect(() => {
    if (!selectedRequestIsSalle) return;
    if (dsuProcessData.materielEvent.financialDecision.financialMode !== 'location_payante') return;
    const computedAmount = selectedSallePricingPreview && selectedSallePricingPreview.unsupportedSalles.length === 0
      ? (selectedSallePricingPreview.totalAmountCents / 100).toFixed(2)
      : '';
    setDsuProcessData((prev) => {
      if (prev.materielEvent.financialDecision.rentalAmountEuros === computedAmount) return prev;
      return {
        ...prev,
        materielEvent: {
          ...prev.materielEvent,
          financialDecision: {
            ...prev.materielEvent.financialDecision,
            rentalAmountEuros: computedAmount,
          },
        },
      };
    });
  }, [
    dsuProcessData.materielEvent.financialDecision.financialMode,
    selectedRequestIsSalle,
    selectedSallePricingPreview,
  ]);

  const handleMailProviderChange = (provider: SupportedMailProvider) => {
    setMailSettingsForm((prev) => {
      if (provider === "custom") {
        return { ...prev, smtpProvider: provider };
      }

      const defaults = providerDefaults[provider];
      return {
        ...prev,
        smtpProvider: provider,
        smtpHost: defaults.host,
        smtpPort: String(defaults.port),
        smtpSecure: defaults.secure,
        smtpRequireTls: defaults.requireTLS,
      };
    });
  };

  const handleMailSettingsSave = () => {
    if (!mailSettingsForm.smtpFrom.trim()) {
      toast.error("Renseigne l’adresse d’envoi");
      return;
    }

    setMailSettingsMutation.mutate({
      smtpProvider: mailSettingsForm.smtpProvider,
      smtpHost: mailSettingsForm.smtpHost.trim() || undefined,
      smtpPort: mailSettingsForm.smtpPort.trim() || undefined,
      smtpUser: mailSettingsForm.smtpUser.trim() || undefined,
      smtpFrom: mailSettingsForm.smtpFrom.trim(),
      smtpSecure: mailSettingsForm.smtpSecure,
      smtpRequireTls: mailSettingsForm.smtpRequireTls,
      smtpPass: mailSettingsForm.smtpPass.trim() || undefined,
    });
  };

  const handleSendTestMail = () => {
    if (!mailSettingsForm.testRecipient.trim()) {
      toast.error("Renseigne une adresse de test");
      return;
    }

    sendTestMailMutation.mutate({
      to: mailSettingsForm.testRecipient.trim(),
    });
  };

  const handleLogisticsSettingsSave = () => {
    const inventory = Object.fromEntries(
      Object.entries(logisticsSettingsForm.inventory).map(([key, value]) => {
        const cleaned = String(value || "").trim();
        if (!cleaned) {
          return [key, null];
        }
        const parsed = Number.parseInt(cleaned, 10);
        return [key, Number.isFinite(parsed) && parsed >= 0 ? parsed : null];
      })
    );
    const replacementValues = Object.fromEntries(
      Object.entries(logisticsSettingsForm.replacementValues).map(([key, value]) => {
        return [key, parseEuroInputToCents(String(value || ""))];
      })
    );

    setLogisticsSettingsMutation.mutate({
      materialReturnLeadDays: logisticsSettingsForm.materialReturnLeadDays,
      materialReturnGraceDays: logisticsSettingsForm.materialReturnGraceDays,
      inventory,
      replacementValues,
    });
  };

  const resetAppearanceForm = () => {
    const nextAppearance = appearanceSettings || defaultPortalAppearance;
    setAppearanceForm(nextAppearance);
    setManualAppearanceBackgroundTargets(resolveManualBackgroundTargets(nextAppearance.backgroundTargets));
  };

  const previewAppearance = appearancePreviewEnabled ? appearanceForm : (appearanceSettings || defaultPortalAppearance);
  const previewHeroImage = previewAppearance.heroImageUrl || heroIllustrationUrl;
  const previewBackgroundImage = appearanceForm.backgroundImageUrl || "";
  const previewBackgroundOverlay = `rgba(247, 250, 252, ${appearanceForm.backgroundOverlayOpacity / 100})`;
  const resolveAppearanceTargetPreviewImage = (target: AppearanceBackgroundTarget) =>
    appearanceForm.backgroundTargetImageUrls?.[target] ||
    (target.startsWith("public_")
      ? appearanceForm.backgroundTargetImageUrls?.public
      : target.startsWith("dashboard_")
        ? appearanceForm.backgroundTargetImageUrls?.dashboard
        : target.startsWith("admin_")
          ? appearanceForm.backgroundTargetImageUrls?.admin
          : "") ||
    previewBackgroundImage;
  const resolveAppearanceTargetImageSourceLabel = (target: AppearanceBackgroundTarget) => {
    if (appearanceForm.backgroundTargetImageUrls?.[target]) {
      return "Fond dédié";
    }
    if (target.startsWith("public_") && appearanceForm.backgroundTargetImageUrls?.public) {
      return "Hérite du groupe public";
    }
    if (target.startsWith("dashboard_") && appearanceForm.backgroundTargetImageUrls?.dashboard) {
      return "Hérite du parcours association";
    }
    if (target.startsWith("admin_") && appearanceForm.backgroundTargetImageUrls?.admin) {
      return "Hérite du pilotage interne";
    }
    if (previewBackgroundImage) {
      return "Hérite du fond global";
    }
    return "Fond neutre";
  };
  const resolveAppearanceTargetOverlay = (target: AppearanceBackgroundTarget) =>
    appearanceForm.backgroundTargetOverlayOpacities?.[target] ?? appearanceForm.backgroundOverlayOpacity;
  const resolveAppearanceTargetBlur = (target: AppearanceBackgroundTarget) =>
    appearanceForm.backgroundTargetBlurRadii?.[target] ?? appearanceForm.backgroundBlurRadius;
  const resolveAppearanceTargetPosition = (target: AppearanceBackgroundTarget) =>
    appearanceForm.backgroundTargetPositions?.[target] ||
    (appearanceForm.backgroundMode === "repeat" ? "top_left" : "center");
  const resolveAppearanceTargetPresentationMode = (target: AppearanceBackgroundTarget) =>
    appearanceForm.backgroundTargetPresentationModes?.[target] || appearanceForm.backgroundPresentationMode;
  const previewBackgroundStyle =
    previewBackgroundImage && appearanceForm.backgroundEnabled
      ? {
          backgroundImage: `linear-gradient(${previewBackgroundOverlay}, ${previewBackgroundOverlay}), url(${previewBackgroundImage})`,
          backgroundSize: appearanceForm.backgroundMode === "repeat" ? "auto" : "cover",
          backgroundRepeat: appearanceForm.backgroundMode === "repeat" ? "repeat" : "no-repeat",
          backgroundAttachment: appearanceForm.backgroundMode === "fixed" ? "fixed" : "scroll",
          backgroundPosition:
            appearanceForm.backgroundMode === "repeat"
              ? "top left"
              : appearanceBackgroundPositionCssValues.center,
        }
      : undefined;
  const isBackgroundTargetingAll = appearanceBackgroundTargets.every((target) =>
    appearanceForm.backgroundTargets.includes(target)
  );
  const selectedBackgroundTargetCount = appearanceForm.backgroundTargets.length;
  const backgroundTargetPreviewMeta: Record<
    AppearanceBackgroundTarget,
    { label: string; accent: string; surface: string; detail: string }
  > = {
    home: {
      label: "Accueil",
      accent: "text-emerald-700",
      surface: "border-emerald-200 bg-emerald-50",
      detail: "Hero, navigation d’entrée, CTA principaux",
    },
    public: {
      label: "Pages publiques",
      accent: "text-sky-700",
      surface: "border-sky-200 bg-sky-50",
      detail: "Annuaire, carte, fiches et pages d’information",
    },
    public_directory: {
      label: "Annuaire public",
      accent: "text-cyan-700",
      surface: "border-cyan-200 bg-cyan-50",
      detail: "Liste publique, fiches visibles et recherche libre",
    },
    public_map: {
      label: "Carte publique",
      accent: "text-teal-700",
      surface: "border-teal-200 bg-teal-50",
      detail: "Cartographie, filtres géographiques et implantation",
    },
    dashboard: {
      label: "Espace association complet",
      accent: "text-violet-700",
      surface: "border-violet-200 bg-violet-50",
      detail: "Tableau de bord, Mon association, Mes documents, Mes demandes, Nouvelle demande, Réservation de salle, Matériel événementiel et Annuaire",
    },
    dashboard_home: {
      label: "Tableau de bord",
      accent: "text-fuchsia-700",
      surface: "border-fuchsia-200 bg-fuchsia-50",
      detail: "Accueil connecté et accès rapide aux actions prioritaires",
    },
    dashboard_profile: {
      label: "Mon association",
      accent: "text-indigo-700",
      surface: "border-indigo-200 bg-indigo-50",
      detail: "Fiche association, coordonnées, documents et identité",
    },
    dashboard_documents: {
      label: "Mes documents",
      accent: "text-blue-700",
      surface: "border-blue-200 bg-blue-50",
      detail: "Pièces administratives, téléchargements et suivi documentaire",
    },
    dashboard_requests: {
      label: "Mes demandes",
      accent: "text-emerald-700",
      surface: "border-emerald-200 bg-emerald-50",
      detail: "Suivi des demandes, statuts, devis et décisions",
    },
    dashboard_new_request: {
      label: "Nouvelle demande",
      accent: "text-orange-700",
      surface: "border-orange-200 bg-orange-50",
      detail: "Choix de démarche, formulaires et lancement de dossier",
    },
    dashboard_reservation_salle: {
      label: "Réservation de salle",
      accent: "text-amber-700",
      surface: "border-amber-200 bg-amber-50",
      detail: "Formulaire salle, créneaux, pièces et validation",
    },
    dashboard_material: {
      label: "Matériel événementiel",
      accent: "text-red-700",
      surface: "border-red-200 bg-red-50",
      detail: "Demande matériel, quantités, besoins et restitution",
    },
    dashboard_directory: {
      label: "Annuaire association",
      accent: "text-purple-700",
      surface: "border-purple-200 bg-purple-50",
      detail: "Répertoire consultable depuis l’espace connecté",
    },
    admin: {
      label: "Administration complète",
      accent: "text-amber-700",
      surface: "border-amber-200 bg-amber-50",
      detail: "Back-office, réglages et pilotage métier",
    },
    admin_pilotage: {
      label: "Suivi & pilotage",
      accent: "text-slate-700",
      surface: "border-slate-200 bg-slate-50",
      detail: "Dashboard admin, demandes, stats, notifications et calendrier",
    },
    admin_logistics: {
      label: "Logistique",
      accent: "text-orange-700",
      surface: "border-orange-200 bg-orange-50",
      detail: "Suivi logistique, terrain et services matériel",
    },
    admin_directory: {
      label: "Référentiels & bordereau",
      accent: "text-lime-700",
      surface: "border-lime-200 bg-lime-50",
      detail: "Bordereau, annuaire CCDS et vues de référentiel",
    },
    admin_compliance: {
      label: "Conformité & réglages",
      accent: "text-rose-700",
      surface: "border-rose-200 bg-rose-50",
      detail: "Conformité, apparence, audit et paramétrage sensible",
    },
  };
  const backgroundPreviewTargets = [...appearanceBackgroundTargets];
  const filteredBackgroundPreviewTargets = backgroundPreviewTargets.filter((target) => {
    if (appearancePreviewFamily === "all") return true;
    if (appearancePreviewFamily === "public") return target === "home" || target.startsWith("public");
    if (appearancePreviewFamily === "association") return target === "dashboard" || target.startsWith("dashboard_");
    return target === "admin" || target.startsWith("admin_");
  });
  const appearancePreviewFamilyLabels: Record<"all" | "public" | "association" | "admin", string> = {
    all: "Toutes les pages",
    public: "Entrée publique",
    association: "Espace association",
    admin: "Administration",
  };

  const commitAppearanceColor = (
    field: "primaryColor" | "secondaryColor" | "accentColor" | "buttonColor" | "alertColor",
    value: string
  ) => {
    const trimmed = value.trim();
    const withHash = trimmed.startsWith("#") ? trimmed : `#${trimmed}`;

    if (/^#([0-9a-fA-F]{6})$/.test(withHash)) {
      setAppearanceForm(prev => ({ ...prev, [field]: withHash.toLowerCase() }));
      return;
    }

    switch (field) {
      case "primaryColor":
        setPrimaryColorInput(appearanceForm.primaryColor);
        break;
      case "secondaryColor":
        setSecondaryColorInput(appearanceForm.secondaryColor);
        break;
      case "accentColor":
        setAccentColorInput(appearanceForm.accentColor);
        break;
      case "buttonColor":
        setButtonColorInput(appearanceForm.buttonColor);
        break;
      case "alertColor":
        setAlertColorInput(appearanceForm.alertColor);
        break;
    }
  };

  const applyAppearancePreset = (presetId: string) => {
    const preset = appearanceSkinPresets.find((entry) => entry.id === presetId);
    if (!preset) return;
    const nextManualTargets = resolveManualBackgroundTargets(
      preset.value.backgroundTargets || defaultPortalAppearance.backgroundTargets
    );
    setManualAppearanceBackgroundTargets(nextManualTargets);
    setAppearanceForm((prev) => ({
      ...prev,
      ...preset.value,
      activeSkinId: preset.id,
    }));
  };

  const readFileAsBase64 = (file: File) =>
    new Promise<string>((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => {
        const result = String(reader.result || "");
        resolve(result.split(",")[1] || "");
      };
      reader.onerror = () => reject(reader.error);
      reader.readAsDataURL(file);
    });

  const handleHeroImageSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;

    if (!file.type.startsWith("image/")) {
      toast.error("Choisis une image PNG, JPG, WebP ou SVG");
      event.target.value = "";
      return;
    }

    setIsHeroUploadBusy(true);
    try {
      const base64 = await readFileAsBase64(file);

      await uploadHeroImageMutation.mutateAsync({
        fileData: base64,
        fileName: file.name,
        mimeType: file.type || "application/octet-stream",
      });
    } catch {
      toast.error("Impossible de lire l’image sélectionnée");
    } finally {
      setIsHeroUploadBusy(false);
      event.target.value = "";
    }
  };

  const handleHeroImageReset = () => {
    setAppearanceForm(prev => ({ ...prev, activeSkinId: "custom", heroImageUrl: "" }));
  };

  const handleAppearanceAssetSelect = async (
    field: AppearanceAssetField,
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    const file = event.target.files?.[0];
    if (!file) return;

    const acceptsIco = field === "faviconUrl";
    const isImage = file.type.startsWith("image/");
    const isIco = acceptsIco && (file.type === "image/x-icon" || file.name.toLowerCase().endsWith(".ico"));
    if (!isImage && !isIco) {
      toast.error(field === "faviconUrl" ? "Choisis une image ou un fichier .ico" : "Choisis une image PNG, JPG, WebP ou SVG");
      event.target.value = "";
      return;
    }

    setIsAppearanceAssetUploadBusy(field);
    try {
      const base64 = await readFileAsBase64(file);
      await uploadAppearanceAssetMutation.mutateAsync({
        field,
        fileData: base64,
        fileName: file.name,
        mimeType: file.type || "application/octet-stream",
      });
    } catch {
      toast.error("Impossible de lire le fichier sélectionné");
    } finally {
      setIsAppearanceAssetUploadBusy(null);
      event.target.value = "";
    }
  };

  const handleAppearanceAssetReset = (field: AppearanceAssetField) => {
    setAppearanceForm((prev) => ({
      ...prev,
      activeSkinId: "custom",
      [field]: "",
      ...(field === "backgroundImageUrl" ? { backgroundEnabled: false } : {}),
    }));
  };

  const handleAppearanceTargetAssetSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const target = pendingBackgroundTargetUpload;
    const file = event.target.files?.[0];
    if (!target || !file) {
      event.target.value = "";
      return;
    }

    if (!file.type.startsWith("image/")) {
      toast.error("Choisis une image PNG, JPG, WebP ou SVG");
      event.target.value = "";
      return;
    }

    setIsAppearanceTargetAssetUploadBusy(target);
    try {
      const base64 = await readFileAsBase64(file);
      await uploadAppearanceTargetAssetMutation.mutateAsync({
        target,
        fileData: base64,
        fileName: file.name,
        mimeType: file.type || "application/octet-stream",
      });
    } catch {
      toast.error("Impossible de lire l’image sélectionnée");
    } finally {
      setIsAppearanceTargetAssetUploadBusy(null);
      setPendingBackgroundTargetUpload(null);
      event.target.value = "";
    }
  };

  const handleAppearanceTargetAssetReset = (target: AppearanceBackgroundTarget) => {
    setAppearanceForm((prev) => {
      const nextBackgroundTargetImageUrls = { ...(prev.backgroundTargetImageUrls || {}) };
      delete nextBackgroundTargetImageUrls[target];
      return {
        ...prev,
        activeSkinId: "custom",
        backgroundTargetImageUrls: nextBackgroundTargetImageUrls,
      };
    });
  };

  const handleAddDecorativeElement = () => {
    setAppearanceForm((prev) => {
      const nextIndex = (prev.decorativeElements?.length || 0) + 1;
      const nextDecoration: PortalAppearanceDecoration = {
        id: `decor-${Date.now()}-${nextIndex}`,
        imageUrl: "",
        scope: "all",
        layer: "background",
        widthPercent: 16,
        topPercent: 18,
        leftPercent: 8,
        opacity: 12,
        blurRadius: 2,
        cardAnchor: "top_right",
        cardInsetPercent: 6,
      };

      return {
        ...prev,
        activeSkinId: "custom",
        decorativeElements: [...(prev.decorativeElements || []), nextDecoration].slice(0, 6),
      };
    });
  };

  const updateDecorativeElement = (id: string, patch: Partial<PortalAppearanceDecoration>) => {
    setAppearanceForm((prev) => ({
      ...prev,
      activeSkinId: "custom",
      decorativeElements: (prev.decorativeElements || []).map((entry) =>
        entry.id === id ? { ...entry, ...patch } : entry
      ),
    }));
  };

  const applyDecorativePreset = (id: string, patch: Partial<PortalAppearanceDecoration>) => {
    updateDecorativeElement(id, patch);
  };

  const handleRemoveDecorativeElement = (id: string) => {
    setAppearanceForm((prev) => ({
      ...prev,
      activeSkinId: "custom",
      decorativeElements: (prev.decorativeElements || []).filter((entry) => entry.id !== id),
    }));
  };

  const handleDecorationAssetSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const decorationId = pendingDecorationUploadId;
    const file = event.target.files?.[0];
    if (!decorationId || !file) {
      event.target.value = "";
      return;
    }

    const currentDecoration = (appearanceForm.decorativeElements || []).find((entry) => entry.id === decorationId);
    if (!currentDecoration) {
      toast.error("Le décor sélectionné n’existe plus dans le formulaire");
      event.target.value = "";
      setPendingDecorationUploadId(null);
      return;
    }

    if (!file.type.startsWith("image/")) {
      toast.error("Choisis une image PNG, JPG, WebP ou SVG");
      event.target.value = "";
      return;
    }

    setIsAppearanceDecorationUploadBusy(decorationId);
    try {
      const base64 = await readFileAsBase64(file);
      await uploadAppearanceDecorationAssetMutation.mutateAsync({
        decorationId,
        fileData: base64,
        fileName: file.name,
        mimeType: file.type || "application/octet-stream",
        decoration: currentDecoration,
      });
      if (currentDecoration.layer === "card" && (file.type === "image/jpeg" || file.name.toLowerCase().endsWith(".jpg") || file.name.toLowerCase().endsWith(".jpeg"))) {
        toast.info("Le JPG fonctionne, mais pour un relief propre sur une card, préfère un PNG, SVG ou WebP avec transparence.");
      }
    } catch {
      toast.error("Impossible de lire l’image décorative");
    } finally {
      setIsAppearanceDecorationUploadBusy(null);
      setPendingDecorationUploadId(null);
      event.target.value = "";
    }
  };

  const handleSalleRibSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;

    const allowedMimeTypes = new Set([
      "application/pdf",
      "image/png",
      "image/jpeg",
      "image/webp",
    ]);
    if (!allowedMimeTypes.has(file.type || "")) {
      toast.error("Choisis un PDF, PNG, JPG ou WebP pour le RIB MJS");
      event.target.value = "";
      return;
    }

    setIsSalleRibUploadBusy(true);
    try {
      const base64 = await new Promise<string>((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => {
          const result = String(reader.result || "");
          resolve(result.split(",")[1] || "");
        };
        reader.onerror = () => reject(reader.error);
        reader.readAsDataURL(file);
      });

      await uploadSalleRibMutation.mutateAsync({
        fileData: base64,
        fileName: file.name,
        mimeType: file.type || "application/octet-stream",
      });
    } catch {
      toast.error("Impossible de lire le RIB sélectionné");
    } finally {
      setIsSalleRibUploadBusy(false);
      event.target.value = "";
    }
  };

  const handleDirectoryWorkbookSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;

    setSelectedDirectoryFileName(file.name);

    try {
      const base64 = await new Promise<string>((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => {
          const result = String(reader.result || "");
          resolve(result.split(",")[1] || "");
        };
        reader.onerror = () => reject(reader.error);
        reader.readAsDataURL(file);
      });

      const payload = {
        fileData: base64,
        fileName: file.name,
        mimeType: file.type || "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      };

      setDirectoryFilePayload(payload);
      previewDirectoryImportMutation.mutate(payload);
    } catch {
      toast.error("Impossible de lire le fichier Excel");
    } finally {
      event.target.value = "";
    }
  };

  const handleAssociationDirectoryImport = () => {
    if (!directoryFilePayload) {
      toast.error("Choisis d'abord un fichier Excel");
      return;
    }

    if (directoryPreview?.duplicateGroups?.some(group => !directoryDuplicateSelections[group.emailOfficielNormalise])) {
      toast.error("Choisis une ligne à conserver pour chaque email dupliqué");
      return;
    }

    importDirectoryMutation.mutate({
      ...directoryFilePayload,
      duplicateSelections: directoryDuplicateSelections,
    });
  };

  if (!canAccessFullAdmin) {
    return (
      <div className="portal-immersive-page min-h-screen flex items-center justify-center">
        <Card className="max-w-md">
          <CardContent className="py-12 text-center">
            <Shield className="h-12 w-12 mx-auto text-muted-foreground mb-4" />
            <h2 className="text-xl font-bold mb-2">Accès refusé</h2>
            <p className="text-muted-foreground mb-4">
              Cette page est réservée aux administrateurs.
            </p>
            <Link href="/dashboard" {...buildRoutePrefetchHandlers("/dashboard")}>
              <Button>Retour au tableau de bord</Button>
            </Link>
          </CardContent>
        </Card>
      </div>
    );
  }

  return (
    <div className="portal-immersive-page min-h-screen overflow-x-hidden">
      {/* Header */}
      <header className="border-b border-border bg-white sticky top-0 z-10">
        <div className="container py-4">
          <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
            <div className="flex min-w-0 items-start gap-3 sm:items-center sm:gap-4">
              <Link href="/dashboard" {...buildRoutePrefetchHandlers("/dashboard")}>
                <Button variant="ghost" size="icon">
                  <ArrowLeft className="h-5 w-5" />
                </Button>
              </Link>
              <div data-tour-id="admin-header" className="min-w-0">
                <h1 className="flex flex-wrap items-center gap-2 text-lg font-bold sm:text-xl">
                  <Shield className="h-5 w-5 text-primary" />
                  {isAccueil
                    ? "Administration accueil"
                    : isLogisticsControl
                      ? "Administration matériel CCDS"
                      : "Administration"}
                </h1>
                <p className="text-sm text-muted-foreground">
                  {dashboardCopy.subtitle}
                </p>
              </div>
            </div>
            <div className="flex w-full flex-col gap-2 sm:flex-row sm:flex-wrap sm:items-center lg:w-auto lg:justify-end">
              <Button variant="outline" size="sm" onClick={() => {
                refetchStats();
                refetchRequests();
              }} className={cn("justify-center", isMobile ? "w-full" : "")}>
                <RefreshCw className="h-4 w-4 mr-2" />
                Actualiser
              </Button>
              {!isAccueil && !isLogisticsControl && (
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => openAdminTab('users')}
                  {...buildRoutePrefetchHandlers("/admin?tab=users")}
                  className={cn(isMobile ? "w-full justify-center" : "")}
                >
                  <UserCog className="h-4 w-4 mr-2" />
                  Utilisateurs
                </Button>
              )}
              {!isAccueil && !isLogisticsControl && (
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => openAdminTab('appearance')}
                  {...buildRoutePrefetchHandlers("/admin?tab=appearance")}
                  className={cn(isMobile ? "w-full justify-center" : "")}
                >
                  <Palette className="h-4 w-4 mr-2" />
                  Apparence
                </Button>
              )}
              {!isAccueil && !isLogisticsControl && (
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => openAdminTab('audit')}
                  {...buildRoutePrefetchHandlers("/admin?tab=audit")}
                  className={cn(isMobile ? "w-full justify-center" : "")}
                >
                  <FileText className="h-4 w-4 mr-2" />
                  Journal d'audit
                </Button>
              )}
              <Button 
                variant="outline" 
                size={isMobile ? "sm" : "icon"} 
                className={cn("relative", isMobile ? "w-full justify-center" : "")}
                onClick={() => openAdminTab('notifications')}
                {...buildRoutePrefetchHandlers("/admin?tab=notifications")}
                data-tour-id="admin-tab-notifications"
              >
                <Bell className={cn("h-4 w-4", isMobile ? "mr-2" : "")} />
                {isMobile ? "Notifications" : null}
                {(unreadCount || 0) > 0 && (
                  <span className="absolute -top-1 -right-1 h-5 w-5 rounded-full bg-red-500 text-white text-xs flex items-center justify-center">
                    {unreadCount}
                  </span>
                )}
              </Button>
            </div>
          </div>
        </div>
      </header>

      <main className="container py-6">
        <Tabs value={activeTab} onValueChange={openAdminTab} className="space-y-6">
          {/* Dashboard Tab */}
          <TabsContent value="dashboard" className="space-y-6">
            {statsLoading ? (
              <div className="flex items-center justify-center py-12">
                <Loader2 className="h-8 w-8 animate-spin text-primary" />
              </div>
            ) : (
              <>
                <RoleHeroPanel
                  icon={isAccueil ? CalendarClock : isLogisticsControl ? PackageCheck : Shield}
                  title={dashboardCopy.title}
                  subtitle={dashboardCopy.subtitle}
                  badges={roleHeroBadges}
                  alert={roleHeroMonitoringAlert}
                />

                <QuickActionsByRole
                  title={dashboardCopy.quickActionsTitle}
                  description={dashboardCopy.quickActionsDescription}
                  actions={roleQuickActions}
                />

                <Suspense fallback={<AdminPanelLoader compact />}>
                  <AccountSecurityPanel compact />
                </Suspense>

                <RoleKpiStrip
                  title={dashboardCopy.statsTitle}
                  description={dashboardCopy.statsDescription}
                  items={roleKpiItems}
                />

                {(user?.role === "admin" || user?.role === "super_admin") && (
                  <AdminSectionCollapsible
                    title="Supervision rapide"
                    description="Le dernier état de la production et de la préprod, sans quitter le tableau de bord."
                    open={!adminCollapsedSections.dashboardSupervision}
                    onOpenChange={(open) => setAdminSectionExpanded("dashboardSupervision", open)}
                    headerBadge={
                      <Button type="button" variant="outline" size="sm" onClick={() => openAdminTab("analytics")}>
                        <Shield className="mr-2 h-4 w-4" />
                        Ouvrir le détail du contrôle
                      </Button>
                    }
                    className="border-primary/10 bg-white"
                    contentClassName="pt-1"
                  >
                    <div className="grid gap-4 lg:grid-cols-2">
                      {([
                        { key: "production", label: "Production", report: operationalTourLatestByEnvironment.production },
                        { key: "secondary", label: "Préprod", report: operationalTourLatestByEnvironment.secondary },
                      ] as const).map((entry) => {
                        const meta = entry.report ? getOperationalTourStatusMeta(entry.report.status) : getOperationalTourStatusMeta("warning");
                        return (
                          <div key={entry.key} className="rounded-lg border bg-muted/20 p-4">
                            <div className="flex items-start justify-between gap-3">
                              <div>
                                <p className="text-sm text-muted-foreground">{entry.label}</p>
                                <Badge className={`mt-2 border ${meta.className}`}>
                                  {entry.report ? meta.label : "Aucun contrôle"}
                                </Badge>
                              </div>
                              {entry.report?.status === "ok" ? (
                                <CheckCircle className="h-5 w-5 text-emerald-600" />
                              ) : entry.report?.status === "error" ? (
                                <AlertTriangle className="h-5 w-5 text-red-600" />
                              ) : (
                                <Shield className="h-5 w-5 text-amber-600" />
                              )}
                            </div>
                            <div className="mt-4 space-y-2 text-sm">
                              <div className="flex items-center justify-between gap-4">
                                <span className="text-muted-foreground">Dernier contrôle</span>
                                <span className="font-medium">
                                  {entry.report ? formatDateTime(entry.report.finishedAt) : "Jamais"}
                                </span>
                              </div>
                              <div className="flex items-center justify-between gap-4">
                                <span className="text-muted-foreground">Résumé</span>
                                <span className="font-medium">
                                  {entry.report
                                    ? `${entry.report.summary.ok} OK • ${entry.report.summary.warning} avert. • ${entry.report.summary.error} err.`
                                    : "Aucun historique"}
                                </span>
                              </div>
                              <div className="flex items-center justify-between gap-4">
                                <span className="text-muted-foreground">URL testée</span>
                                <span className="max-w-[60%] truncate text-right font-medium">
                                  {entry.report?.baseUrl || operationalTourSettingsForm.targets[entry.key].baseUrl || "-"}
                                </span>
                              </div>
                            </div>
                          </div>
                        );
                      })}
                    </div>
                  </AdminSectionCollapsible>
                )}

                <div className="grid gap-4 xl:grid-cols-[1.5fr_0.5fr]">
                  <AdminSectionCollapsible
                    title={dashboardCopy.sectionTitle}
                    description={dashboardCopy.sectionDescription}
                    open={!adminCollapsedSections.dashboardOperational}
                    onOpenChange={(open) => setAdminSectionExpanded("dashboardOperational", open)}
                    className="border-primary/10 bg-white"
                    contentClassName="pt-1"
                    headerBadge={
                      <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                        {dashboardOperationalAlerts.reduce((total, alert) => total + alert.count, 0)} signalement(s)
                      </Badge>
                    }
                  >
                    <div className="grid gap-4 md:grid-cols-3">
                      {dashboardOperationalAlerts.map((alert) => (
                        <button
                          key={alert.key}
                          type="button"
                          onClick={alert.onClick}
                          className={cn(
                            "rounded-lg border p-4 text-left transition hover:shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/30",
                            alert.tone
                          )}
                        >
                          <div className="flex items-start justify-between gap-3">
                            <div>
                              <p className={cn("text-sm font-medium", alert.accent)}>{alert.label}</p>
                              <p className="mt-2 text-3xl font-bold">{alert.count}</p>
                            </div>
                            {alert.count > 0 ? (
                              <AlertTriangle className={cn("h-5 w-5", alert.accent)} />
                            ) : (
                              <CheckCircle className="h-5 w-5 text-emerald-600" />
                            )}
                          </div>
                          <p className="mt-3 text-xs opacity-80">
                            {alert.count > 0 ? alert.actionLabel : "Sous contrôle pour le moment"}
                          </p>
                        </button>
                      ))}
                    </div>
                  </AdminSectionCollapsible>

                  <Card>
                    <CardHeader>
                      <CardTitle>{isAccueil ? "Navigation métier" : canManageLogistics ? "Pilotage logistique" : "Modules utiles"}</CardTitle>
                      <CardDescription>
                        {isAccueil
                          ? "Les accès directs vers les écrans d’accueil que vous utilisez le plus."
                          : canManageLogistics
                            ? "Les chemins courts vers les écrans qui débloquent le matériel et les retours."
                            : "Les accès directs aux modules d’administration les plus consultés."}
                      </CardDescription>
                    </CardHeader>
                    <CardContent className="space-y-3">
                      {canManageLogistics && !isAccueil ? (
                        <>
                          <Button className="w-full justify-between" onClick={() => openAdminTab("logistique")}>
                            <span>Ouvrir le pilotage logistique</span>
                            <PackageCheck className="h-4 w-4" />
                          </Button>
                          <Button variant="outline" className="w-full justify-between" onClick={() => openAdminTab("calendrier")}>
                            <span>Voir le calendrier matériel</span>
                            <Calendar className="h-4 w-4" />
                          </Button>
                        </>
                      ) : (
                        <>
                          <Button
                            className="w-full justify-between"
                            onClick={() => {
                              setRequestTypeFilter(isAccueil ? "demande_salle" : "all");
                              openAdminTab("requests");
                            }}
                          >
                            <span>{isAccueil ? "Ouvrir les demandes de salle" : "Ouvrir les demandes"}</span>
                            <ClipboardList className="h-4 w-4" />
                          </Button>
                          <Button variant="outline" className="w-full justify-between" onClick={() => openAdminTab("calendrier")}>
                            <span>{isAccueil ? "Voir le calendrier des salles" : "Voir le calendrier"}</span>
                            <Calendar className="h-4 w-4" />
                          </Button>
                          <Button variant="outline" className="w-full justify-between" onClick={() => openAdminTab("notifications")}>
                            <span>Consulter les notifications</span>
                            <Bell className="h-4 w-4" />
                          </Button>
                          {canAccessInternalDirectory && (
                            <Button variant="outline" className="w-full justify-between" onClick={() => openAdminTab("ccds-directory")}>
                              <span>Ouvrir l’annuaire CCDS</span>
                              <Users className="h-4 w-4" />
                            </Button>
                          )}
                        </>
                      )}
                      <Button
                        variant="outline"
                        className="w-full justify-between"
                        onClick={() => {
                          setRequestStatusFilter("soumise");
                          if (isAccueil) {
                            setRequestTypeFilter("demande_salle");
                          }
                          openAdminTab("requests");
                        }}
                      >
                        <span>Traiter les demandes entrantes</span>
                        <ClipboardList className="h-4 w-4" />
                      </Button>
                      {!isAccueil && (
                        <Button variant="outline" className="w-full justify-between" onClick={() => openAdminTab("audit")}>
                          <span>Consulter le journal d'audit</span>
                          <FileText className="h-4 w-4" />
                        </Button>
                      )}
                    </CardContent>
                  </Card>
                </div>

                {!isLogisticsControl && (
                  <AdminSectionCollapsible
                    title="Facturation"
                    description="Vue rapide de toutes les locations de salle payantes pour repérer d’un coup les paiements en attente, en retard, reçus ou validés."
                    open={!adminCollapsedSections.dashboardBilling}
                    onOpenChange={(open) => setAdminSectionExpanded("dashboardBilling", open)}
                    className="border-primary/10 bg-white"
                    contentClassName="pt-1"
                    headerBadge={
                      <div className="flex flex-wrap gap-2">
                          {[
                            { key: "all", label: "Toutes", count: salleBillingCounts.all },
                            { key: "en_attente", label: "En attente", count: salleBillingCounts.en_attente },
                            { key: "en_retard", label: "En retard", count: salleBillingCounts.en_retard },
                            { key: "recues", label: "Reçues", count: salleBillingCounts.recues },
                            { key: "validees", label: "Validées", count: salleBillingCounts.validees },
                          ].map((filterOption) => (
                            <Button
                              key={filterOption.key}
                              type="button"
                              variant={billingFilter === filterOption.key ? "default" : "outline"}
                              size="sm"
                              onClick={() => setBillingFilter(filterOption.key as typeof billingFilter)}
                              className="justify-between gap-2"
                            >
                              <span>{filterOption.label}</span>
                              <Badge
                                variant="secondary"
                                className={cn(
                                  "rounded-full px-2 py-0 text-[11px]",
                                  billingFilter === filterOption.key && "bg-white/20 text-current"
                                )}
                              >
                                {filterOption.count}
                              </Badge>
                            </Button>
                          ))}
                      </div>
                    }
                  >
                      {billingRequestsLoading ? (
                        <div className="flex justify-center py-8">
                          <Loader2 className="h-6 w-6 animate-spin" />
                        </div>
                      ) : filteredSalleBillingEntries.length > 0 ? (
                        <div className="overflow-x-auto">
                          <table className="min-w-full text-sm">
                            <thead>
                              <tr className="border-b text-left text-muted-foreground">
                                <th className="px-3 py-2 font-medium">Association</th>
                                <th className="px-3 py-2 font-medium">Salle / dossier</th>
                                <th className="px-3 py-2 font-medium">Montant</th>
                                <th className="px-3 py-2 font-medium">Échéance</th>
                                <th className="px-3 py-2 font-medium">État paiement</th>
                                <th className="px-3 py-2 font-medium">Action</th>
                              </tr>
                            </thead>
                            <tbody>
                              {filteredSalleBillingEntries.map((entry) => (
                                <tr key={entry.id} className="border-b last:border-b-0">
                                  <td className="px-3 py-3 align-top">
                                    <p className="font-medium text-foreground">{entry.associationName}</p>
                                    <p className="text-xs text-muted-foreground">
                                      Demande créée le {formatDate(entry.createdAt)}
                                    </p>
                                  </td>
                                  <td className="px-3 py-3 align-top">
                                    <p className="font-medium text-foreground">{entry.titre}</p>
                                    <p className="text-xs text-muted-foreground">
                                      {entry.salles.length > 0 ? entry.salles.join(", ") : "Salle non renseignée"}
                                      {entry.dateReservation ? ` • usage le ${formatDate(entry.dateReservation)}` : ""}
                                    </p>
                                  </td>
                                  <td className="px-3 py-3 align-top">
                                    <p className="font-medium text-foreground">{formatAmount(entry.amountDueCents)}</p>
                                    <p className="text-xs text-muted-foreground">
                                      Reçu : {entry.amountReceivedCents > 0 ? formatAmount(entry.amountReceivedCents) : "-"}
                                    </p>
                                  </td>
                                  <td className="px-3 py-3 align-top">
                                    <p className={cn("font-medium", entry.isLate && "text-red-700")}>
                                      {entry.paymentDueDate ? formatDate(entry.paymentDueDate) : "-"}
                                    </p>
                                    <p className="text-xs text-muted-foreground">
                                      {entry.paymentReceivedAt ? `Reçu le ${formatDate(entry.paymentReceivedAt)}` : "Aucun paiement enregistré"}
                                    </p>
                                  </td>
                                  <td className="px-3 py-3 align-top">
                                    <div className="flex flex-wrap items-center gap-2">
                                      <Badge className={salleBillingStatusClasses[entry.paymentStatus]}>
                                        {SALLE_WORKFLOW_PAYMENT_STATUS_LABELS[entry.paymentStatus]}
                                      </Badge>
                                      {entry.isLate && (
                                        <Badge variant="destructive">Retard</Badge>
                                      )}
                                    </div>
                                  </td>
                                  <td className="px-3 py-3 align-top">
                                    <Link href={`/dashboard/requests/${entry.id}`} {...buildRoutePrefetchHandlers(`/dashboard/requests/${entry.id}`)}>
                                      <Button size="sm" variant="outline">
                                        <Eye className="mr-2 h-4 w-4" />
                                        Ouvrir le dossier
                                      </Button>
                                    </Link>
                                  </td>
                                </tr>
                              ))}
                            </tbody>
                          </table>
                        </div>
                      ) : (
                        <div className="rounded-lg border border-dashed px-6 py-10 text-center">
                          <FileText className="mx-auto h-10 w-10 text-muted-foreground/50" />
                          <p className="mt-4 font-medium">Aucune location payante dans ce filtre</p>
                          <p className="mt-1 text-sm text-muted-foreground">
                            Le tableau se remplit automatiquement dès qu’une demande de salle payante entre dans le circuit de facturation.
                          </p>
                        </div>
                      )}
                  </AdminSectionCollapsible>
                )}

                {isAccueil ? (
                  <Card className="border-primary/10">
                    <CardHeader>
                      <CardTitle className="flex items-center gap-2">
                        <CalendarClock className="h-5 w-5 text-primary" />
                        {dashboardCopy.todayFlowTitle}
                      </CardTitle>
                      <CardDescription>
                        {dashboardCopy.todayFlowDescription}
                      </CardDescription>
                    </CardHeader>
                    <CardContent className="grid gap-4 lg:grid-cols-2">
                      <div className="space-y-3 rounded-lg border bg-primary/5 p-4">
                        <div className="flex items-center justify-between">
                          <div>
                            <p className="text-sm font-medium text-primary">Devis transmis et retours association</p>
                            <p className="text-xs text-muted-foreground">Les dossiers en attente de réponse ou revenus vers l’accueil après retour de l’association.</p>
                          </div>
                          <Badge variant="secondary">{accueilWorkflowCounts.waitingForAssociation + accueilWorkflowCounts.backFromAssociation}</Badge>
                        </div>
                        <div className="space-y-2">
                          {salleWorkflowEntries
                            .filter((entry) => entry.quoteStatus === "en_attente_association" || (entry.quoteStatus === "accepte" && entry.directorStatus === "a_transmettre"))
                            .slice(0, 6)
                            .map((entry) => (
                              <div key={`accueil-flow-${entry.id}`} className="rounded-lg border bg-background p-3">
                                <div className="flex items-start justify-between gap-3">
                                  <div className="min-w-0">
                                    <p className="font-medium">{entry.associationName}</p>
                                    <p className="text-sm text-muted-foreground truncate">{entry.titre}</p>
                                    <p className="mt-1 text-xs text-muted-foreground">
                                      {entry.reservationStart ? `Usage à partir du ${formatDate(entry.reservationStart)}` : "Date non renseignée"}
                                    </p>
                                  </div>
                                  <Badge variant="outline" className={entry.quoteStatus === "en_attente_association" ? "bg-violet-100 text-violet-700 border-violet-200" : "bg-blue-100 text-blue-700 border-blue-200"}>
                                    {entry.quoteStatus === "en_attente_association" ? "Réponse attendue" : "Revenu à l’accueil"}
                                  </Badge>
                                </div>
                                <div className="mt-3 flex flex-wrap gap-2">
                                  {entry.quoteStatus === "en_attente_association" ? (
                                    <Button size="sm" variant="outline" disabled>
                                      Réponse de l’association attendue
                                    </Button>
                                  ) : (
                                    <Button
                                      size="sm"
                                      variant="outline"
                                      onClick={() => {
                                        setRequestTypeFilter("demande_salle");
                                        setActiveTab("requests");
                                      }}
                                    >
                                      Traiter
                                    </Button>
                                  )}
                                  <Button size="sm" variant="ghost" asChild>
                                    <Link href={`/dashboard/requests/${entry.id}`} {...buildRoutePrefetchHandlers(`/dashboard/requests/${entry.id}`)}>
                                      Ouvrir le dossier
                                    </Link>
                                  </Button>
                                </div>
                              </div>
                            ))}
                          {salleWorkflowEntries.filter((entry) => entry.quoteStatus === "en_attente_association" || (entry.quoteStatus === "accepte" && entry.directorStatus === "a_transmettre")).length === 0 && (
                            <div className="rounded-lg border border-dashed bg-background/80 p-4 text-sm text-muted-foreground">
                              Aucun devis ou retour association à reprendre pour le moment.
                            </div>
                          )}
                        </div>
                      </div>

                      <div className="space-y-3 rounded-lg border bg-emerald-50/60 p-4">
                        <div className="flex items-center justify-between">
                          <div>
                            <p className="text-sm font-medium text-emerald-800">Dossiers signés et réservations du jour</p>
                            <p className="text-xs text-emerald-700">Les dossiers à envoyer après signature et les salles déjà prévues aujourd’hui.</p>
                          </div>
                          <Badge variant="secondary">{accueilWorkflowCounts.signedReadyToSend + accueilWorkflowCounts.reservedToday}</Badge>
                        </div>
                        <div className="space-y-2">
                          {salleWorkflowEntries
                            .filter((entry) => (entry.directorStatus === "signee" && !entry.finalNotificationSentAt) || entry.todayInRange)
                            .slice(0, 6)
                            .map((entry) => (
                              <div key={`accueil-signed-${entry.id}`} className="rounded-lg border bg-background p-3">
                                <div className="flex items-start justify-between gap-3">
                                  <div className="min-w-0">
                                    <p className="font-medium">{entry.associationName}</p>
                                    <p className="text-sm text-muted-foreground truncate">{entry.titre}</p>
                                    <p className="mt-1 text-xs text-muted-foreground">
                                      {entry.reservationStart ? `Réservation du ${formatDate(entry.reservationStart)}` : "Date non renseignée"}
                                    </p>
                                  </div>
                                  <Badge variant="outline" className={entry.directorStatus === "signee" && !entry.finalNotificationSentAt ? "bg-emerald-100 text-emerald-700 border-emerald-200" : "bg-blue-100 text-blue-700 border-blue-200"}>
                                    {entry.directorStatus === "signee" && !entry.finalNotificationSentAt ? "Prêt à envoyer" : "Salle réservée"}
                                  </Badge>
                                </div>
                                <div className="mt-3 flex flex-wrap gap-2">
                                  {entry.directorStatus === "signee" && !entry.finalNotificationSentAt ? (
                                    <Button
                                      size="sm"
                                      variant="outline"
                                      onClick={() => {
                                        setRequestTypeFilter("demande_salle");
                                        setActiveTab("requests");
                                      }}
                                    >
                                      Ouvrir pour finaliser
                                    </Button>
                                  ) : null}
                                  <Button size="sm" variant="ghost" asChild>
                                    <Link href={`/dashboard/requests/${entry.id}`} {...buildRoutePrefetchHandlers(`/dashboard/requests/${entry.id}`)}>
                                      Ouvrir le dossier
                                    </Link>
                                  </Button>
                                </div>
                              </div>
                            ))}
                          {salleWorkflowEntries.filter((entry) => (entry.directorStatus === "signee" && !entry.finalNotificationSentAt) || entry.todayInRange).length === 0 && (
                            <div className="rounded-lg border border-dashed bg-background/80 p-4 text-sm text-muted-foreground">
                              Aucun dossier signé à expédier ni occupation de salle à surveiller aujourd’hui.
                            </div>
                          )}
                        </div>
                      </div>
                    </CardContent>
                  </Card>
                ) : (
                  <>
                    {isFullAdminView && (
                      <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
                        <Card>
                          <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
                            <CardTitle className="text-sm font-medium">Associations</CardTitle>
                            <Building2 className="h-4 w-4 text-muted-foreground" />
                          </CardHeader>
                          <CardContent>
                            <div className="text-2xl font-bold">{stats?.totalAssociations || 0}</div>
                            <p className="text-xs text-muted-foreground">
                              +{stats?.newAssociationsThisMonth || 0} ce mois
                            </p>
                          </CardContent>
                        </Card>

                        <Card>
                          <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
                            <CardTitle className="text-sm font-medium">Demandes totales</CardTitle>
                            <ClipboardList className="h-4 w-4 text-muted-foreground" />
                          </CardHeader>
                          <CardContent>
                            <div className="text-2xl font-bold">{stats?.totalRequests || 0}</div>
                            <p className="text-xs text-muted-foreground">
                              +{stats?.newRequestsThisMonth || 0} ce mois
                            </p>
                          </CardContent>
                        </Card>

                        <Card>
                          <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
                            <CardTitle className="text-sm font-medium">En attente</CardTitle>
                            <Clock className="h-4 w-4 text-yellow-500" />
                          </CardHeader>
                          <CardContent>
                            <div className="text-2xl font-bold text-yellow-600">
                              {stats?.pendingRequests || 0}
                            </div>
                            <p className="text-xs text-muted-foreground">
                              à traiter
                            </p>
                          </CardContent>
                        </Card>

                        <Card>
                          <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
                            <CardTitle className="text-sm font-medium">Taux d'acceptation</CardTitle>
                            <TrendingUp className="h-4 w-4 text-green-500" />
                          </CardHeader>
                          <CardContent>
                            <div className="text-2xl font-bold text-green-600">
                              {stats?.acceptanceRate || 0}%
                            </div>
                            <p className="text-xs text-muted-foreground">
                              {stats?.validatedRequests || 0} validées
                            </p>
                          </CardContent>
                        </Card>
                      </div>
                    )}

                    <div className="grid gap-6 xl:grid-cols-[1.25fr_0.75fr]">
                      <Card className="border-primary/10">
                        <CardHeader>
                          <CardTitle className="flex items-center gap-2">
                            <CalendarClock className="h-5 w-5 text-primary" />
                            {dashboardCopy.todayFlowTitle}
                          </CardTitle>
                          <CardDescription>
                            {dashboardCopy.todayFlowDescription}
                          </CardDescription>
                        </CardHeader>
                        <CardContent className="grid gap-4 lg:grid-cols-2">
                      <div className="space-y-3 rounded-lg border bg-blue-50/60 p-4">
                        <div className="flex items-center justify-between">
                          <div>
                            <p className="text-sm font-medium text-blue-800">Matin - Départs</p>
                            <p className="text-xs text-blue-700">Préparation des mises à disposition et livraisons du jour.</p>
                          </div>
                          <Badge variant="secondary">{dailyOperations.departures.length}</Badge>
                        </div>
                        <div className="space-y-2">
                          {dailyOperations.departures.length > 0 ? dailyOperations.departures.map((entry) => (
                            <div key={`departure-${entry.requestId}`} className="rounded-lg border bg-background/90 p-3">
                              <div className="flex items-start justify-between gap-3">
                                <div className="min-w-0">
                                  <p className="font-medium">{entry.associationName}</p>
                                  <p className="text-sm text-muted-foreground truncate">{entry.title}</p>
                                  <p className="mt-1 text-xs text-muted-foreground">
                                    {formatTime(entry.pickupDate)} · {entry.commune || "Commune non renseignée"}
                                  </p>
                                </div>
                                <Badge variant={entry.boardState === "a_attribuer" ? "destructive" : "secondary"}>
                                  {entry.boardState === "a_attribuer" ? "Non assigné" : "Assigné"}
                                </Badge>
                              </div>
                              <div className="mt-3 flex flex-wrap gap-2">
                                {entry.boardState !== "conforme" ? (
                                  <Button
                                    size="sm"
                                    variant={entry.boardState === "a_attribuer" ? "default" : "outline"}
                                    onClick={() => openLogisticsAssignmentDialog(entry)}
                                  >
                                    {entry.boardState === "a_attribuer" ? "Assigner" : "Réassigner et relancer"}
                                  </Button>
                                ) : null}
                                <Button size="sm" variant="ghost" asChild>
                                  <Link href={`/dashboard/requests/${entry.requestId}`} {...buildRoutePrefetchHandlers(`/dashboard/requests/${entry.requestId}`)}>
                                    Voir le dossier
                                  </Link>
                                </Button>
                              </div>
                            </div>
                          )) : (
                            <div className="rounded-lg border border-dashed bg-background/80 p-4 text-sm text-muted-foreground">
                              Aucun départ prévu aujourd’hui.
                            </div>
                          )}
                        </div>
                      </div>

                      <div className="space-y-3 rounded-lg border bg-emerald-50/60 p-4">
                        <div className="flex items-center justify-between">
                          <div>
                            <p className="text-sm font-medium text-emerald-800">Après-midi - Retours</p>
                            <p className="text-xs text-emerald-700">Restitutions à suivre en direct côté agents et terrain.</p>
                          </div>
                          <Badge variant="secondary">{dailyOperations.returns.length}</Badge>
                        </div>
                        <div className="space-y-2">
                          {dailyOperations.returns.length > 0 ? dailyOperations.returns.map((entry) => (
                            <div key={`return-${entry.requestId}`} className="rounded-lg border bg-background/90 p-3">
                              <div className="flex items-start justify-between gap-3">
                                <div className="min-w-0">
                                  <p className="font-medium">{entry.associationName}</p>
                                  <p className="text-sm text-muted-foreground truncate">{entry.title}</p>
                                  <p className="mt-1 text-xs text-muted-foreground">
                                    {formatTime(entry.restitutionDate)} · {entry.serviceLabel || "Service non affecté"}
                                  </p>
                                </div>
                                <Badge variant="outline" className={materialReturnBoardConfig[entry.boardState]?.className}>
                                  {materialReturnBoardConfig[entry.boardState]?.label || entry.boardStateLabel}
                                </Badge>
                              </div>
                              <div className="mt-3 flex flex-wrap gap-2">
                                {entry.boardState === "a_attribuer" ? (
                                  <Button size="sm" onClick={() => openLogisticsAssignmentDialog(entry)}>
                                    Assigner
                                  </Button>
                                ) : entry.boardState !== "conforme" ? (
                                  <Button size="sm" variant="outline" onClick={() => openLogisticsAssignmentDialog(entry)}>
                                    Réassigner et relancer
                                  </Button>
                                ) : null}
                                {entry.boardState !== "conforme" && entry.boardState !== "litige" ? (
                                  <Button
                                    size="sm"
                                    variant={entry.boardState === "retard" ? "default" : "outline"}
                                    onClick={() =>
                                      handleReactivateMaterialReturn(
                                        entry,
                                        entry.boardState === "retard"
                                          ? "Suivi rouvert et lien de restitution renvoyé"
                                          : "Lien de restitution renvoyé"
                                      )
                                    }
                                    disabled={reactivateMaterialReturnMutation.isPending}
                                  >
                                    {entry.boardState === "retard" ? (
                                      <RotateCcw className="mr-1.5 h-3.5 w-3.5" />
                                    ) : (
                                      <Send className="mr-1.5 h-3.5 w-3.5" />
                                    )}
                                    {entry.boardState === "retard" ? "Rouvrir et renvoyer le lien" : "Renvoyer le lien actif"}
                                  </Button>
                                ) : null}
                                <Button size="sm" variant="ghost" asChild>
                                  <Link href={`/dashboard/requests/${entry.requestId}`} {...buildRoutePrefetchHandlers(`/dashboard/requests/${entry.requestId}`)}>
                                    Ouvrir le dossier
                                  </Link>
                                </Button>
                              </div>
                            </div>
                          )) : (
                            <div className="rounded-lg border border-dashed bg-background/80 p-4 text-sm text-muted-foreground">
                              Aucun retour attendu aujourd’hui.
                            </div>
                          )}
                        </div>
                      </div>
                        </CardContent>
                      </Card>

                      <Card>
                    <CardHeader>
                      <CardTitle className="flex items-center gap-2">
                        <Calendar className="h-5 w-5 text-primary" />
                        Mini-calendrier de tension
                      </CardTitle>
                      <CardDescription>
                        Les jours à surveiller pour le stock, les conflits et les missions matérielles.
                      </CardDescription>
                    </CardHeader>
                    <CardContent className="space-y-4">
                      <div className="grid grid-cols-7 gap-1 text-center text-xs font-medium text-muted-foreground">
                        {["L", "M", "M", "J", "V", "S", "D"].map((day) => (
                          <span key={day}>{day}</span>
                        ))}
                      </div>
                      <div className="grid grid-cols-7 gap-1">
                        {miniCalendarTensionDays.map((cell) => (
                          <button
                            key={cell.key}
                            type="button"
                            disabled={!cell.iso}
                            onClick={() => setActiveTab("calendrier")}
                            className={cn(
                              "flex aspect-square items-center justify-center rounded-md border text-sm transition",
                              !cell.iso && "border-transparent bg-transparent",
                              cell.iso && "bg-background hover:border-primary/40 hover:bg-primary/5",
                              cell.isToday && "border-primary font-semibold text-primary",
                              cell.tension && "border-red-200 bg-red-50 text-red-800",
                              cell.litige && "border-orange-200 bg-orange-50 text-orange-800"
                            )}
                            title={cell.iso ? `${cell.load} mission(s) · ${cell.tension ? "tension stock" : "charge normale"}` : ""}
                          >
                            {cell.day || ""}
                          </button>
                        ))}
                      </div>
                      <div className="space-y-2 text-xs text-muted-foreground">
                        <div className="flex items-center gap-2">
                          <span className="h-3 w-3 rounded-full bg-red-200" />
                          <span>Jour sous tension stock ou conflit de réservation</span>
                        </div>
                        <div className="flex items-center gap-2">
                          <span className="h-3 w-3 rounded-full bg-orange-200" />
                          <span>Jour avec litige ou dégradation encore ouverte</span>
                        </div>
                      </div>
                      <Button variant="outline" className="w-full justify-between" onClick={() => setActiveTab("calendrier")}>
                        <span>Basculer vers le calendrier complet</span>
                        <ArrowUpRight className="h-4 w-4" />
                      </Button>
                    </CardContent>
                      </Card>
                    </div>

                    <div className="grid gap-6 xl:grid-cols-[0.95fr_1.05fr]">
                      <Suspense fallback={<AdminPanelLoader compact />}>
                        <AdminDashboardCharts
                          usageByCommuneData={usageByCommuneData}
                          monthlyChartData={monthlyChartData}
                          typeChartData={typeChartData}
                          isFullAdminView={isFullAdminView}
                        />
                      </Suspense>

                  <Card>
                    <CardHeader>
                      <CardTitle className="flex items-center gap-2">
                        <Shield className="h-5 w-5 text-primary" />
                        Boîte noire récente
                      </CardTitle>
                      <CardDescription>
                        Les cinq dernières actions sensibles pour garder le fil de la journée.
                      </CardDescription>
                    </CardHeader>
                    <CardContent className="space-y-4">
                      {dashboardRecentAuditLogs.length > 0 ? dashboardRecentAuditLogs.map((log, index) => (
                        <div key={log.id} className="flex gap-3">
                          <div className="flex flex-col items-center">
                            <span className="mt-1 h-2.5 w-2.5 rounded-full bg-primary" />
                            {index < dashboardRecentAuditLogs.length - 1 && (
                              <span className="mt-1 h-full w-px bg-border" />
                            )}
                          </div>
                          <div className="pb-4">
                            <p className="text-sm font-medium">
                              Utilisateur #{log.userId} · {formatAuditActionLabel(log.action)}
                            </p>
                            <p className="text-sm text-muted-foreground">
                              {`${log.entityType || "élément"}${log.entityId ? ` #${log.entityId}` : ""}`}
                            </p>
                            <p className="mt-1 text-xs text-muted-foreground">
                              {formatDateTime(log.createdAt)}
                              {log.ipAddress ? ` · IP ${log.ipAddress}` : ""}
                            </p>
                          </div>
                        </div>
                      )) : (
                        <div className="rounded-lg border border-dashed p-4 text-sm text-muted-foreground">
                          Aucune action récente à afficher.
                        </div>
                      )}
                      <Button variant="outline" className="w-full justify-between" onClick={() => setActiveTab("audit")}>
                        <span>Ouvrir le journal d'audit complet</span>
                        <ArrowUpRight className="h-4 w-4" />
                      </Button>
                    </CardContent>
                  </Card>
                    </div>

                    {isFullAdminView && (
                      <>
                        <div className="grid gap-4 md:grid-cols-3">
                  <Card>
                    <CardHeader className="pb-2">
                      <CardTitle className="text-sm font-medium">Délai moyen de traitement</CardTitle>
                    </CardHeader>
                    <CardContent>
                      <div className="text-2xl font-bold">
                        {avgProcessingTime ? `${Math.round(avgProcessingTime as number)} jours` : '-'}
                      </div>
                    </CardContent>
                  </Card>
                  <Card>
                    <CardHeader className="pb-2">
                      <CardTitle className="text-sm font-medium">Demandes cette semaine</CardTitle>
                    </CardHeader>
                    <CardContent>
                      <div className="text-2xl font-bold">{stats?.requestsThisWeek || 0}</div>
                    </CardContent>
                  </Card>
                  <Card>
                    <CardHeader className="pb-2">
                      <CardTitle className="text-sm font-medium">Associations actives</CardTitle>
                    </CardHeader>
                    <CardContent>
                      <div className="text-2xl font-bold">{stats?.activeAssociations || 0}</div>
                    </CardContent>
                  </Card>
                        </div>
                      </>
                    )}
                  </>
                )}
              </>
            )}
          </TabsContent>

          {canAccessAnalytics && (
            <TabsContent value="analytics" className="space-y-6">
              {analyticsLoading ? (
                <div className="flex items-center justify-center py-12">
                  <Loader2 className="h-8 w-8 animate-spin text-primary" />
                </div>
              ) : (
                <div className="space-y-6">
                  <AdminSurface
                    title="Statistiques d’activité"
                    description="Vue de pilotage pour suivre l’usage des salles, du matériel CCDS et des associations utilisatrices sans retraitement manuel."
                    badge={
                      <Badge variant="outline" className="border-blue-200 bg-blue-50 text-blue-700">
                        {analyticsPeriod === "7d"
                          ? "Lecture hebdomadaire"
                          : analyticsPeriod === "month"
                            ? "Lecture mensuelle"
                            : analyticsPeriod === "quarter"
                              ? "Lecture trimestrielle"
                              : analyticsPeriod === "semester"
                                ? "Lecture semestrielle"
                                : "Lecture annuelle"}
                      </Badge>
                    }
                    className="border-primary/15 bg-primary/[0.03]"
                  >
                    <div className="space-y-5">
                      <div className="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
                        <div className="rounded-2xl border border-slate-200 bg-slate-50/70 p-2">
                          <div className="flex flex-wrap gap-2">
                            {[
                              { key: "7d", label: "Hebdo" },
                              { key: "month", label: "Mensuel" },
                              { key: "quarter", label: "Trimestriel" },
                              { key: "semester", label: "Semestriel" },
                              { key: "year", label: "Annuel" },
                            ].map((option) => (
                              <Button
                                key={option.key}
                                type="button"
                                size="sm"
                                variant={analyticsPeriod === option.key ? "default" : "ghost"}
                                className="rounded-full"
                                onClick={() => setAnalyticsPeriod(option.key as typeof analyticsPeriod)}
                              >
                                {option.label}
                              </Button>
                            ))}
                          </div>
                        </div>
                        <AdminActionButtonGroup className="xl:justify-end">
                          <Button type="button" size="sm" variant="outline" onClick={exportAnalyticsCsv} disabled={!analyticsData}>
                            <Download className="mr-2 h-4 w-4" />
                            Export CSV
                          </Button>
                          <Button type="button" size="sm" variant="outline" onClick={exportAnalyticsExcel} disabled={!analyticsData}>
                            <Download className="mr-2 h-4 w-4" />
                            Export Excel
                          </Button>
                          <Button type="button" size="sm" variant="outline" onClick={openAnalyticsPrintableReport} disabled={!analyticsData}>
                            <Eye className="mr-2 h-4 w-4" />
                            Aperçu PDF imprimable
                          </Button>
                        </AdminActionButtonGroup>
                      </div>

                      <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                        <AdminMetricTile
                          label="Réservations sur la période"
                          value={analyticsData?.summary.totalReservations || 0}
                          tone="info"
                          detail={`${analyticsData?.comparison.totalReservationsPct || 0}% vs ${analyticsData?.comparisonLabel?.toLowerCase() || "période précédente"}`}
                        />
                        <AdminMetricTile
                          label="Réservations de salles"
                          value={analyticsData?.summary.salleReservations || 0}
                          tone="success"
                          detail={`Taux d’occupation global : ${analyticsData?.summary.occupancyRate || 0}%`}
                        />
                        <AdminMetricTile
                          label="Réservations de matériel"
                          value={analyticsData?.summary.materialReservations || 0}
                          tone="warning"
                          detail={`${analyticsData?.summary.totalMaterialUnits || 0} unité(s) demandée(s)`}
                        />
                        <AdminMetricTile
                          label="Associations utilisatrices"
                          value={analyticsData?.summary.activeAssociations || 0}
                          detail={`Fréquence moyenne : ${analyticsData?.summary.averageReservationsPerAssociation || 0}`}
                        />
                      </div>
                    </div>
                  </AdminSurface>

                  <Card>
                    <CardHeader>
                      <CardTitle className="flex items-center gap-2">
                        <Clock className="h-5 w-5 text-primary" />
                        Automatisation des rapports
                      </CardTitle>
                      <CardDescription>
                        Génération périodique des bilans d’activité, archivage des exports et diffusion interne sans retraitement manuel.
                      </CardDescription>
                    </CardHeader>
                    <CardContent className="space-y-6">
                      <div className="grid gap-4 lg:grid-cols-[1fr_1fr]">
                        <div className="space-y-4 rounded-lg border p-4">
                          <div className="flex items-center justify-between gap-3">
                            <div>
                              <p className="font-medium">Planification active</p>
                              <p className="text-sm text-muted-foreground">
                                Le scheduler vérifie les échéances et génère les rapports terminés automatiquement.
                              </p>
                            </div>
                            <Switch
                              checked={statsAutomationForm.enabled}
                              onCheckedChange={(checked) => setStatsAutomationForm((prev) => ({ ...prev, enabled: checked }))}
                              disabled={!(user?.role === "admin" || user?.role === "super_admin")}
                            />
                          </div>
                          <div className="space-y-3">
                            <p className="text-sm font-medium">Fréquences</p>
                            <div className="grid gap-2 sm:grid-cols-2">
                              {[
                                { key: "weekly", label: "Hebdomadaire" },
                                { key: "month", label: "Mensuel" },
                                { key: "quarter", label: "Trimestriel" },
                                { key: "semester", label: "Semestriel" },
                                { key: "year", label: "Annuel" },
                              ].map((item) => (
                                <label key={item.key} className="flex items-center gap-3 rounded-md border px-3 py-2 text-sm">
                                  <Checkbox
                                    checked={statsAutomationForm.frequencies[item.key as keyof typeof statsAutomationForm.frequencies]}
                                    onCheckedChange={(checked) =>
                                      setStatsAutomationForm((prev) => ({
                                        ...prev,
                                        frequencies: {
                                          ...prev.frequencies,
                                          [item.key]: Boolean(checked),
                                        },
                                      }))
                                    }
                                    disabled={!(user?.role === "admin" || user?.role === "super_admin")}
                                  />
                                  <span>{item.label}</span>
                                </label>
                              ))}
                            </div>
                          </div>
                          <div className="space-y-2">
                            <Label>Destinataires mail des rapports</Label>
                            <Input
                              value={statsAutomationForm.recipientEmails}
                              onChange={(e) => setStatsAutomationForm((prev) => ({ ...prev, recipientEmails: e.target.value }))}
                              placeholder="adresse1@ccds-guyane.fr, adresse2@ccds-guyane.fr"
                              disabled={!(user?.role === "admin" || user?.role === "super_admin")}
                            />
                            <p className="text-xs text-muted-foreground">
                              Optionnel. Si vide, les rapports restent archivés dans le portail et une notification interne est créée.
                            </p>
                          </div>
                          {(user?.role === "admin" || user?.role === "super_admin") && (
                            <div className="flex flex-wrap gap-2">
                              <Button
                                type="button"
                                onClick={saveStatsAutomation}
                                disabled={updateStatsAutomationMutation.isPending}
                              >
                                {updateStatsAutomationMutation.isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
                                Enregistrer l’automatisation
                              </Button>
                            </div>
                          )}
                        </div>

                        <div className="space-y-4 rounded-lg border p-4">
                          <div>
                            <p className="font-medium">Génération manuelle immédiate</p>
                            <p className="text-sm text-muted-foreground">
                              Permet de forcer un rapport sans attendre la prochaine échéance automatique.
                            </p>
                          </div>
                          <div className="flex flex-wrap gap-2">
                            {[
                              { key: "weekly", label: "Hebdo" },
                              { key: "month", label: "Mensuel" },
                              { key: "quarter", label: "Trimestriel" },
                              { key: "semester", label: "Semestriel" },
                              { key: "year", label: "Annuel" },
                            ].map((item) => (
                              <Button
                                key={item.key}
                                type="button"
                                variant="outline"
                                size="sm"
                                onClick={() => generateStatsReportNowMutation.mutate({ period: item.key as "weekly" | "month" | "quarter" | "semester" | "year" })}
                                disabled={!(user?.role === "admin" || user?.role === "super_admin") || generateStatsReportNowMutation.isPending}
                              >
                                {generateStatsReportNowMutation.isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
                                {item.label}
                              </Button>
                            ))}
                          </div>
                          <div className="rounded-lg border border-dashed bg-muted/30 px-4 py-3 text-sm text-muted-foreground">
                            Les rapports automatiques sont générés sur période terminée. Les rapports manuels reprennent l’état courant du portail au moment du clic.
                          </div>
                        </div>
                      </div>

                      <div className="space-y-3">
                        <div>
                          <h3 className="font-medium">Historique des rapports générés</h3>
                          <p className="text-sm text-muted-foreground">
                            Les derniers exports PDF et Excel produits automatiquement ou manuellement.
                          </p>
                        </div>
                        {(statsReportHistory || []).length > 0 ? (
                          <div className="overflow-x-auto">
                            <table className="min-w-full text-sm">
                              <thead>
                                <tr className="border-b text-left text-muted-foreground">
                                  <th className="px-3 py-2 font-medium">Période</th>
                                  <th className="px-3 py-2 font-medium">Couverture</th>
                                  <th className="px-3 py-2 font-medium">Généré le</th>
                                  <th className="px-3 py-2 font-medium">Mode</th>
                                  <th className="px-3 py-2 font-medium">Fichiers</th>
                                </tr>
                              </thead>
                              <tbody>
                                {(statsReportHistory || []).map((entry) => (
                                  <tr key={entry.id} className="border-b last:border-b-0">
                                    <td className="px-3 py-3 font-medium">{entry.periodLabel}</td>
                                    <td className="px-3 py-3">{formatDate(entry.startDate)} au {formatDate(entry.endDate)}</td>
                                    <td className="px-3 py-3">{formatDateTime(entry.generatedAt)}</td>
                                    <td className="px-3 py-3">
                                      <Badge variant="outline">{entry.generatedBy === "manual" ? "Manuel" : "Automatique"}</Badge>
                                    </td>
                                    <td className="px-3 py-3">
                                      <div className="flex flex-wrap gap-2">
                                        <Link href={buildArchivedReportViewHref(entry)} {...buildRoutePrefetchHandlers(buildArchivedReportViewHref(entry))}>
                                          <Button
                                            size="sm"
                                            variant="outline"
                                            type="button"
                                          >
                                            <Eye className="mr-2 h-4 w-4" />
                                            Voir le rapport
                                          </Button>
                                        </Link>
                                        <Button
                                          size="sm"
                                          variant="outline"
                                          type="button"
                                          onClick={() => openArchivedPrintableReport(entry.pdfUrl, true)}
                                        >
                                          <Printer className="mr-2 h-4 w-4" />
                                          Imprimer
                                        </Button>
                                        <a href={entry.excelUrl} target="_blank" rel="noreferrer">
                                          <Button size="sm" variant="outline">
                                            <Download className="mr-2 h-4 w-4" />
                                            Télécharger Excel
                                          </Button>
                                        </a>
                                      </div>
                                    </td>
                                  </tr>
                                ))}
                              </tbody>
                            </table>
                          </div>
                        ) : (
                          <div className="rounded-lg border border-dashed px-6 py-8 text-center text-sm text-muted-foreground">
                            Aucun rapport automatique archivé pour le moment.
                          </div>
                        )}
                      </div>
                    </CardContent>
                  </Card>

                  <Suspense fallback={<AdminPanelLoader compact />}>
                    <AdminAnalyticsCharts
                      periodLabel={analyticsData?.periodLabel}
                      timelineData={analyticsTimelineData}
                      categoryData={analyticsCategoryData}
                      roomOccupancyData={analyticsRoomOccupancyData}
                      materialUsageData={analyticsMaterialUsageData}
                    />
                  </Suspense>

                  <Card>
                    <CardHeader className="gap-4">
                      <div className="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
                        <div>
                          <CardTitle>Associations et structures utilisatrices</CardTitle>
                          <CardDescription>
                            Repère les associations les plus actives et leur fréquence d’utilisation.
                          </CardDescription>
                        </div>
                        <div className="w-full lg:max-w-sm">
                          <Input
                            placeholder="Rechercher une association ou une catégorie"
                            value={analyticsAssociationSearch}
                            onChange={(e) => setAnalyticsAssociationSearch(e.target.value)}
                          />
                        </div>
                      </div>
                    </CardHeader>
                    <CardContent>
                      {filteredAnalyticsAssociationUsage.length > 0 ? (
                        <div className="overflow-x-auto">
                          <table className="min-w-full text-sm">
                            <thead>
                              <tr className="border-b text-left text-muted-foreground">
                                <th className="px-3 py-2 font-medium">Association</th>
                                <th className="px-3 py-2 font-medium">Catégorie</th>
                                <th className="px-3 py-2 font-medium">Total</th>
                                <th className="px-3 py-2 font-medium">Salles</th>
                                <th className="px-3 py-2 font-medium">Matériel</th>
                                <th className="px-3 py-2 font-medium">Dernière réservation</th>
                              </tr>
                            </thead>
                            <tbody>
                              {filteredAnalyticsAssociationUsage.map((entry) => (
                                <tr key={entry.associationId} className="border-b last:border-b-0">
                                  <td className="px-3 py-3 font-medium">{entry.associationName}</td>
                                  <td className="px-3 py-3 text-muted-foreground">{entry.thematics.join(", ")}</td>
                                  <td className="px-3 py-3">{entry.totalReservations}</td>
                                  <td className="px-3 py-3">{entry.salleReservations}</td>
                                  <td className="px-3 py-3">{entry.materialReservations}</td>
                                  <td className="px-3 py-3">{formatDate(entry.lastReservationAt)}</td>
                                </tr>
                              ))}
                            </tbody>
                          </table>
                        </div>
                      ) : (
                        <div className="rounded-lg border border-dashed px-6 py-10 text-center text-sm text-muted-foreground">
                          Aucune structure ne correspond à cette recherche.
                        </div>
                      )}
                    </CardContent>
                  </Card>

                  <Card>
                    <CardHeader>
                      <CardTitle>Historique des réservations</CardTitle>
                      <CardDescription>
                        Dernières réservations prises en compte dans le rapport courant.
                      </CardDescription>
                    </CardHeader>
                    <CardContent>
                      {(analyticsData?.history || []).length > 0 ? (
                        <div className="overflow-x-auto">
                          <table className="min-w-full text-sm">
                            <thead>
                              <tr className="border-b text-left text-muted-foreground">
                                <th className="px-3 py-2 font-medium">Type</th>
                                <th className="px-3 py-2 font-medium">Association</th>
                                <th className="px-3 py-2 font-medium">Dossier</th>
                                <th className="px-3 py-2 font-medium">Période</th>
                                <th className="px-3 py-2 font-medium">Ressources</th>
                                <th className="px-3 py-2 font-medium">Statut</th>
                              </tr>
                            </thead>
                            <tbody>
                              {(analyticsData?.history || []).map((entry) => (
                                <tr key={`${entry.type}-${entry.id}`} className="border-b last:border-b-0">
                                  <td className="px-3 py-3">{requestTypeLabels[entry.type] || entry.type}</td>
                                  <td className="px-3 py-3">{entry.associationName}</td>
                                  <td className="px-3 py-3">
                                    <div className="space-y-1">
                                      <p className="font-medium">{entry.title}</p>
                                      <p className="text-xs text-muted-foreground">Créé le {formatDate(entry.createdAt)}</p>
                                    </div>
                                  </td>
                                  <td className="px-3 py-3">
                                    {formatDate(entry.startDate)}{entry.endDate && entry.endDate !== entry.startDate ? ` au ${formatDate(entry.endDate)}` : ""}
                                  </td>
                                  <td className="px-3 py-3 text-muted-foreground">{entry.resources || "-"}</td>
                                  <td className="px-3 py-3">
                                    <Badge variant="outline">{statusConfig[entry.status]?.label || entry.status}</Badge>
                                  </td>
                                </tr>
                              ))}
                            </tbody>
                          </table>
                        </div>
                      ) : (
                        <div className="rounded-lg border border-dashed px-6 py-10 text-center text-sm text-muted-foreground">
                          Aucun historique à afficher sur cette période.
                        </div>
                      )}
                    </CardContent>
                  </Card>

                  <Card className="border-primary/10">
                    <CardHeader className="gap-4">
                      <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
                        <div>
                          <CardTitle className="flex items-center gap-2">
                            <Monitor className="h-5 w-5 text-primary" />
                            Double Tour opérationnel du site
                          </CardTitle>
                          <CardDescription>
                            Un tour technique automatisé et un tour utilisateur en lecture seule pour vérifier la stabilité, la cohérence et l’expérience réelle du portail.
                          </CardDescription>
                        </div>
                        <div className="flex flex-wrap gap-2">
                          <Select value={operationalTourEnvironment} onValueChange={(value) => setOperationalTourEnvironment(value as OperationalTourEnvironment)}>
                            <SelectTrigger className="w-[220px]">
                              <SelectValue placeholder="Choisir l’environnement" />
                            </SelectTrigger>
                            <SelectContent>
                              <SelectItem value="production">Production</SelectItem>
                              <SelectItem value="secondary">Préproduction / Local</SelectItem>
                            </SelectContent>
                          </Select>
                          <Button
                            type="button"
                            onClick={() => runOperationalTourMutation.mutate({ environment: operationalTourEnvironment })}
                            disabled={runOperationalTourMutation.isPending}
                          >
                            {runOperationalTourMutation.isPending ? (
                              <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                            ) : (
                              <Shield className="mr-2 h-4 w-4" />
                            )}
                            Lancer le contrôle
                          </Button>
                          {selectedOperationalTourReport ? (
                            <Button type="button" variant="outline" onClick={() => exportOperationalTourReport(selectedOperationalTourReport)}>
                              <Download className="mr-2 h-4 w-4" />
                              Export JSON
                            </Button>
                          ) : null}
                        </div>
                      </div>
                    </CardHeader>
                    <CardContent className="space-y-6">
                      {(user?.role === "admin" || user?.role === "super_admin") && (
                        <div className="rounded-lg border bg-muted/20 p-4">
                          <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
                            <div className="space-y-1">
                              <p className="font-medium">Tour guide utilisateur visible sur le site</p>
                              <p className="text-sm text-muted-foreground">
                                Active ou coupe a tout moment les bulles d’aide, les surlignages et le guidage pas a pas pour les associations, l’accueil et l’administration.
                              </p>
                            </div>
                            <div className="flex items-center gap-3">
                              <Badge
                                variant="outline"
                                className={userOperatingTourSettings?.enabled ? "border-emerald-200 bg-emerald-50 text-emerald-700" : "border-slate-200 bg-slate-50 text-slate-700"}
                              >
                                {userOperatingTourSettings?.enabled ? "Actif" : "Desactive"}
                              </Badge>
                              {user?.role === "super_admin" ? (
                                <div className="flex items-center gap-3">
                                  <Switch
                                    checked={Boolean(userOperatingTourSettings?.enabled)}
                                    onCheckedChange={(checked) => setUserOperatingTourMutation.mutate({ enabled: checked })}
                                    disabled={setUserOperatingTourMutation.isPending}
                                  />
                                  <span className="text-sm font-medium">
                                    {userOperatingTourSettings?.enabled ? "Guide visible" : "Guide coupe"}
                                  </span>
                                </div>
                              ) : null}
                            </div>
                          </div>
                        </div>
                      )}
                      {user?.role === "super_admin" && (
                        <div className="rounded-lg border p-4 space-y-4">
                          <div className="flex items-center justify-between gap-3">
                            <div>
                              <p className="font-medium">Configuration des environnements</p>
                              <p className="text-sm text-muted-foreground">
                                Les comptes enregistrés servent uniquement au contrôle automatisé et restent en lecture seule. Le double tour couvre les volets association, accueil / hôtesse et administration. Pour un compte Google ou Facebook, laissez le mot de passe vide : le tour ouvrira directement la session du compte déjà lié.
                              </p>
                            </div>
                            <Button
                              type="button"
                              variant="outline"
                              onClick={() => saveOperationalTourSettingsMutation.mutate(operationalTourSettingsForm)}
                              disabled={saveOperationalTourSettingsMutation.isPending}
                            >
                              {saveOperationalTourSettingsMutation.isPending ? (
                                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                              ) : (
                                <Save className="mr-2 h-4 w-4" />
                              )}
                              Enregistrer la configuration
                            </Button>
                          </div>
                          <div className="grid gap-4 xl:grid-cols-2">
                            {([
                              { key: "production", label: "Production" },
                              { key: "secondary", label: "Préproduction / Local" },
                            ] as const).map((target) => (
                              <div key={target.key} className="rounded-lg border bg-muted/20 p-4 space-y-3">
                                <div className="flex items-center justify-between gap-3">
                                  <p className="font-medium">{target.label}</p>
                                  <Button
                                    type="button"
                                    size="sm"
                                    variant={operationalTourSettingsForm.defaultEnvironment === target.key ? "default" : "outline"}
                                    onClick={() => setOperationalTourSettingsForm((prev) => ({ ...prev, defaultEnvironment: target.key }))}
                                  >
                                    Environnement par défaut
                                  </Button>
                                </div>
                                <div className="space-y-2">
                                  <Label>URL du site</Label>
                                  <Input
                                    value={operationalTourSettingsForm.targets[target.key].baseUrl}
                                    onChange={(e) => setOperationalTourSettingsForm((prev) => ({
                                      ...prev,
                                      targets: {
                                        ...prev.targets,
                                        [target.key]: { ...prev.targets[target.key], baseUrl: e.target.value },
                                      },
                                    }))}
                                    placeholder={target.key === "production" ? "https://www.portail-association973.com" : "https://preprod.example.fr"}
                                  />
                                </div>
                                <div className="grid gap-3 md:grid-cols-2">
                                  <div className="space-y-2">
                                    <Label>Compte association</Label>
                                    <Input
                                      value={operationalTourSettingsForm.targets[target.key].associationEmail}
                                      onChange={(e) => setOperationalTourSettingsForm((prev) => ({
                                        ...prev,
                                        targets: {
                                          ...prev.targets,
                                          [target.key]: { ...prev.targets[target.key], associationEmail: e.target.value },
                                        },
                                      }))}
                                      placeholder="association@test.fr"
                                    />
                                  </div>
                                  <div className="space-y-2">
                                    <Label>Type de connexion association</Label>
                                    <Select
                                      value={operationalTourSettingsForm.targets[target.key].associationAuthMode}
                                      onValueChange={(value) => setOperationalTourSettingsForm((prev) => ({
                                        ...prev,
                                        targets: {
                                          ...prev.targets,
                                          [target.key]: {
                                            ...prev.targets[target.key],
                                            associationAuthMode: value as OperationalTourAuthMode,
                                            associationPassword: value === "oauth" ? "" : prev.targets[target.key].associationPassword,
                                          },
                                        },
                                      }))}
                                    >
                                      <SelectTrigger>
                                        <SelectValue placeholder="Choisir le type de connexion" />
                                      </SelectTrigger>
                                      <SelectContent>
                                        <SelectItem value="local">Compte local avec mot de passe</SelectItem>
                                        <SelectItem value="oauth">Compte Google / Facebook</SelectItem>
                                      </SelectContent>
                                    </Select>
                                  </div>
                                  <div className="space-y-2">
                                    <Label>Mot de passe association</Label>
                                    <Input
                                      type="password"
                                      value={operationalTourSettingsForm.targets[target.key].associationPassword}
                                      onChange={(e) => setOperationalTourSettingsForm((prev) => ({
                                        ...prev,
                                        targets: {
                                          ...prev.targets,
                                          [target.key]: { ...prev.targets[target.key], associationPassword: e.target.value },
                                        },
                                      }))}
                                      disabled={operationalTourSettingsForm.targets[target.key].associationAuthMode === "oauth"}
                                      placeholder={
                                        operationalTourSettingsForm.targets[target.key].associationAuthMode === "oauth"
                                          ? "Non utilisé pour un compte Google/Facebook"
                                          : "Laisser vide pour conserver"
                                      }
                                    />
                                  </div>
                                  <div className="space-y-2">
                                    <Label>Compte accueil / hôtesse</Label>
                                    <Input
                                      value={operationalTourSettingsForm.targets[target.key].accueilEmail}
                                      onChange={(e) => setOperationalTourSettingsForm((prev) => ({
                                        ...prev,
                                        targets: {
                                          ...prev.targets,
                                          [target.key]: { ...prev.targets[target.key], accueilEmail: e.target.value },
                                        },
                                      }))}
                                      placeholder="accueil@test.fr"
                                    />
                                  </div>
                                  <div className="space-y-2">
                                    <Label>Type de connexion accueil</Label>
                                    <Select
                                      value={operationalTourSettingsForm.targets[target.key].accueilAuthMode}
                                      onValueChange={(value) => setOperationalTourSettingsForm((prev) => ({
                                        ...prev,
                                        targets: {
                                          ...prev.targets,
                                          [target.key]: {
                                            ...prev.targets[target.key],
                                            accueilAuthMode: value as OperationalTourAuthMode,
                                            accueilPassword: value === "oauth" ? "" : prev.targets[target.key].accueilPassword,
                                          },
                                        },
                                      }))}
                                    >
                                      <SelectTrigger>
                                        <SelectValue placeholder="Choisir le type de connexion" />
                                      </SelectTrigger>
                                      <SelectContent>
                                        <SelectItem value="local">Compte local avec mot de passe</SelectItem>
                                        <SelectItem value="oauth">Compte Google / Facebook</SelectItem>
                                      </SelectContent>
                                    </Select>
                                  </div>
                                  <div className="space-y-2">
                                    <Label>Mot de passe accueil</Label>
                                    <Input
                                      type="password"
                                      value={operationalTourSettingsForm.targets[target.key].accueilPassword}
                                      onChange={(e) => setOperationalTourSettingsForm((prev) => ({
                                        ...prev,
                                        targets: {
                                          ...prev.targets,
                                          [target.key]: { ...prev.targets[target.key], accueilPassword: e.target.value },
                                        },
                                      }))}
                                      disabled={operationalTourSettingsForm.targets[target.key].accueilAuthMode === "oauth"}
                                      placeholder={
                                        operationalTourSettingsForm.targets[target.key].accueilAuthMode === "oauth"
                                          ? "Non utilisé pour un compte Google/Facebook"
                                          : "Laisser vide pour conserver"
                                      }
                                    />
                                  </div>
                                  <div className="space-y-2">
                                    <Label>Compte admin dédié</Label>
                                    <Input
                                      value={operationalTourSettingsForm.targets[target.key].adminEmail}
                                      onChange={(e) => setOperationalTourSettingsForm((prev) => ({
                                        ...prev,
                                        targets: {
                                          ...prev.targets,
                                          [target.key]: { ...prev.targets[target.key], adminEmail: e.target.value },
                                        },
                                      }))}
                                      placeholder="admin@test.fr"
                                    />
                                  </div>
                                  <div className="space-y-2">
                                    <Label>Type de connexion admin</Label>
                                    <Select
                                      value={operationalTourSettingsForm.targets[target.key].adminAuthMode}
                                      onValueChange={(value) => setOperationalTourSettingsForm((prev) => ({
                                        ...prev,
                                        targets: {
                                          ...prev.targets,
                                          [target.key]: {
                                            ...prev.targets[target.key],
                                            adminAuthMode: value as OperationalTourAuthMode,
                                            adminPassword: value === "oauth" ? "" : prev.targets[target.key].adminPassword,
                                          },
                                        },
                                      }))}
                                    >
                                      <SelectTrigger>
                                        <SelectValue placeholder="Choisir le type de connexion" />
                                      </SelectTrigger>
                                      <SelectContent>
                                        <SelectItem value="local">Compte local avec mot de passe</SelectItem>
                                        <SelectItem value="oauth">Compte Google / Facebook</SelectItem>
                                      </SelectContent>
                                    </Select>
                                  </div>
                                  <div className="space-y-2">
                                    <Label>Mot de passe admin</Label>
                                    <Input
                                      type="password"
                                      value={operationalTourSettingsForm.targets[target.key].adminPassword}
                                      onChange={(e) => setOperationalTourSettingsForm((prev) => ({
                                        ...prev,
                                        targets: {
                                          ...prev.targets,
                                          [target.key]: { ...prev.targets[target.key], adminPassword: e.target.value },
                                        },
                                      }))}
                                      disabled={operationalTourSettingsForm.targets[target.key].adminAuthMode === "oauth"}
                                      placeholder={
                                        operationalTourSettingsForm.targets[target.key].adminAuthMode === "oauth"
                                          ? "Non utilisé pour un compte Google/Facebook"
                                          : "Laisser vide pour conserver"
                                      }
                                    />
                                  </div>
                                </div>
                              </div>
                            ))}
                          </div>
                        </div>
                      )}

                      {selectedOperationalTourReport ? (
                        <>
                          <div className="grid gap-4 md:grid-cols-4">
                            <div className="rounded-lg border bg-muted/30 p-4">
                              <p className="text-sm text-muted-foreground">État global</p>
                              <Badge className={`mt-2 border ${getOperationalTourStatusMeta(selectedOperationalTourReport.status).className}`}>
                                {getOperationalTourStatusMeta(selectedOperationalTourReport.status).label}
                              </Badge>
                            </div>
                            <div className="rounded-lg border bg-muted/30 p-4">
                              <p className="text-sm text-muted-foreground">Environnement</p>
                              <p className="mt-2 text-lg font-semibold">{selectedOperationalTourReport.environmentLabel}</p>
                            </div>
                            <div className="rounded-lg border bg-muted/30 p-4">
                              <p className="text-sm text-muted-foreground">Durée</p>
                              <p className="mt-2 text-lg font-semibold">{Math.round((selectedOperationalTourReport.durationMs || 0) / 1000)} s</p>
                            </div>
                            <div className="rounded-lg border bg-muted/30 p-4">
                              <p className="text-sm text-muted-foreground">Dernier contrôle</p>
                              <p className="mt-2 text-sm font-semibold">{formatDateTime(selectedOperationalTourReport.finishedAt)}</p>
                            </div>
                          </div>

                          <div className="grid gap-4 xl:grid-cols-2">
                            {([
                              { key: "technical", title: "Tour technique", section: selectedOperationalTourSections.technical },
                              { key: "user", title: "Tour utilisateur", section: selectedOperationalTourSections.user },
                            ] as const).map((entry) => {
                              const sectionStatus = entry.section.error > 0 ? "error" : entry.section.warning > 0 ? "warning" : "ok";
                              const meta = getOperationalTourStatusMeta(sectionStatus);
                              const kindMeta = getOperationalTourKindMeta(entry.key);
                              return (
                                <div key={entry.key} className="rounded-lg border bg-muted/20 p-4">
                                  <div className="flex items-center justify-between gap-3">
                                    <div className="space-y-2">
                                      <Badge className={`border ${kindMeta.className}`}>{kindMeta.label}</Badge>
                                      <p className="font-medium">{entry.title}</p>
                                    </div>
                                    <Badge className={`border ${meta.className}`}>{meta.label}</Badge>
                                  </div>
                                  <div className="mt-4 grid grid-cols-4 gap-3 text-sm">
                                    <div>
                                      <p className="text-muted-foreground">Total</p>
                                      <p className="mt-1 text-lg font-semibold">{entry.section.total}</p>
                                    </div>
                                    <div>
                                      <p className="text-muted-foreground">OK</p>
                                      <p className="mt-1 text-lg font-semibold">{entry.section.ok}</p>
                                    </div>
                                    <div>
                                      <p className="text-muted-foreground">Avert.</p>
                                      <p className="mt-1 text-lg font-semibold">{entry.section.warning}</p>
                                    </div>
                                    <div>
                                      <p className="text-muted-foreground">Erreurs</p>
                                      <p className="mt-1 text-lg font-semibold">{entry.section.error}</p>
                                    </div>
                                  </div>
                                </div>
                              );
                            })}
                          </div>

                          <div className="rounded-lg border">
                            <div className="border-b px-4 py-3">
                              <p className="font-medium">Détail des vérifications</p>
                              <p className="text-sm text-muted-foreground">
                                {selectedOperationalTourReport.baseUrl}
                              </p>
                            </div>
                            <div className="divide-y">
                              {([
                                { key: "technical", title: "Tour technique", steps: selectedOperationalTourSections.technical.steps },
                                { key: "user", title: "Tour utilisateur", steps: selectedOperationalTourSections.user.steps },
                              ] as const).map((section) => {
                                const kindMeta = getOperationalTourKindMeta(section.key);
                                return (
                                  <div key={section.key}>
                                    <div className="border-b bg-muted/20 px-4 py-3">
                                      <div className="flex items-center gap-2">
                                        <Badge className={`border ${kindMeta.className}`}>{kindMeta.label}</Badge>
                                        <p className="font-medium">{section.title}</p>
                                      </div>
                                    </div>
                                    <div className="divide-y">
                                      {section.steps.map((step: any) => {
                                        const meta = getOperationalTourStatusMeta(step.status);
                                        return (
                                          <div key={step.id} className="flex flex-col gap-3 px-4 py-3 lg:flex-row lg:items-start lg:justify-between">
                                            <div className="space-y-1">
                                              <div className="flex flex-wrap items-center gap-2">
                                                <p className="font-medium">{step.label}</p>
                                                <Badge className={`border ${meta.className}`}>{meta.label}</Badge>
                                                {step.critical === false ? <Badge variant="outline">Optionnel</Badge> : null}
                                              </div>
                                              <p className="text-sm text-muted-foreground">{step.message}</p>
                                              {step.details ? (
                                                <p className="text-xs text-muted-foreground break-all">{step.details}</p>
                                              ) : null}
                                            </div>
                                            <div className="text-xs text-muted-foreground">
                                              {Math.max(0, Math.round((step.durationMs || 0) / 100) / 10)} s
                                            </div>
                                          </div>
                                        );
                                      })}
                                      {section.steps.length === 0 ? (
                                        <div className="px-4 py-6 text-sm text-muted-foreground">
                                          Aucun contrôle enregistré dans cette section.
                                        </div>
                                      ) : null}
                                    </div>
                                  </div>
                                );
                              })}
                            </div>
                          </div>
                        </>
                      ) : (
                        <div className="rounded-lg border border-dashed px-6 py-10 text-center text-sm text-muted-foreground">
                          Aucun contrôle lancé pour le moment.
                        </div>
                      )}

                      <div className="rounded-lg border">
                        <div className="flex flex-col gap-3 border-b px-4 py-3 lg:flex-row lg:items-center lg:justify-between">
                          <div>
                            <p className="font-medium">Historique des contrôles</p>
                            <p className="text-sm text-muted-foreground">
                              Les derniers tours lancés depuis l’administration.
                            </p>
                          </div>
                          {user?.role === "super_admin" ? (
                            <Button
                              type="button"
                              size="sm"
                              variant="outline"
                              onClick={() => {
                                if (typeof window !== "undefined" && !window.confirm("Nettoyer l’historique ancien et conserver seulement les derniers contrôles utiles ?")) {
                                  return;
                                }
                                clearOperationalTourHistoryMutation.mutate();
                              }}
                              disabled={clearOperationalTourHistoryMutation.isPending || (operationalTourHistory || []).length === 0}
                            >
                              {clearOperationalTourHistoryMutation.isPending ? (
                                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                              ) : (
                                <Trash2 className="mr-2 h-4 w-4" />
                              )}
                              Nettoyer l’historique ancien
                            </Button>
                          ) : null}
                        </div>
                        {(operationalTourHistory || []).length > 0 ? (
                          <div className="divide-y">
                            {(operationalTourHistory || []).map((entry: any) => {
                              const meta = getOperationalTourStatusMeta(entry.status);
                              return (
                                <div key={entry.id} className="flex flex-col gap-3 px-4 py-3 lg:flex-row lg:items-center lg:justify-between">
                                  <div className="space-y-1">
                                    <div className="flex flex-wrap items-center gap-2">
                                      <p className="font-medium">{entry.environmentLabel}</p>
                                      <Badge className={`border ${meta.className}`}>{meta.label}</Badge>
                                    </div>
                                    <p className="text-sm text-muted-foreground">
                                      {formatDateTime(entry.finishedAt)} • {entry.summary.ok} OK • {entry.summary.warning} avertissement(s) • {entry.summary.error} erreur(s)
                                    </p>
                                  </div>
                                  <div className="flex flex-wrap gap-2">
                                    <Button type="button" size="sm" variant="outline" onClick={() => setSelectedOperationalTourReportId(entry.id)}>
                                      <Eye className="mr-2 h-4 w-4" />
                                      Voir
                                    </Button>
                                    <Button type="button" size="sm" variant="outline" onClick={() => exportOperationalTourReport(entry)}>
                                      <Download className="mr-2 h-4 w-4" />
                                      JSON
                                    </Button>
                                  </div>
                                </div>
                              );
                            })}
                          </div>
                        ) : (
                          <div className="px-4 py-8 text-sm text-muted-foreground">
                            Aucun historique disponible.
                          </div>
                        )}
                      </div>
                    </CardContent>
                  </Card>
                </div>
              )}
            </TabsContent>
          )}

          <TabsContent value="diffusion" className="space-y-6">
            {user?.role !== "logistique_controle" && (
            <Card>
              <CardHeader>
                <CardTitle className="flex items-center gap-2">
                  <Mail className="h-5 w-5 text-primary" />
                  Messagerie du portail
                </CardTitle>
                <CardDescription>
                  Configure l’adresse d’envoi utilisée pour les invitations, la diffusion interne et les notifications par email.
                </CardDescription>
              </CardHeader>
              <CardContent className="space-y-6">
                <div className="grid gap-4 md:grid-cols-4">
                  <div className="rounded-lg border bg-muted/30 p-4">
                    <p className="text-sm text-muted-foreground">État</p>
                    <div className="mt-2">
                      <Badge variant={mailSettings?.ready ? "default" : "secondary"}>
                        {mailSettings?.ready ? "Configuration prête" : "Configuration incomplète"}
                      </Badge>
                    </div>
                  </div>
                  <div className="rounded-lg border bg-muted/30 p-4">
                    <p className="text-sm text-muted-foreground">Source</p>
                    <p className="mt-2 font-medium">
                      {mailSettings?.source === "admin"
                        ? "Administration"
                        : mailSettings?.source === "admin+environment"
                          ? "Admin + environnement"
                          : "Environnement serveur"}
                    </p>
                  </div>
                  <div className="rounded-lg border bg-muted/30 p-4">
                    <p className="text-sm text-muted-foreground">Mot de passe</p>
                    <p className="mt-2 font-medium">
                      {mailSettings?.smtpPassConfigured ? "Enregistré" : "Non renseigné"}
                    </p>
                  </div>
                  <div className="rounded-lg border bg-muted/30 p-4">
                    <p className="text-sm text-muted-foreground">Dernière mise à jour</p>
                    <p className="mt-2 font-medium">
                      {mailSettings?.updatedAt ? formatDateTime(mailSettings.updatedAt) : "Jamais"}
                    </p>
                    {mailSettings?.updatedByUserName && (
                      <p className="text-xs text-muted-foreground mt-1">
                        par {mailSettings.updatedByUserName}
                      </p>
                    )}
                  </div>
                </div>

                {!mailSettings?.ready && (mailSettings?.missing?.length || 0) > 0 && (
                  <div className="rounded-lg border border-amber-200 bg-amber-50 p-4 text-sm text-amber-900">
                    <p className="font-medium">Configuration incomplète</p>
                    <p className="mt-1">
                      Champs à compléter : {mailSettings?.missing.join(", ")}
                    </p>
                  </div>
                )}

                <div className="grid gap-6 xl:grid-cols-[1.15fr_0.85fr]">
                  <section className="space-y-4">
                    <div className="grid gap-4 md:grid-cols-2">
                      <div className="space-y-2">
                        <Label>Fournisseur mail</Label>
                        <Select
                          value={mailSettingsForm.smtpProvider}
                          onValueChange={(value: SupportedMailProvider) => handleMailProviderChange(value)}
                          disabled={user?.role !== "super_admin"}
                        >
                          <SelectTrigger>
                            <SelectValue placeholder="Choisir un fournisseur" />
                          </SelectTrigger>
                          <SelectContent>
                            {Object.entries(mailProviderLabels).map(([value, label]) => (
                              <SelectItem key={value} value={value}>
                                {label}
                              </SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                      </div>
                      <div className="space-y-2">
                        <Label>Adresse d’envoi</Label>
                        <Input
                          type="email"
                          value={mailSettingsForm.smtpFrom}
                          onChange={(e) => setMailSettingsForm((prev) => ({ ...prev, smtpFrom: e.target.value }))}
                          placeholder="contact@portail-association973.com"
                          disabled={user?.role !== "super_admin"}
                        />
                      </div>
                    </div>

                    <div className="grid gap-4 md:grid-cols-2">
                      <div className="space-y-2">
                        <Label>Identifiant SMTP</Label>
                        <Input
                          value={mailSettingsForm.smtpUser}
                          onChange={(e) => setMailSettingsForm((prev) => ({ ...prev, smtpUser: e.target.value }))}
                          placeholder="contact@portail-association973.com"
                          disabled={user?.role !== "super_admin"}
                        />
                      </div>
                      <div className="space-y-2">
                        <Label>Mot de passe SMTP</Label>
                        <Input
                          type="password"
                          value={mailSettingsForm.smtpPass}
                          onChange={(e) => setMailSettingsForm((prev) => ({ ...prev, smtpPass: e.target.value }))}
                          placeholder={mailSettings?.smtpPassConfigured ? "Laisser vide pour conserver" : "Mot de passe"}
                          disabled={user?.role !== "super_admin"}
                        />
                        <p className="text-xs text-muted-foreground">
                          Laisse vide pour conserver le mot de passe déjà enregistré.
                        </p>
                      </div>
                    </div>

                    <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                      <div className="space-y-2 xl:col-span-2">
                        <Label>Serveur SMTP</Label>
                        <Input
                          value={mailSettingsForm.smtpHost}
                          onChange={(e) => setMailSettingsForm((prev) => ({ ...prev, smtpHost: e.target.value }))}
                          placeholder="smtp.mail.ovh.net"
                          disabled={user?.role !== "super_admin"}
                        />
                      </div>
                      <div className="space-y-2">
                        <Label>Port</Label>
                        <Input
                          value={mailSettingsForm.smtpPort}
                          onChange={(e) => setMailSettingsForm((prev) => ({ ...prev, smtpPort: e.target.value }))}
                          placeholder="465"
                          disabled={user?.role !== "super_admin"}
                        />
                      </div>
                      <div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-1">
                        <div className="rounded-lg border p-3">
                          <div className="flex items-center justify-between gap-3">
                            <div>
                              <p className="text-sm font-medium">SSL / TLS</p>
                              <p className="text-xs text-muted-foreground">Connexion sécurisée directe</p>
                            </div>
                            <Switch
                              checked={mailSettingsForm.smtpSecure}
                              onCheckedChange={(checked) => setMailSettingsForm((prev) => ({ ...prev, smtpSecure: checked }))}
                              disabled={user?.role !== "super_admin"}
                            />
                          </div>
                        </div>
                        <div className="rounded-lg border p-3">
                          <div className="flex items-center justify-between gap-3">
                            <div>
                              <p className="text-sm font-medium">STARTTLS</p>
                              <p className="text-xs text-muted-foreground">Exiger TLS après connexion</p>
                            </div>
	                            <Switch
	                              checked={mailSettingsForm.smtpRequireTls}
	                              onCheckedChange={(checked) => setMailSettingsForm((prev) => ({ ...prev, smtpRequireTls: checked }))}
	                              disabled={user?.role !== "super_admin"}
	                            />
			                        </div>
				                    </div>
				                  </div>
				                </div>

	                    <div className="flex flex-wrap gap-3">
                      <Button
                        onClick={handleMailSettingsSave}
                        disabled={user?.role !== "super_admin" || setMailSettingsMutation.isPending}
                      >
                        {setMailSettingsMutation.isPending ? (
                          <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                        ) : (
                          <Settings className="mr-2 h-4 w-4" />
                        )}
                        Enregistrer la configuration
                      </Button>
                    </div>
                  </section>

                  <section className="space-y-4">
                    <Card className="border-dashed">
                      <CardHeader>
                        <CardTitle className="text-base">Tester l’envoi</CardTitle>
                        <CardDescription>
                          Enregistre d’abord la configuration, puis envoie un email de test vers l’adresse de ton choix.
                        </CardDescription>
                      </CardHeader>
                      <CardContent className="space-y-4">
                        <div className="space-y-2">
                          <Label>Adresse de test</Label>
                          <Input
                            type="email"
                            value={mailSettingsForm.testRecipient}
                            onChange={(e) => setMailSettingsForm((prev) => ({ ...prev, testRecipient: e.target.value }))}
                            placeholder="admin@exemple.fr"
                            disabled={user?.role !== "super_admin"}
                          />
                        </div>
                        <Button
                          variant="outline"
                          onClick={handleSendTestMail}
                          disabled={user?.role !== "super_admin" || sendTestMailMutation.isPending}
                        >
                          {sendTestMailMutation.isPending ? (
                            <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                          ) : (
                            <Mail className="mr-2 h-4 w-4" />
                          )}
                          Envoyer un email de test
                        </Button>
                      </CardContent>
                    </Card>
                  </section>
                </div>
              </CardContent>
            </Card>
            )}

            <div className="flex items-center justify-between gap-4">
              <div>
                <h2 className="text-lg font-semibold">Services internes matériel CCDS</h2>
                <p className="text-sm text-muted-foreground">
                  Crée les services utilisés pour la récupération du matériel et pour le contrôle de son retour.
                </p>
              </div>
              <div className="flex flex-wrap gap-2">
                <Button
                  variant="outline"
                  onClick={() =>
                    setEditingOperationalRecapService({
                      label: "",
                      usage: "terrain",
                      description: "",
                      recipientEmails: "",
                      actif: true,
                    })
                  }
                >
                  <Plus className="mr-2 h-4 w-4" />
                  Service de récupération
                </Button>
                <Button
                  onClick={() =>
                    setEditingOperationalRecapService({
                      label: "",
                      usage: "controle",
                      description: "",
                      recipientEmails: "",
                      actif: true,
                    })
                  }
                >
                  <Plus className="mr-2 h-4 w-4" />
                  Service de contrôle
                </Button>
              </div>
            </div>

            <div className="grid gap-6 lg:grid-cols-2">
              <div className="space-y-4">
                <div>
                  <h3 className="font-medium">Services terrain / récupération</h3>
                  <p className="text-sm text-muted-foreground">
                    Reçoivent la mission terrain pour récupérer le matériel CCDS.
                  </p>
                </div>
                {terrainOperationalRecapServices.map((service) => (
                <Card key={service.id}>
                  <CardHeader className="pb-3">
                    <div className="flex items-start justify-between gap-3">
                      <div>
                        <CardTitle className="text-base">{service.label}</CardTitle>
                        <CardDescription className="mt-1">
                          {service.description || "Aucune description"}
                        </CardDescription>
                      </div>
                      <Badge variant={service.actif ? "default" : "secondary"}>
                        {service.actif ? "Actif" : "Inactif"}
                      </Badge>
                    </div>
                  </CardHeader>
                  <CardContent className="space-y-4">
                    <div className="rounded-lg border bg-muted/30 p-3 text-sm">
                      <p className="font-medium">{service.recipientEmails.length} destinataire(s)</p>
                      <p className="mt-2 text-muted-foreground break-words">
                        {service.recipientEmails.join(", ")}
                      </p>
                    </div>
                    <div className="flex gap-2">
                        <Button
                          variant="outline"
                          size="sm"
                          onClick={() =>
                            setEditingOperationalRecapService({
                              id: service.id,
                              label: service.label,
                              usage: service.usage === "controle" ? "controle" : "terrain",
                              description: service.description || "",
                              recipientEmails: service.recipientEmails.join(", "),
                              actif: service.actif,
                            })
                          }
                      >
                        <Pencil className="mr-2 h-4 w-4" />
                        Modifier
                      </Button>
                      <AdminOverflowMenu label={`Actions pour ${service.label}`}>
                        <DropdownMenuItem
                          variant="destructive"
                          onClick={() => deleteOperationalRecapServiceMutation.mutate({ id: service.id })}
                        >
                          <Trash2 className="h-4 w-4" />
                          Supprimer
                        </DropdownMenuItem>
                      </AdminOverflowMenu>
                    </div>
                  </CardContent>
                </Card>
                ))}
                {terrainOperationalRecapServices.length === 0 && (
                  <Card>
                    <CardContent className="py-10 text-center text-muted-foreground">
                      Aucun service de récupération enregistré pour le moment.
                    </CardContent>
                  </Card>
                )}
              </div>

              <div className="space-y-4">
                <div>
                  <h3 className="font-medium">Services internes de contrôle</h3>
                  <p className="text-sm text-muted-foreground">
                    Reçoivent les confirmations, retards et alertes pour vérifier le retour du matériel.
                  </p>
                </div>
                {controlOperationalRecapServices.map((service) => (
                  <Card key={service.id}>
                    <CardHeader className="pb-3">
                      <div className="flex items-start justify-between gap-3">
                        <div>
                          <CardTitle className="text-base">{service.label}</CardTitle>
                          <CardDescription className="mt-1">
                            {service.description || "Aucune description"}
                          </CardDescription>
                        </div>
                        <Badge variant={service.actif ? "default" : "secondary"}>
                          {service.actif ? "Actif" : "Inactif"}
                        </Badge>
                      </div>
                    </CardHeader>
                    <CardContent className="space-y-4">
                      <div className="rounded-lg border bg-muted/30 p-3 text-sm">
                        <p className="font-medium">{service.recipientEmails.length} destinataire(s)</p>
                        <p className="mt-2 text-muted-foreground break-words">
                          {service.recipientEmails.join(", ")}
                        </p>
                      </div>
                      <div className="flex gap-2">
                        <Button
                          variant="outline"
                          size="sm"
                          onClick={() =>
                            setEditingOperationalRecapService({
                              id: service.id,
                              label: service.label,
                              usage: service.usage === "controle" ? "controle" : "terrain",
                              description: service.description || "",
                              recipientEmails: service.recipientEmails.join(", "),
                              actif: service.actif,
                            })
                          }
                        >
                          <Pencil className="mr-2 h-4 w-4" />
                          Modifier
                        </Button>
                        <AdminOverflowMenu label={`Actions pour ${service.label}`}>
                          <DropdownMenuItem
                            variant="destructive"
                            onClick={() => deleteOperationalRecapServiceMutation.mutate({ id: service.id })}
                          >
                            <Trash2 className="h-4 w-4" />
                            Supprimer
                          </DropdownMenuItem>
                        </AdminOverflowMenu>
                      </div>
                    </CardContent>
                  </Card>
                ))}
                {controlOperationalRecapServices.length === 0 && (
                  <Card>
                    <CardContent className="py-10 text-center text-muted-foreground">
                      Aucun service de contrôle enregistré pour le moment.
                    </CardContent>
                  </Card>
                )}
              </div>

              {normalizedOperationalRecapServices.length === 0 && (
                <Card className="lg:col-span-2">
                  <CardContent className="py-10 text-center text-muted-foreground">
                    Crée d’abord un service de récupération ou de contrôle pour l’utiliser dans les demandes de matériel.
                  </CardContent>
                </Card>
              )}
            </div>
          </TabsContent>

          {/* Requests Tab */}
          <TabsContent value="requests" className="space-y-4">
            <AdminSurface
              title="Gestion des demandes"
              description="Point d’entrée de l’exploitation quotidienne pour retrouver, filtrer et traiter rapidement les dossiers en cours."
              badge={
                <Badge variant="outline" className="border-blue-200 bg-blue-50 text-blue-700">
                  {requestsData?.total || 0} dossier(s)
                </Badge>
              }
            >
              <div className="space-y-5">
                <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                  <AdminMetricTile
                    label="Demandes visibles"
                    value={requestsData?.data?.length || 0}
                    tone="info"
                    detail={requestSearch ? "Après recherche en cours" : "Vue actuelle"}
                  />
                  <AdminMetricTile
                    label="Réponse attendue"
                    value={(requestsData?.data || []).filter((request) => request.status === "soumise").length}
                    tone="warning"
                    detail="Dossiers à instruire ou à reprendre"
                  />
                  <AdminMetricTile
                    label="En traitement"
                    value={(requestsData?.data || []).filter((request) => request.status === "en_cours_traitement").length}
                    tone="success"
                    detail="Suivis en cours côté accueil ou services"
                  />
                  <AdminMetricTile
                    label="Validées"
                    value={(requestsData?.data || []).filter((request) => request.status === "validee").length}
                    detail="Décisions déjà rendues"
                  />
                </div>

                <div className="grid gap-3 rounded-2xl border border-slate-200 bg-slate-50/80 p-3 md:grid-cols-2 xl:grid-cols-[minmax(0,1fr)_180px_210px]">
                  <div className="relative">
                    <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
                    <Input
                      placeholder="Rechercher..."
                      value={requestSearch}
                      onChange={(e) => setRequestSearch(e.target.value)}
                      className="w-full pl-9"
                    />
                  </div>
                  <Select value={requestStatusFilter} onValueChange={setRequestStatusFilter}>
                    <SelectTrigger className="w-full">
                      <SelectValue placeholder="Statut" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="all">Tous</SelectItem>
                      <SelectItem value="soumise">Soumise</SelectItem>
                      <SelectItem value="en_cours_traitement">En cours</SelectItem>
                      <SelectItem value="validee">Validée</SelectItem>
                      <SelectItem value="refusee">Refusée</SelectItem>
                      <SelectItem value="annulee">Annulée</SelectItem>
                    </SelectContent>
                  </Select>
                  <Select value={requestTypeFilter} onValueChange={setRequestTypeFilter}>
                    <SelectTrigger className="w-full">
                      <SelectValue placeholder="Type" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="all">Tous</SelectItem>
                      {Object.entries(requestTypeLabels).map(([key, label]) => (
                        <SelectItem key={key} value={key}>{label}</SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>
              </div>
            </AdminSurface>

            <Card>
              <CardContent className="pt-6">
                {requestsLoading ? (
                  <div className="flex justify-center py-8">
                    <Loader2 className="h-6 w-6 animate-spin" />
                  </div>
                ) : (
                  <div className="space-y-3">
                    {requestsData?.data?.map((request) => {
                      const formData = parseRequestFormData(request.formData);
                      const isDemandeSalle = request.type === 'demande_salle';
                      const isDemandeMateriel = request.type === 'demande_materiel_evenementiel';
                      const canOperateRequest =
                        isDemandeMateriel
                          ? canManageLogistics
                          : !isLogisticsControl;
                      const dailySlots = Array.isArray(formData.horairesParJour)
                        ? formData.horairesParJour.filter((slot: any) => slot?.date)
                        : [];
                      const creneau = getReservationSalleScheduleSummary({
                        useDetailedSchedule: Boolean(formData.useDetailedSchedule),
                        horairesParJour: dailySlots,
                        heureDebut: String(formData.heureDebut || ''),
                        heureFin: String(formData.heureFin || ''),
                      });
                      const periode = formatReservationSallePeriod({
                        dateReservation: String(formData.dateReservation || ''),
                        dateFinReservation: String(formData.dateFinReservation || ''),
                      });
                      const salles = Array.isArray(formData.sallesSelectionnees) ? formData.sallesSelectionnees : [];
                      const materielPeriode = formatAdminMaterialEventPeriod(formData);
                      const materielCreneau = getReservationSalleScheduleSummary({
                        useDetailedSchedule: false,
                        horairesParJour: [],
                        heureDebut: String(formData.heureDebutManifestation || formData.heureDebut || ''),
                        heureFin: String(formData.heureFinManifestation || formData.heureFin || ''),
                      });
                      const materielsLoues = materialEventItems
                        .filter((item) => parseMaterialEventQuantity(formData.quantitesDemandees?.[item.key]) > 0)
                        .map((item) => {
                          const quantity = parseMaterialEventQuantity(formData.quantitesDemandees?.[item.key]);
                          return quantity > 1 ? `${item.label} (${quantity})` : item.label;
                        });
                      const salleWorkflowStage = isDemandeSalle ? getSalleWorkflowStageFromFormData(formData) : null;
                      const requestStatusMeta = getAdminRequestStatusMeta(request);
                      const waitingForAssociationResponse = isDemandeSalle && formData?.salleWorkflow?.quoteStatus === "en_attente_association";
                      const assignedToCurrentUser = Boolean(user?.id && request.assigneA === user.id);
                      const requestFinancialMode = formData?.cadreDSU?.materielEvent?.financialDecision?.financialMode;
                      const hasRequestFinancialDecision = ['gratuite', 'gratuite_avec_caution', 'location_payante'].includes(String(requestFinancialMode));
                      const requiresFinancialPriority = (
                        (request.type === 'demande_salle' || request.type === 'demande_materiel_evenementiel')
                        && (request.status === 'soumise' || request.status === 'en_cours_traitement' || request.status === 'validee')
                        && !hasRequestFinancialDecision
                      );

                      return (
                      <div key={request.id} className="flex flex-col gap-4 rounded-2xl border border-border/70 bg-white/95 p-4 transition-all hover:-translate-y-0.5 hover:bg-muted/40 hover:shadow-sm 2xl:flex-row 2xl:items-start 2xl:justify-between">
                        <div className="min-w-0 flex-1">
                          <div className="mb-1 flex flex-wrap items-center gap-2">
                            <h3 className="font-medium">{request.titre}</h3>
                            <Badge variant="outline" className={requestStatusMeta.className}>
                              {requestStatusMeta.label}
                            </Badge>
                            {request.priority === 'haute' && (
                              <Badge variant="destructive">Prioritaire</Badge>
                            )}
                            {requiresFinancialPriority && (
                              <Badge className="bg-amber-100 text-amber-900 border border-amber-200">
                                Action prioritaire
                              </Badge>
                            )}
                            {salleWorkflowStage && !waitingForAssociationResponse && (
                              <Badge variant="outline" className={salleWorkflowStage.className}>
                                {salleWorkflowStage.label}
                              </Badge>
                            )}
                            {assignedToCurrentUser && !waitingForAssociationResponse && (request.status === 'soumise' || request.status === 'en_cours_traitement') && (
                              <Badge variant="outline" className="bg-primary/10 text-primary border-primary/20">
                                À traiter par vous
                              </Badge>
                            )}
                            {isDemandeMateriel && !canManageLogistics && (
                              <Badge variant="outline" className="bg-slate-100 text-slate-700 border-slate-200">
                                Suivi par logistique & contrôle
                              </Badge>
                            )}
                          </div>
                          <p className="text-sm text-muted-foreground mb-2">
                            {requestTypeLabels[request.type]} • Association #{request.associationId}
                          </p>
                          {isDemandeSalle && (
                            <div className="mb-3 grid gap-2 sm:grid-cols-3">
                              <div className="rounded-md border bg-white px-3 py-2">
                                <p className="text-[11px] uppercase tracking-wide text-muted-foreground">Période</p>
                                <p className="text-sm font-medium">{periode}</p>
                              </div>
                              <div className="rounded-md border bg-white px-3 py-2">
                                <p className="text-[11px] uppercase tracking-wide text-muted-foreground">Organisation horaire</p>
                                <p className="text-sm font-medium">{creneau || '-'}</p>
                              </div>
                              <div className="rounded-md border bg-white px-3 py-2">
                                <p className="text-[11px] uppercase tracking-wide text-muted-foreground">Salle(s)</p>
                                <p className="text-sm font-medium">{salles.length > 0 ? salles.join(', ') : '-'}</p>
                              </div>
                            </div>
                          )}
                          {isDemandeMateriel && (
                            <div className="mb-3 grid gap-2 sm:grid-cols-3">
                              <div className="rounded-md border bg-white px-3 py-2">
                                <p className="text-[11px] uppercase tracking-wide text-muted-foreground">Période</p>
                                <p className="text-sm font-medium">{materielPeriode}</p>
                              </div>
                              <div className="rounded-md border bg-white px-3 py-2">
                                <p className="text-[11px] uppercase tracking-wide text-muted-foreground">Organisation horaire</p>
                                <p className="text-sm font-medium">{materielCreneau || '-'}</p>
                              </div>
                              <div className="rounded-md border bg-white px-3 py-2">
                                <p className="text-[11px] uppercase tracking-wide text-muted-foreground">Matériel loué</p>
                                <p className="text-sm font-medium">{materielsLoues.length > 0 ? materielsLoues.join(', ') : '-'}</p>
                              </div>
                            </div>
                          )}
                          <div className="flex flex-wrap items-center gap-3 text-sm text-muted-foreground">
                            <span className="flex items-center gap-1">
                              <Calendar className="h-3 w-3" />
                              {formatDate(request.createdAt)}
                            </span>
                            {request.montantDemande && (
                              <span className="font-medium text-foreground">
                                {formatAmount(request.montantDemande)}
                              </span>
                            )}
                            {request.assigneA && (
                              <span className="flex items-center gap-1">
                                <UserCog className="h-3 w-3" />
                                {assignedToCurrentUser ? 'Pris en charge par vous' : 'Assigné'}
                              </span>
                            )}
                          </div>
                        </div>
                        <div className={cn("flex w-full flex-col gap-2 sm:grid sm:grid-cols-2 2xl:flex 2xl:w-auto 2xl:flex-col", isMobile && "pt-1")}>
                          {(request.status === 'soumise' || request.status === 'en_cours_traitement') && canOperateRequest && !waitingForAssociationResponse && (
                            <>
                              <Button
                                size="sm"
                                variant={assignedToCurrentUser ? "default" : "secondary"}
                                onClick={() => openRequestProcessDialog(request)}
                                className="w-full 2xl:w-auto"
                              >
                                Traiter
                              </Button>
                            </>
                          )}
                          {waitingForAssociationResponse && canOperateRequest && (
                            <Button size="sm" variant="outline" disabled className="w-full 2xl:w-auto">
                              Réponse de l’association attendue
                            </Button>
                          )}
                          {((request.type === 'demande_salle' || request.type === 'demande_materiel_evenementiel') && (request.status === 'validee' || request.status === 'refusee')) && (
                            <Button
                              size="sm"
                              variant="outline"
                              onClick={() => pdfMutation.mutate({ id: request.id })}
                              disabled={pdfMutation.isPending}
                              className="w-full 2xl:w-auto"
                            >
                              {pdfMutation.isPending ? (
                                <Loader2 className="h-4 w-4 animate-spin" />
                              ) : (
                                <Download className="h-4 w-4" />
                              )}
                            </Button>
                          )}
                          <Link href={`/dashboard/requests/${request.id}`} {...buildRoutePrefetchHandlers(`/dashboard/requests/${request.id}`)}>
                            <Button size="sm" variant="ghost" className="w-full 2xl:w-auto">
                              <Eye className="h-4 w-4" />
                            </Button>
                          </Link>
                        </div>
                      </div>
                    )})}
                    {(!requestsData?.data || requestsData.data.length === 0) && (
                      <div className="text-center py-12 text-muted-foreground">
                        <ClipboardList className="h-12 w-12 mx-auto mb-4 opacity-50" />
                        <p>Aucune demande trouvée</p>
                      </div>
                    )}
                  </div>
                )}
              </CardContent>
            </Card>
          </TabsContent>

          {/* Associations Tab */}
          <TabsContent value="associations" className="space-y-4">
            <AdminSurface
              title="Gestion des associations"
              description="Vue centrale des structures inscrites pour piloter l’activité, suivre la complétude des profils et retrouver rapidement les contacts utiles."
              badge={
                <Badge variant="outline" className="border-emerald-200 bg-emerald-50 text-emerald-700">
                  {associationsData?.total || 0} association(s)
                </Badge>
              }
            >
              <div className="space-y-5">
                <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                  <AdminMetricTile
                    label="Associations visibles"
                    value={associationsData?.total || 0}
                    tone="info"
                    detail="Résultat de la recherche et du filtre actif"
                  />
                  <AdminMetricTile
                    label="Profils complets"
                    value={(associationsData?.data || []).filter((assoc) => assoc.profileComplete).length}
                    tone="success"
                    detail="Profils prêts pour un usage fluide du portail"
                  />
                  <AdminMetricTile
                    label="Structures actives"
                    value={(associationsData?.data || []).filter((assoc) => assoc.isActive).length}
                    detail="Comptes actuellement exploitables"
                  />
                  <AdminMetricTile
                    label="Profils préremplis"
                    value={(associationsData?.data || []).filter((assoc) => assoc.sourceDirectoryEntryId).length}
                    tone="warning"
                    detail="Associations issues du référentiel initial"
                  />
                </div>

                <div className="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
                  <div className="flex flex-wrap gap-2">
                    {associationCommuneOptions.map((option) => (
                      <Button
                        key={option.value}
                        type="button"
                        variant={associationCommuneFilter === option.value ? "default" : "outline"}
                        size="sm"
                        className="rounded-full gap-2"
                        onClick={() => setAssociationCommuneFilter(option.value)}
                      >
                        <span>{option.label}</span>
                        <span
                          className={cn(
                            "inline-flex min-w-6 items-center justify-center rounded-full px-2 py-0.5 text-[11px] font-semibold",
                            associationCommuneFilter === option.value
                              ? "bg-white/20 text-white"
                              : "bg-muted text-muted-foreground"
                          )}
                        >
                          {associationCommuneCounts?.[option.value] ?? 0}
                        </span>
                      </Button>
                    ))}
                  </div>

                  <div className="grid gap-3 rounded-2xl border border-slate-200 bg-slate-50/80 p-3 sm:grid-cols-[minmax(0,1fr)_44px] xl:min-w-[420px]">
                    <div className="relative">
                      <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
                      <Input
                        placeholder="Rechercher..."
                        value={associationSearch}
                        onChange={(e) => setAssociationSearch(e.target.value)}
                        className="pl-9 w-full"
                      />
                    </div>
	                    <Button variant="outline" size="icon">
	                      <Download className="h-4 w-4" />
	                    </Button>
			                    </div>
			                    </div>
			                  </div>
	            </AdminSurface>

            <Card>
              <CardContent className="pt-6">
                <div className="mb-5 flex flex-wrap gap-2">
                  {associationCommuneOptions.map((option) => (
                    <Button
                      key={option.value}
                      type="button"
                      variant={associationCommuneFilter === option.value ? "default" : "outline"}
                      size="sm"
                      className="rounded-full gap-2"
                      onClick={() => setAssociationCommuneFilter(option.value)}
                    >
                      <span>{option.label}</span>
                      <span
                        className={cn(
                          "inline-flex min-w-6 items-center justify-center rounded-full px-2 py-0.5 text-[11px] font-semibold",
                          associationCommuneFilter === option.value
                            ? "bg-white/20 text-white"
                            : "bg-muted text-muted-foreground"
                        )}
                      >
                        {associationCommuneCounts?.[option.value] ?? 0}
                      </span>
                    </Button>
                  ))}
                </div>
                {associationsLoading ? (
                  <div className="flex justify-center py-8">
                    <Loader2 className="h-6 w-6 animate-spin" />
                  </div>
                ) : (
                  <div className="space-y-3">
                    {associationsData?.data?.map((assoc) => (
                      <div key={assoc.id} className="flex flex-col gap-4 rounded-2xl border border-border/70 bg-white/95 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:bg-muted/40 hover:shadow-sm xl:flex-row xl:items-start xl:justify-between">
                        <div className="min-w-0 flex-1">
                          <div className="mb-1 flex flex-wrap items-center gap-2">
                            <h3 className="font-medium">{assoc.nomAssociation}</h3>
                            <Badge variant={assoc.isActive ? 'default' : 'secondary'}>
                              {assoc.isActive ? 'Actif' : 'Inactif'}
                            </Badge>
                            {assoc.sourceDirectoryEntryId ? (
                              <Badge variant="outline" className="bg-primary/5 text-primary border-primary/20">
                                Prérempli
                              </Badge>
                            ) : null}
                            {assoc.profileComplete && (
                              <Badge variant="outline" className="bg-green-50 text-green-700">
                                Profil complet
                              </Badge>
                            )}
                          </div>
                          <div className="grid gap-2 sm:grid-cols-3">
                            <div className="rounded-xl border bg-slate-50/70 px-3 py-2">
                              <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Commune</p>
                              <p className="mt-1 text-sm font-medium text-slate-900">{assoc.ville || "Non renseignée"}</p>
                            </div>
                            <div className="rounded-xl border bg-slate-50/70 px-3 py-2">
                              <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">SIRET</p>
                              <p className="mt-1 text-sm font-medium text-slate-900">{assoc.siret || "Non renseigné"}</p>
                            </div>
                            <div className="rounded-xl border bg-slate-50/70 px-3 py-2">
                              <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Contact</p>
                              <p className="mt-1 truncate text-sm font-medium text-slate-900">{assoc.emailContact || "Non renseigné"}</p>
                            </div>
                          </div>
                          <div className="mt-3 text-sm text-muted-foreground space-y-1">
                            {(assoc.facebookUrl || assoc.instagramUrl) ? (
                              <div className="flex flex-wrap gap-2 pt-1">
                                {assoc.facebookUrl ? (
                                  <a
                                    href={assoc.facebookUrl}
                                    target="_blank"
                                    rel="noreferrer"
                                    className="inline-flex items-center gap-1 rounded-full border px-2 py-1 text-xs text-foreground hover:bg-muted/50"
                                  >
                                    <Facebook className="h-3.5 w-3.5 text-primary" />
                                    Facebook
                                  </a>
                                ) : null}
                                {assoc.instagramUrl ? (
                                  <a
                                    href={assoc.instagramUrl}
                                    target="_blank"
                                    rel="noreferrer"
                                    className="inline-flex items-center gap-1 rounded-full border px-2 py-1 text-xs text-foreground hover:bg-muted/50"
                                  >
                                    <Instagram className="h-3.5 w-3.5 text-primary" />
                                    Instagram
                                  </a>
                                ) : null}
                              </div>
                            ) : null}
                          </div>
                        </div>
                        <div className="flex flex-col gap-3 xl:min-w-[220px] xl:items-end">
                          <div className="rounded-xl border bg-slate-50/70 px-3 py-2 text-sm text-muted-foreground">
                            Inscrite le <span className="font-medium text-slate-900">{formatDate(assoc.createdAt)}</span>
                          </div>
                          <div className="flex flex-wrap items-center gap-2 xl:justify-end">
                          <Button
                            size="sm"
                            variant="outline"
                            onClick={() => setSelectedAssociation(assoc)}
                          >
                            <FileText className="mr-1 h-4 w-4" />
                            Documents
                          </Button>
                          <AdminOverflowMenu label={`Actions pour ${assoc.nomAssociation || "cette association"}`}>
                            <DropdownMenuItem
                              onClick={() =>
                                toggleAssociationMutation.mutate({
                                  id: assoc.id,
                                  isActive: !assoc.isActive,
                                })
                              }
                            >
                              <RotateCcw className="h-4 w-4" />
                              {assoc.isActive ? "Désactiver" : "Activer"}
                            </DropdownMenuItem>
                          </AdminOverflowMenu>
                          </div>
                        </div>
                      </div>
                    ))}
                    {(!associationsData?.data || associationsData.data.length === 0) && (
                      <div className="text-center py-12 text-muted-foreground">
                        <Building2 className="h-12 w-12 mx-auto mb-4 opacity-50" />
                        <p>Aucune association trouvée</p>
                      </div>
                    )}
                  </div>
                )}
              </CardContent>
            </Card>
          </TabsContent>

          {/* Users Tab */}
          <TabsContent value="users" className="space-y-4">
            <AdminSurface
              title="Comptes internes et habilitations"
              description="Pilotage des équipes internes, des rôles et du groupe logistique pour garder une administration claire, maîtrisée et cohérente."
              badge={
                <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                  {adminUsers?.length || 0} compte(s)
                </Badge>
              }
            >
              <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                <AdminMetricTile
                  label="Comptes internes"
                  value={adminUsers?.length || 0}
                  tone="info"
                  detail="Population administrative du portail"
                />
                <AdminMetricTile
                  label="Actifs"
                  value={(adminUsers || []).filter((admin) => admin.isActive).length}
                  tone="success"
                  detail="Comptes actuellement utilisables"
                />
                <AdminMetricTile
                  label="Groupe logistique"
                  value={logisticsGroupForm.memberUserIds.length}
                  tone="warning"
                  detail="Membres rattachés au suivi matériel CCDS"
                />
                <AdminMetricTile
                  label="Super admins"
                  value={(adminUsers || []).filter((admin) => admin.role === "super_admin").length}
                  detail="Comptes à privilèges maximum"
                />
              </div>
            </AdminSurface>

            <Card>
              <CardHeader>
                <CardTitle>Groupe interne matériel CCDS - logistique et contrôle</CardTitle>
                <CardDescription>
                  Ce groupe interne à accès limité réunit les personnes qui traitent les demandes de matériel CCDS, suivent les restitutions et contrôlent les retours. Le Super Administrateur peut tout voir et attribue les accès, mais n’est pas rattaché à ce groupe métier.
                </CardDescription>
              </CardHeader>
              <CardContent className="space-y-4">
                <div className="space-y-2">
                  <Label htmlFor="logistics-group-label">Nom du groupe interne</Label>
                  <Input
                    id="logistics-group-label"
                    value={logisticsGroupForm.label}
                    onChange={(e) => setLogisticsGroupForm((prev) => ({ ...prev, label: e.target.value }))}
                    placeholder="Ex. Groupe interne matériel CCDS - logistique et contrôle"
                    disabled={user?.role !== "super_admin"}
                  />
                </div>

                <div className="space-y-3">
                  <div className="flex items-center justify-between">
                    <p className="font-medium">Membres du groupe</p>
                    <p className="text-sm text-muted-foreground">
                      Plusieurs comptes internes nominatifs peuvent y être rattachés. Le Super Administrateur conserve la visibilité complète, sans faire partie de l’équipe métier.
                    </p>
                  </div>
                  <div className="rounded-lg border bg-muted/20 p-3 text-sm text-muted-foreground">
                    Crée un compte interne par personne, avec son nom et son adresse email, puis rattache-le à ce groupe pour lui donner un accès limité au matériel CCDS.
                  </div>
                  <div className="grid gap-3">
                    {adminUsers?.filter((admin) => admin.role === "admin" || admin.role === "service_terrain" || admin.role === "logistique_controle").map((admin) => {
                      const checked = logisticsGroupForm.memberUserIds.includes(admin.id);
                      return (
                        <label
                          key={admin.id}
                          className="flex items-start justify-between gap-4 rounded-lg border p-4"
                        >
                          <div className="space-y-1">
                            <p className="font-medium">{admin.name || "Sans nom"}</p>
                            <p className="text-sm text-muted-foreground">{admin.email}</p>
                          </div>
                          <Checkbox
                            checked={checked}
                            disabled={user?.role !== "super_admin"}
                            onCheckedChange={(value: boolean | "indeterminate") => toggleLogisticsGroupMember(admin.id, Boolean(value))}
                          />
                        </label>
                      );
                    })}
                    {(!adminUsers || adminUsers.filter((admin) => admin.role === "admin" || admin.role === "service_terrain" || admin.role === "logistique_controle").length === 0) && (
                      <div className="rounded-lg border border-dashed p-4 text-sm text-muted-foreground">
                        Aucun administrateur disponible pour composer le groupe.
                      </div>
                    )}
                  </div>
                </div>

                <AdminActionButtonGroup>
                  <Button
                    onClick={handleSaveLogisticsGroup}
                    disabled={user?.role !== "super_admin" || setLogisticsGroupMutation.isPending}
                  >
                    {setLogisticsGroupMutation.isPending && (
                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                    )}
                    Enregistrer le groupe matériel CCDS
                  </Button>
                </AdminActionButtonGroup>
              </CardContent>
            </Card>

            <Card>
              <CardHeader>
                <div className="flex items-start justify-between gap-4">
                  <div>
                    <CardTitle>Comptes internes</CardTitle>
                    <CardDescription>
                      Création, rôle, activation et suivi des comptes internes du portail
                    </CardDescription>
                  </div>
                  {user?.role === "super_admin" && (
                    <Button
                      size="sm"
                      onClick={() =>
                        setEditingAdminUser({
                          name: "",
                          email: "",
                          role: "admin",
                          canManageLogistics: false,
                          isActive: true,
                          password: "",
                        })
                      }
                    >
                      <Plus className="mr-2 h-4 w-4" />
                      Nouveau compte interne
                    </Button>
                  )}
                </div>
              </CardHeader>
              <CardContent>
                <div className="space-y-3">
                  {adminUsers?.map((admin) => (
                    <div key={admin.id} className="flex flex-col gap-4 rounded-2xl border border-border/70 bg-white/95 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:bg-muted/40 hover:shadow-sm lg:flex-row lg:items-center lg:justify-between">
                      <div className="flex items-start gap-3">
                        <div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10">
                          <UserCog className="h-5 w-5 text-primary" />
                        </div>
                        <div className="space-y-1">
                          <p className="font-medium">{admin.name || 'Sans nom'}</p>
                          <p className="text-sm text-muted-foreground">{admin.email}</p>
                          <p className="text-sm text-muted-foreground">
                            {internalRoleDescriptions[admin.role as keyof typeof internalRoleDescriptions]}
                          </p>
                          <div className="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
                            <span>Méthode: {admin.loginMethod || 'local_jwt'}</span>
                            <span>•</span>
                            <span>Dernière connexion: {formatDate(admin.lastSignedIn)}</span>
                          </div>
                        </div>
                      </div>
                      <div className="flex flex-wrap items-center gap-2">
                        <RoleTooltip>
                          <RoleTooltipTrigger asChild>
                            <Badge variant={admin.role === 'super_admin' ? 'default' : 'secondary'}>
                              {internalRoleLabels[admin.role as keyof typeof internalRoleLabels] || 'Admin'}
                            </Badge>
                          </RoleTooltipTrigger>
                          <RoleTooltipContent side="top">
                            {internalRoleDescriptions[admin.role as keyof typeof internalRoleDescriptions]}
                          </RoleTooltipContent>
                        </RoleTooltip>
                        {(admin.role !== "super_admin" && (admin.role === "logistique_controle" || admin.canManageLogistics || logisticsGroupForm.memberUserIds.includes(admin.id))) && (
                          <Badge variant="outline">Matériel CCDS</Badge>
                        )}
                        <Badge variant={admin.isActive ? 'outline' : 'destructive'}>
                          {admin.isActive ? 'Actif' : 'Désactivé'}
                        </Badge>
                        {user?.role === "super_admin" && (
                          <>
                            <Button
                              size="sm"
                              variant="outline"
                              onClick={() =>
                                setEditingAdminUser({
                                  id: admin.id,
                                  name: admin.name || "",
                                  email: admin.email || "",
                                  role: admin.role,
                                  canManageLogistics:
                                    admin.role === "super_admin" || admin.role === "logistique_controle"
                                      ? true
                                      : Boolean(admin.canManageLogistics),
                                  isActive: admin.isActive,
                                  password: "",
                                })
                              }
                            >
                              <Pencil className="mr-2 h-4 w-4" />
                              Modifier
                            </Button>
                            <AdminOverflowMenu label={`Actions pour ${admin.name || admin.email || "ce compte interne"}`}>
                              <DropdownMenuItem
                                onClick={() =>
                                  toggleAdminUserActiveMutation.mutate({
                                    id: admin.id,
                                    isActive: !admin.isActive,
                                  })
                                }
                              >
                                <RotateCcw className="h-4 w-4" />
                                {admin.isActive ? "Désactiver" : "Réactiver"}
                              </DropdownMenuItem>
                              <DropdownMenuSeparator />
                              <DropdownMenuItem
                                variant="destructive"
                                onClick={() => {
                                  if (typeof window !== "undefined" && !window.confirm(`Supprimer définitivement le compte interne de ${admin.name || admin.email || "cet utilisateur"} ?`)) {
                                    return;
                                  }
                                  deleteAdminUserMutation.mutate({ id: admin.id });
                                }}
                                disabled={deleteAdminUserMutation.isPending}
                              >
                                <Trash2 className="h-4 w-4" />
                                Supprimer définitivement
                              </DropdownMenuItem>
                            </AdminOverflowMenu>
                          </>
                        )}
                      </div>
                    </div>
                  ))}
                  {(!adminUsers || adminUsers.length === 0) && (
                    <div className="text-center py-12 text-muted-foreground">
                      <Users className="h-12 w-12 mx-auto mb-4 opacity-50" />
                      <p>Aucun administrateur</p>
                    </div>
                  )}
                </div>
              </CardContent>
            </Card>
          </TabsContent>

          {/* Notifications Tab */}
          <TabsContent value="notifications" className="space-y-4">
            <AdminSurface
              title={dashboardCopy.notificationsTitle}
              description={
                isAccueil
                  ? "Nouvelles demandes de salle, réponses association et confirmations finales à reprendre."
                  : isLogisticsControl
                    ? "Restitutions, missions terrain, litiges et alertes matériel prioritaires."
                    : `${unreadCount || 0} notification(s) non lue(s) dans la file d’exploitation.`
              }
              badge={
                <Badge variant="outline" className="border-red-200 bg-red-50 text-red-700">
                  {unreadCount || 0} non lue(s)
                </Badge>
              }
            >
              <div className="space-y-5">
                <div className="grid gap-4 md:grid-cols-3">
                  <AdminMetricTile
                    label="À lire"
                    value={unreadCount || 0}
                    tone="warning"
                    detail="Notifications non encore ouvertes"
                  />
                  <AdminMetricTile
                    label="Visibles"
                    value={filteredNotifications?.length || 0}
                    tone="info"
                    detail={showUnreadNotificationsOnly ? "Filtre non lu actif" : "Toutes les priorités du rôle"}
                  />
                  <AdminMetricTile
                    label="Mode de lecture"
                    value={showUnreadNotificationsOnly ? "Non lu" : "Complet"}
                    detail="Le flux s’adapte à votre besoin du moment"
                  />
                </div>

                <AdminActionButtonGroup>
                  <Button
                    variant={showUnreadNotificationsOnly ? "default" : "outline"}
                    size="sm"
                    onClick={() => setShowUnreadNotificationsOnly((current) => !current)}
                  >
                    {showUnreadNotificationsOnly ? "Voir tout le flux" : "Afficher seulement le non lu"}
                  </Button>
                  {(unreadCount || 0) > 0 && (
                    <Button variant="outline" size="sm" onClick={() => markAllReadMutation.mutate()}>
                      Tout marquer comme lu
                    </Button>
                  )}
                </AdminActionButtonGroup>
              </div>
            </AdminSurface>

            <AdminSectionCollapsible
              title="Flux détaillé des notifications"
              description="Ouvre ce bloc pour relire toute la file d’exploitation, puis referme-le quand tu veux garder une vue plus compacte du rôle."
              open={!adminCollapsedSections.notificationsFeed}
              onOpenChange={(open) => setAdminSectionExpanded("notificationsFeed", open)}
              className="bg-white"
              contentClassName="pt-1"
              headerBadge={
                <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                  {filteredNotifications?.length || 0} notification(s)
                </Badge>
              }
            >
              <Card className="border-0 shadow-none">
                <CardContent className="px-0 pb-0 pt-2">
                  <ScrollArea className="h-[500px] pr-1">
                    <div className="space-y-3">
                      {filteredNotifications?.map((notif) => (
                        <div 
                          key={notif.id} 
                          className={`cursor-pointer rounded-2xl border p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-sm ${!notif.lu ? 'border-blue-200 bg-blue-50/90' : 'bg-white/95 hover:bg-muted/40'}`}
                          onClick={() => {
                            if (!notif.lu) {
                              markNotificationReadMutation.mutate({ id: notif.id });
                            }
                            if (notif.lien) {
                              setLocation(notif.lien);
                            }
                          }}
                        >
                          <div className="flex items-start gap-3">
                            <div className={`rounded-full p-2 ${
                              notif.type === 'nouvelle_demande' ? 'bg-blue-100' :
                              notif.type === 'demande_en_retard' ? 'bg-red-100' :
                              notif.type === 'nouvelle_association' ? 'bg-green-100' :
                              'bg-gray-100'
                            }`}>
                              {notif.type === 'nouvelle_demande' && <ClipboardList className="h-4 w-4 text-blue-600" />}
                              {notif.type === 'demande_en_retard' && <AlertTriangle className="h-4 w-4 text-red-600" />}
                              {notif.type === 'nouvelle_association' && <Building2 className="h-4 w-4 text-green-600" />}
                              {notif.type === 'systeme' && <Settings className="h-4 w-4 text-gray-600" />}
                            </div>
                            <div className="flex-1">
                              <div className="flex items-center justify-between gap-3">
                                <h4 className="font-medium">{notif.titre}</h4>
                                {!notif.lu && (
                                  <Badge variant="default" className="text-xs">Nouveau</Badge>
                                )}
                              </div>
                              <p className="mt-1 text-sm text-muted-foreground">{notif.message}</p>
                              <p className="mt-2 text-xs text-muted-foreground">
                                {formatDateTime(notif.createdAt)}
                              </p>
                            </div>
                          </div>
                        </div>
                      ))}
                      {(!filteredNotifications || filteredNotifications.length === 0) && (
                        <div className="py-12 text-center text-muted-foreground">
                          <Bell className="mx-auto mb-4 h-12 w-12 opacity-50" />
                          <p>{showUnreadNotificationsOnly ? "Aucune notification non lue" : "Aucune notification prioritaire pour ce rôle"}</p>
                        </div>
                      )}
                    </div>
                  </ScrollArea>
                </CardContent>
              </Card>
            </AdminSectionCollapsible>
          </TabsContent>

          {/* Appearance Tab */}
          <TabsContent value="appearance" className="space-y-6">
            <div className="grid items-start gap-6 2xl:grid-cols-[minmax(0,1.12fr)_360px]">
              <section className="min-w-0 space-y-6">
                <Card>
                  <CardHeader>
                    <CardTitle className="flex items-center gap-2">
                      <Palette className="h-5 w-5 text-primary" />
                      Apparence du portail
                    </CardTitle>
                    <CardDescription>
                      Gère les skins, couleurs, typographies, visuels et styles d’interface sans toucher au back-end ni redéployer l’application.
                    </CardDescription>
                  </CardHeader>
                  <CardContent className="space-y-8">
                    <div className="rounded-2xl border border-slate-200 bg-slate-50/70 p-4">
                      <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
                        <div className="space-y-1">
                          <p className="text-sm font-semibold text-slate-950">Centre de personnalisation</p>
                          <p className="text-sm text-muted-foreground">
                            Les réglages sont rangés par usage pour éviter l’effet brouillon: base visuelle, branding, immersion et bannière.
                          </p>
                        </div>
                        <div className="flex flex-wrap gap-2">
                          <a href="#appearance-presets" className="rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-50">
                            Skins
                          </a>
                          <a href="#appearance-foundation" className="rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-50">
                            Socle
                          </a>
                          <a href="#appearance-branding" className="rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-50">
                            Branding
                          </a>
                          <a href="#appearance-immersive" className="rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-50">
                            Immersion
                          </a>
                          <a href="#appearance-hero" className="rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-50">
                            Bannière
                          </a>
                        </div>
                      </div>
                    </div>

	                    <AdminSectionCollapsible
	                      title="Skins et base active"
	                      description="Choisis une base, puis affine seulement ce qui doit l’être."
	                      open={!adminCollapsedSections.appearancePresets}
	                      onOpenChange={(open) => setAdminSectionExpanded("appearancePresets", open)}
	                      className="bg-white/90"
	                      contentClassName="space-y-5"
	                    >

                      <div className="rounded-lg border bg-muted/25 p-4">
                      <div className="flex flex-wrap items-start justify-between gap-3">
                        <div className="space-y-1">
                          <p className="text-sm font-medium">Skin actif</p>
                          <div className="flex flex-wrap items-center gap-2">
                            <Badge variant="secondary">{appearanceForm.activeSkinId || "custom"}</Badge>
                            <span className="text-sm text-muted-foreground">
                              {appearanceSkinPresets.find((preset) => preset.id === appearanceForm.activeSkinId)?.label || "Personnalisation libre"}
                            </span>
                          </div>
                        </div>
                        <div className="flex items-center gap-3">
                          <span className="text-sm text-muted-foreground">
                            {appearancePreviewEnabled ? "Aperçu local actif" : "Aperçu local coupé"}
                          </span>
                          <Switch checked={appearancePreviewEnabled} onCheckedChange={setAppearancePreviewEnabled} />
                        </div>
                      </div>
                      </div>

                      <div className="space-y-3">
                        <div>
                          <Label>Skins disponibles</Label>
                        <p className="text-sm text-muted-foreground">
                          Une base prête à l’emploi pour démarrer plus vite.
                        </p>
                        </div>
                        <div className="grid gap-3 lg:grid-cols-2">
                          {appearanceSkinPresets.map((preset) => {
                            const isActive = appearanceForm.activeSkinId === preset.id;
                            return (
                              <button
                                key={preset.id}
                                type="button"
                                className={cn(
                                  "rounded-lg border px-4 py-4 text-left transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
                                  isActive ? "border-primary bg-primary/5" : "hover:bg-accent/40"
                                )}
                                onClick={() => applyAppearancePreset(preset.id)}
                              >
                                <div className="mb-3 flex items-center justify-between gap-3">
                                  <div className="flex items-center gap-2">
                                    <span className="h-5 w-5 rounded-full border" style={{ backgroundColor: preset.value.primaryColor }} />
                                    <span className="h-5 w-5 rounded-full border" style={{ backgroundColor: preset.value.secondaryColor }} />
                                    <span className="h-5 w-5 rounded-full border" style={{ backgroundColor: preset.value.accentColor }} />
                                  </div>
                                  <Badge variant={isActive ? "default" : "outline"}>{preset.category}</Badge>
                                </div>
                                <p className="font-medium">{preset.label}</p>
                                <p className="mt-1 text-sm text-muted-foreground">{preset.description}</p>
                                <p className="mt-2 text-xs text-muted-foreground">
                                  Usages : {preset.usage.join(", ")}
                                </p>
                              </button>
                            );
	                          })}
	                        </div>
		                    </div>
	                    </AdminSectionCollapsible>

	                    <AdminSectionCollapsible
	                      title="Socle du portail"
	                      description="Regroupe les réglages transverses qui donnent le ton général: identité, densité, composants, palette et typographies."
	                      open={!adminCollapsedSections.appearanceFoundation}
	                      onOpenChange={(open) => setAdminSectionExpanded("appearanceFoundation", open)}
	                      className="bg-white/90"
	                      contentClassName="space-y-5"
	                    >

                    <div className="grid gap-6 xl:grid-cols-2">
                      <div className="space-y-4 rounded-lg border p-4">
                        <div>
                          <Label className="text-sm">Identité du portail</Label>
                          <p className="text-sm text-muted-foreground">
                            Nom, signature et mode global affichés dans l’accueil et les espaces internes.
                          </p>
                        </div>
                        <div className="space-y-2">
                          <Label htmlFor="portalTitle">Titre du portail</Label>
                          <Input
                            id="portalTitle"
                            value={appearanceForm.portalTitle}
                            onChange={(e) => setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", portalTitle: e.target.value }))}
                            placeholder="Portail Associations 973"
                          />
                        </div>
                        <div className="space-y-2">
                          <Label htmlFor="portalTagline">Accroche</Label>
                          <Textarea
                            id="portalTagline"
                            value={appearanceForm.portalTagline}
                            onChange={(e) => setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", portalTagline: e.target.value }))}
                            rows={3}
                            placeholder="Démarches, annuaire et services du tissu associatif guyanais"
                          />
                        </div>
                        <div className="rounded-2xl border border-slate-200 bg-slate-50/70 p-4">
                          <div className="flex items-start justify-between gap-3">
                            <div className="space-y-1">
                              <p className="text-sm font-semibold text-slate-950">Aperçu titre & accroche</p>
                              <p className="text-xs text-muted-foreground">
                                Visualise immédiatement le rendu avant validation.
                              </p>
                            </div>
                            <Badge variant="outline" className="border-slate-200 bg-white text-slate-600">
                              Aperçu live
                            </Badge>
                          </div>
                          <div className="mt-4 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
                            <div className="border-b border-slate-200 bg-slate-50/80 px-4 py-3">
                              <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">
                                En-tête du portail
                              </p>
                            </div>
                            <div className="space-y-3 px-4 py-4">
                              <div className="flex items-center gap-3">
                                <div
                                  className="h-10 w-10 rounded-xl border border-slate-200 shadow-sm"
                                  style={{
                                    background: `linear-gradient(135deg, ${appearanceForm.primaryColor}22 0%, ${appearanceForm.secondaryColor}18 100%)`,
                                  }}
                                />
                                <div className="min-w-0">
                                  <p
                                    className="break-words text-lg font-semibold leading-tight text-slate-950"
                                    style={{
                                      fontFamily:
                                        appearanceForm.headingFontFamily === "marianne"
                                          ? '"Marianne", system-ui, sans-serif'
                                          : undefined,
                                    }}
                                  >
                                    {appearanceForm.portalTitle || "Titre du portail"}
                                  </p>
                                  <p
                                    className="mt-1 break-words text-sm leading-6 text-slate-600"
                                    style={{
                                      fontFamily:
                                        appearanceForm.fontFamily === "marianne"
                                          ? '"Marianne", system-ui, sans-serif'
                                          : undefined,
                                    }}
                                  >
                                    {appearanceForm.portalTagline || "Votre accroche apparaîtra ici dès qu’elle sera saisie."}
                                  </p>
                                </div>
                              </div>
                              <div className="flex flex-wrap gap-2">
                                <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                  Titre
                                </Badge>
                                <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                  Accroche
                                </Badge>
                                <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                  Identité live
                                </Badge>
                              </div>
                            </div>
                          </div>
                        </div>
                        <div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
                          <div className="flex items-start justify-between gap-3">
                            <div className="space-y-1">
                              <p className="text-sm font-semibold text-slate-950">Placement libre dans la bannière</p>
                              <p className="text-xs text-muted-foreground">
                                Les réglages détaillés sont regroupés juste dessous dans la zone Bannière pour éviter les allers-retours.
                              </p>
                            </div>
                            <a
                              href="#appearance-hero"
                              className="rounded-full border border-slate-200 bg-slate-50 px-3 py-1 text-xs font-medium text-slate-700 transition hover:bg-slate-100"
                            >
                              Aller à la bannière
                            </a>
                          </div>
                        </div>
                        <div className="space-y-2">
                          <Label htmlFor="themeMode">Mode de thème</Label>
                          <Select
                            value={appearanceForm.themeMode}
                            onValueChange={(value: AppearanceThemeMode) =>
                              setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", themeMode: value }))
                            }
                          >
                            <SelectTrigger id="themeMode">
                              <SelectValue placeholder="Choisir un mode" />
                            </SelectTrigger>
                            <SelectContent>
                              <SelectItem value="light">Clair</SelectItem>
                              <SelectItem value="dark">Sombre</SelectItem>
                              <SelectItem value="system">Automatique</SelectItem>
                            </SelectContent>
                          </Select>
                        </div>
                      </div>

                      <div className="space-y-4 rounded-lg border p-4">
                        <div>
                          <Label className="text-sm">Comportement des composants</Label>
                          <p className="text-sm text-muted-foreground">
                            Définis le ton du menu, des cartes, des boutons, des tableaux et des champs.
                          </p>
                        </div>
                        <div className="grid gap-4 sm:grid-cols-2">
                          <div className="space-y-2">
                            <Label>Cartes</Label>
                            <Select
                              value={appearanceForm.cardStyle}
                              onValueChange={(value: AppearanceCardStyle) =>
                                setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", cardStyle: value }))
                              }
                            >
                              <SelectTrigger><SelectValue /></SelectTrigger>
                              <SelectContent>
                                {appearanceCardStyles.map((style) => (
                                  <SelectItem key={style} value={style}>{appearanceCardStyleLabels[style]}</SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                          <div className="space-y-2">
                            <Label>Tableaux</Label>
                            <Select
                              value={appearanceForm.tableStyle}
                              onValueChange={(value: AppearanceTableStyle) =>
                                setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", tableStyle: value }))
                              }
                            >
                              <SelectTrigger><SelectValue /></SelectTrigger>
                              <SelectContent>
                                {appearanceTableStyles.map((style) => (
                                  <SelectItem key={style} value={style}>{appearanceTableStyleLabels[style]}</SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                          <div className="space-y-2">
                            <Label>Densité d’affichage</Label>
                            <Select
                              value={appearanceForm.displayDensity}
                              onValueChange={(value: AppearanceDisplayDensity) =>
                                setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", displayDensity: value }))
                              }
                            >
                              <SelectTrigger><SelectValue /></SelectTrigger>
                              <SelectContent>
                                {appearanceDisplayDensities.map((density) => (
                                  <SelectItem key={density} value={density}>{appearanceDisplayDensityLabels[density]}</SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                          <div className="space-y-2">
                            <Label>Sidebar</Label>
                            <Select
                              value={appearanceForm.sidebarStyle}
                              onValueChange={(value: AppearanceSidebarStyle) =>
                                setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", sidebarStyle: value }))
                              }
                            >
                              <SelectTrigger><SelectValue /></SelectTrigger>
                              <SelectContent>
                                {appearanceSidebarStyles.map((style) => (
                                  <SelectItem key={style} value={style}>{appearanceSidebarStyleLabels[style]}</SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                          <div className="space-y-2">
                            <Label>Boutons</Label>
                            <Select
                              value={appearanceForm.buttonStyle}
                              onValueChange={(value: AppearanceButtonStyle) =>
                                setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", buttonStyle: value }))
                              }
                            >
                              <SelectTrigger><SelectValue /></SelectTrigger>
                              <SelectContent>
                                {appearanceButtonStyles.map((style) => (
                                  <SelectItem key={style} value={style}>{appearanceButtonStyleLabels[style]}</SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                          <div className="space-y-2 sm:col-span-2">
                            <Label>Champs de formulaire</Label>
                            <Select
                              value={appearanceForm.inputStyle}
                              onValueChange={(value: AppearanceInputStyle) =>
                                setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", inputStyle: value }))
                              }
                            >
                              <SelectTrigger><SelectValue /></SelectTrigger>
                              <SelectContent>
                                {appearanceInputStyles.map((style) => (
                                  <SelectItem key={style} value={style}>{appearanceInputStyleLabels[style]}</SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
	                        </div>
	                      </div>
	                    </div>
                    <div className="rounded-lg border p-4">
                      <div className="mb-4">
                        <Label className="text-sm">Palette du portail</Label>
                        <p className="text-sm text-muted-foreground">
                          Toutes les zones importantes du portail reprennent ces variables CSS en temps réel.
                        </p>
                      </div>
                      <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
                        <div className="space-y-2">
                          <Label htmlFor="primaryColor">Couleur principale</Label>
                          <div className="flex items-center gap-3">
                            <Input
                              id="primaryColor"
                              type="color"
                              value={appearanceForm.primaryColor}
                              onChange={(e) => setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", primaryColor: e.target.value }))}
                              className="h-11 w-16 p-1"
                            />
                            <Input value={primaryColorInput} onChange={(e) => setPrimaryColorInput(e.target.value)} onBlur={(e) => commitAppearanceColor("primaryColor", e.target.value)} className="font-mono" />
                          </div>
                        </div>
                        <div className="space-y-2">
                          <Label htmlFor="secondaryColor">Couleur secondaire</Label>
                          <div className="flex items-center gap-3">
                            <Input
                              id="secondaryColor"
                              type="color"
                              value={appearanceForm.secondaryColor}
                              onChange={(e) => setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", secondaryColor: e.target.value }))}
                              className="h-11 w-16 p-1"
                            />
                            <Input value={secondaryColorInput} onChange={(e) => setSecondaryColorInput(e.target.value)} onBlur={(e) => commitAppearanceColor("secondaryColor", e.target.value)} className="font-mono" />
                          </div>
                        </div>
                        <div className="space-y-2">
                          <Label htmlFor="accentColor">Couleur d’accentuation</Label>
                          <div className="flex items-center gap-3">
                            <Input
                              id="accentColor"
                              type="color"
                              value={appearanceForm.accentColor}
                              onChange={(e) => setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", accentColor: e.target.value }))}
                              className="h-11 w-16 p-1"
                            />
                            <Input value={accentColorInput} onChange={(e) => setAccentColorInput(e.target.value)} onBlur={(e) => commitAppearanceColor("accentColor", e.target.value)} className="font-mono" />
                          </div>
                        </div>
                        <div className="space-y-2">
                          <Label htmlFor="buttonColor">Couleur des boutons</Label>
                          <div className="flex items-center gap-3">
                            <Input
                              id="buttonColor"
                              type="color"
                              value={appearanceForm.buttonColor}
                              onChange={(e) => setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", buttonColor: e.target.value }))}
                              className="h-11 w-16 p-1"
                            />
                            <Input value={buttonColorInput} onChange={(e) => setButtonColorInput(e.target.value)} onBlur={(e) => commitAppearanceColor("buttonColor", e.target.value)} className="font-mono" />
                          </div>
                        </div>
                        <div className="space-y-2">
                          <Label htmlFor="alertColor">Couleur d’alerte</Label>
                          <div className="flex items-center gap-3">
                            <Input
                              id="alertColor"
                              type="color"
                              value={appearanceForm.alertColor}
                              onChange={(e) => setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", alertColor: e.target.value }))}
                              className="h-11 w-16 p-1"
                            />
                            <Input value={alertColorInput} onChange={(e) => setAlertColorInput(e.target.value)} onBlur={(e) => commitAppearanceColor("alertColor", e.target.value)} className="font-mono" />
                          </div>
                        </div>
                      </div>
                    </div>

                    <input ref={logoImageInputRef} type="file" accept="image/png,image/jpeg,image/webp,image/svg+xml" className="hidden" onChange={(event) => void handleAppearanceAssetSelect("logoUrl", event)} />
                    <input ref={faviconImageInputRef} type="file" accept="image/png,image/jpeg,image/webp,image/svg+xml,image/x-icon,.ico" className="hidden" onChange={(event) => void handleAppearanceAssetSelect("faviconUrl", event)} />
                    <input
                      ref={heroImageInputRef}
                      type="file"
                      accept="image/png,image/jpeg,image/webp,image/svg+xml"
                      className="hidden"
                      onChange={handleHeroImageSelect}
                    />
                    <input ref={backgroundImageInputRef} type="file" accept="image/png,image/jpeg,image/webp,image/svg+xml" className="hidden" onChange={(event) => void handleAppearanceAssetSelect("backgroundImageUrl", event)} />
                    <input ref={backgroundTargetImageInputRef} type="file" accept="image/png,image/jpeg,image/webp,image/svg+xml" className="hidden" onChange={(event) => void handleAppearanceTargetAssetSelect(event)} />
                    <input ref={decorationImageInputRef} type="file" accept="image/png,image/jpeg,image/webp,image/svg+xml" className="hidden" onChange={(event) => void handleDecorationAssetSelect(event)} />
	                    <input
	                      ref={salleRibInputRef}
	                      type="file"
	                      accept="application/pdf,image/png,image/jpeg,image/webp"
	                      className="hidden"
	                      onChange={handleSalleRibSelect}
	                    />
	                  </div>

	                    </AdminSectionCollapsible>

		                    <AdminSectionCollapsible
		                      title="Branding et univers visuels"
		                      description="Logo, favicon, fond global et variations par zone métier."
		                      open={!adminCollapsedSections.appearanceBranding}
		                      onOpenChange={(open) => setAdminSectionExpanded("appearanceBranding", open)}
		                      className="bg-white/90"
		                      contentClassName="space-y-5"
		                    >

                    <div className="grid gap-6 2xl:grid-cols-[minmax(22rem,0.92fr)_minmax(0,1.08fr)]">
                      <div className="space-y-4 rounded-2xl border border-slate-200 bg-slate-50/50 p-5">
                        <div>
                          <Label className="text-sm">Typographies</Label>
                          <p className="text-sm text-muted-foreground">
                            Choisis la famille visuelle du texte puis ajuste la hiérarchie globale.
                          </p>
                        </div>
                        <div className="grid gap-4 xl:grid-cols-2">
                          <div className="space-y-2">
                            <Label>Police principale</Label>
                            <p className="text-xs text-muted-foreground">
                              Texte courant, paragraphes et aides.
                            </p>
                            <Select
                              value={appearanceForm.fontFamily}
                              onValueChange={(value: AppearanceFontFamily) =>
                                setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", fontFamily: value }))
                              }
                            >
                              <SelectTrigger className="h-11">
                                <SelectValue />
                              </SelectTrigger>
                              <SelectContent>
                                {appearanceFontFamilies.map((font) => (
                                  <SelectItem key={font} value={font}>{appearanceFontFamilyLabels[font]}</SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                          <div className="space-y-2">
                            <Label>Police des titres</Label>
                            <p className="text-xs text-muted-foreground">
                              En-têtes, sections et repères visuels.
                            </p>
                            <Select
                              value={appearanceForm.headingFontFamily}
                              onValueChange={(value: AppearanceFontFamily) =>
                                setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", headingFontFamily: value }))
                              }
                            >
                              <SelectTrigger className="h-11">
                                <SelectValue />
                              </SelectTrigger>
                              <SelectContent>
                                {appearanceFontFamilies.map((font) => (
                                  <SelectItem key={font} value={font}>{appearanceFontFamilyLabels[font]}</SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                        </div>
                        <div className="rounded-2xl border border-slate-200 bg-white px-4 py-3 shadow-sm">
                          <p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Aperçu typographique</p>
                          <div className="mt-3 space-y-2">
                            <p className="text-lg font-semibold text-slate-950">
                              Marianne reste propre, lisible et institutionnelle.
                            </p>
                            <p className="text-sm leading-6 text-slate-600">
                              Utilise cette zone pour vérifier rapidement l’équilibre entre les titres, les labels et le texte courant avant publication.
                            </p>
                          </div>
                        </div>
                        <div className="space-y-3">
                          <div className="flex items-center justify-between">
                            <Label>Taille des titres</Label>
                            <span className="text-sm font-medium">{appearanceForm.headingScalePercent}%</span>
                          </div>
                          <Slider
                            value={[appearanceForm.headingScalePercent]}
                            min={85}
                            max={130}
                            step={1}
                            onValueChange={(values) => setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", headingScalePercent: values[0] ?? prev.headingScalePercent }))}
                          />
                        </div>
                        <div className="space-y-3">
                          <div className="flex items-center justify-between">
                            <Label>Taille du texte courant</Label>
                            <span className="text-sm font-medium">{appearanceForm.bodyScalePercent}%</span>
                          </div>
                          <Slider
                            value={[appearanceForm.bodyScalePercent]}
                            min={90}
                            max={115}
                            step={1}
                            onValueChange={(values) => setAppearanceForm((prev) => ({ ...prev, activeSkinId: "custom", bodyScalePercent: values[0] ?? prev.bodyScalePercent }))}
                          />
                        </div>
                      </div>

                      <AdminSectionCollapsible
                        title="Univers visuels"
                        description="Visuels et fonds: pilote l’identité visuelle et l’ambiance sans nuire à la lisibilité."
                        open={!adminCollapsedSections.appearanceVisualUniverse}
                        onOpenChange={(open) => setAdminSectionExpanded("appearanceVisualUniverse", open)}
                        className="border-slate-200 bg-white"
                        contentClassName="space-y-6"
                      >
                        <div className="flex flex-wrap gap-2">
                          <Badge variant="outline" className="border-emerald-200 bg-emerald-50 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-emerald-700">
                            Logo {appearanceForm.logoUrl ? "personnalisé" : "par défaut"}
                          </Badge>
                          <Badge variant="outline" className="border-blue-200 bg-blue-50 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-blue-700">
                            Favicon {appearanceForm.faviconUrl ? "personnalisé" : "par défaut"}
                          </Badge>
                          <Badge variant="outline" className="border-violet-200 bg-violet-50 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-violet-700">
                            Fond {appearanceForm.backgroundEnabled ? "actif" : "inactif"}
                          </Badge>
                          <Badge variant="outline" className="border-amber-200 bg-amber-50 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-amber-700">
                            {appearanceForm.backgroundTargets.length} zone(s)
                          </Badge>
                        </div>
                        <div className="grid gap-5">
                          <div id="appearance-immersive" className="rounded-2xl border border-slate-200 bg-slate-50/60 p-5">
                            <div className="mb-4 flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
                              <div className="space-y-1.5">
                                <p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Identité principale</p>
                                <p className="text-base font-semibold text-slate-950">Logo du portail</p>
                                <p className="text-sm text-muted-foreground">{appearanceForm.logoUrl ? "Logo personnalisé actif" : "Aucun logo personnalisé"}</p>
                              </div>
                              <div className="flex flex-wrap gap-2">
                                <Button type="button" variant="outline" className="h-11 min-w-[138px] justify-center" onClick={() => logoImageInputRef.current?.click()} disabled={user?.role !== "super_admin" || isAppearanceAssetUploadBusy === "logoUrl"}>
                                  {isAppearanceAssetUploadBusy === "logoUrl" ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Upload className="mr-2 h-4 w-4" />}
                                  Importer
                                </Button>
                                <Button type="button" variant="outline" className="h-11 min-w-[138px] justify-center" onClick={() => handleAppearanceAssetReset("logoUrl")} disabled={user?.role !== "super_admin"}>
                                  <Trash2 className="mr-2 h-4 w-4" />
                                  Retirer
                                </Button>
                              </div>
                            </div>
                            <div className="grid gap-4 lg:grid-cols-[0.92fr_1.08fr]">
                              <div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
                                <p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Aperçu agrandi</p>
                                <div className="mt-4 flex min-h-[176px] items-center justify-center rounded-2xl border border-dashed border-slate-200 bg-slate-50 p-6">
                                  {appearanceForm.logoUrl ? (
                                    <img
                                      src={appearanceForm.logoUrl}
                                      alt="Aperçu du logo"
                                      className="max-h-32 w-auto object-contain"
                                    />
                                  ) : (
                                    <div className="text-center text-sm text-muted-foreground">
                                      Aucun logo importé
                                    </div>
                                  )}
                                </div>
                              </div>
                              <div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
                                <p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Rendu portail</p>
                                <div className="mt-4 overflow-hidden rounded-2xl border border-slate-200 bg-white">
                                  <div className="flex items-center gap-3 border-b border-slate-200 bg-slate-50 px-4 py-3">
                                    <div className="flex h-11 w-11 items-center justify-center overflow-hidden rounded-full border border-slate-200 bg-white">
                                      {appearanceForm.logoUrl ? (
                                        <img src={appearanceForm.logoUrl} alt="" className="h-9 w-9 object-contain" />
                                      ) : (
                                        <Shield className="h-5 w-5 text-slate-400" />
                                      )}
                                    </div>
                                    <div className="min-w-0">
                                      <p className="truncate text-sm font-semibold text-slate-950">{appearanceForm.portalTitle}</p>
                                      <p className="truncate text-xs text-muted-foreground">{appearanceForm.portalTagline}</p>
                                    </div>
                                  </div>
                                  <div className="space-y-3 p-4">
                                    <div className="h-3 w-32 rounded-full bg-slate-200" />
                                    <div className="h-3 w-48 rounded-full bg-slate-100" />
                                    <div className="grid gap-2 sm:grid-cols-2">
                                      <div className="h-16 rounded-xl border border-slate-200 bg-slate-50" />
                                      <div className="h-16 rounded-xl border border-slate-200 bg-slate-50" />
                                    </div>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </div>
                          <AdminSectionCollapsible
                            title="Navigation web / Favicon"
                            description={appearanceForm.faviconUrl ? "Favicon personnalisé actif" : "Favicon navigateur par défaut"}
                            open={!adminCollapsedSections.appearanceFavicon}
                            onOpenChange={(open) => setAdminSectionExpanded("appearanceFavicon", open)}
                            className="border-slate-200 bg-slate-50/60"
                            contentClassName="space-y-4"
                            headerBadge={(
                              <Badge variant="outline" className="border-blue-200 bg-blue-50 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-blue-700">
                                Favicon
                              </Badge>
                            )}
                          >
                            <div className="flex flex-wrap gap-2">
                                <Button type="button" variant="outline" className="h-11 min-w-[138px] justify-center" onClick={() => faviconImageInputRef.current?.click()} disabled={user?.role !== "super_admin" || isAppearanceAssetUploadBusy === "faviconUrl"}>
                                  {isAppearanceAssetUploadBusy === "faviconUrl" ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Upload className="mr-2 h-4 w-4" />}
                                  Importer
                                </Button>
                                <Button type="button" variant="outline" className="h-11 min-w-[138px] justify-center" onClick={() => handleAppearanceAssetReset("faviconUrl")} disabled={user?.role !== "super_admin"}>
                                  <Trash2 className="mr-2 h-4 w-4" />
                                  Retirer
                                </Button>
                              </div>
                            <div className="grid gap-4 lg:grid-cols-[0.92fr_1.08fr]">
                              <div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
                                <p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Aperçu agrandi</p>
                                <div className="mt-4 flex min-h-[176px] items-center justify-center rounded-2xl border border-dashed border-slate-200 bg-slate-50 p-6">
                                  <div className="flex h-24 w-24 items-center justify-center overflow-hidden rounded-[1.25rem] border border-slate-200 bg-white shadow-sm">
                                    {appearanceForm.faviconUrl ? (
                                      <img
                                        src={appearanceForm.faviconUrl}
                                        alt="Aperçu du favicon"
                                        className="h-16 w-16 object-contain"
                                      />
                                    ) : (
                                      <Globe className="h-8 w-8 text-slate-400" />
                                    )}
                                  </div>
                                </div>
                              </div>
                              <div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
                                <p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Simulation navigateur</p>
                                <div className="mt-4 rounded-2xl border border-slate-200 bg-slate-50 p-4">
                                  <div className="rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm">
                                    <div className="flex items-center gap-3">
                                      <div className="flex h-8 w-8 items-center justify-center overflow-hidden rounded-lg border border-slate-200 bg-white">
                                        {appearanceForm.faviconUrl ? (
                                          <img src={appearanceForm.faviconUrl} alt="" className="h-5 w-5 object-contain" />
                                        ) : (
                                          <Globe className="h-4 w-4 text-slate-400" />
                                        )}
                                      </div>
                                      <div className="min-w-0">
                                        <p className="truncate text-sm font-semibold text-slate-950">{appearanceForm.portalTitle}</p>
                                <p className="truncate text-xs text-muted-foreground">portail-association973.com</p>
                                      </div>
                                    </div>
                                  </div>
                                  <div className="mt-4 grid gap-2 sm:grid-cols-3">
                                    <div className="rounded-xl border border-slate-200 bg-white px-3 py-2 text-center text-xs text-slate-600">Onglet</div>
                                    <div className="rounded-xl border border-slate-200 bg-white px-3 py-2 text-center text-xs text-slate-600">Favori</div>
                                    <div className="rounded-xl border border-slate-200 bg-white px-3 py-2 text-center text-xs text-slate-600">Raccourci</div>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </AdminSectionCollapsible>
                          <AdminSectionCollapsible
                            title="Habillage métier"
                            description="Fond immersif global"
                            open={!adminCollapsedSections.appearanceBackground}
                            onOpenChange={(open) => setAdminSectionExpanded("appearanceBackground", open)}
                            className="border-slate-200 bg-muted/20"
                            contentClassName="space-y-5"
                            headerBadge={(
                              <Badge variant="outline" className="border-violet-200 bg-violet-50 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-violet-700">
                                {appearanceForm.backgroundEnabled ? "Fond actif" : "Fond inactif"}
                              </Badge>
                            )}
                          >
                            <div className="flex gap-2">
                                <Button type="button" variant="outline" onClick={() => backgroundImageInputRef.current?.click()} disabled={user?.role !== "super_admin" || isAppearanceAssetUploadBusy === "backgroundImageUrl"}>
                                  {isAppearanceAssetUploadBusy === "backgroundImageUrl" ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Upload className="mr-2 h-4 w-4" />}
                                  Importer
                                </Button>
                                <Button type="button" variant="outline" onClick={() => handleAppearanceAssetReset("backgroundImageUrl")} disabled={user?.role !== "super_admin"}>
                                  <Trash2 className="mr-2 h-4 w-4" />
                                  Retirer
                                </Button>
                              </div>
                            <div className="mb-5 rounded-2xl border border-slate-200 bg-white/90 p-4 shadow-sm">
                              <div className="space-y-1">
                                <p className="text-sm font-semibold text-slate-950">Guide rapide du fond immersif</p>
                                <p className="text-xs leading-5 text-muted-foreground">
                                  Travaille d’abord le fond de page, puis ajuste seulement ce qui améliore vraiment la lisibilité et le relief visuel.
                                </p>
                              </div>
                              <div className="mt-4 grid gap-3 xl:grid-cols-3">
                                <div className="rounded-xl border border-slate-200 bg-slate-50/70 px-4 py-3">
                                  <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">1. Portée</p>
                                  <p className="mt-2 text-sm font-semibold text-slate-950">Global, univers ou page</p>
                                  <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                    Commence par un fond global si tu veux aller vite. Passe ensuite en mode individuel pour cibler une catégorie entière ou une page précise.
                                  </p>
                                </div>
                                <div className="rounded-xl border border-slate-200 bg-slate-50/70 px-4 py-3">
                                  <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">2. Lisibilité</p>
                                  <p className="mt-2 text-sm font-semibold text-slate-950">Voile, flou et position</p>
                                  <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                    Pour un fond beau et clair, vise en général un voile entre 0% et 18%, un flou entre 0px et 2px, puis ajuste la position pour dégager le centre.
                                  </p>
                                </div>
                                <div className="rounded-xl border border-slate-200 bg-slate-50/70 px-4 py-3">
                                  <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">3. Relief</p>
                                  <p className="mt-2 text-sm font-semibold text-slate-950">Décor séparé du fond</p>
                                  <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                    Le fond habille la page. Le décor ajoute un accent sur une card. Ce sont deux couches différentes pour garder un rendu premium et lisible.
                                  </p>
                                </div>
                              </div>
                              <div className="mt-4 grid gap-3 xl:grid-cols-2">
                                <div className="rounded-xl border border-emerald-200 bg-emerald-50/70 px-4 py-3">
                                  <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-emerald-700">Fond recommandé</p>
                                  <p className="mt-2 text-sm font-semibold text-emerald-950">WebP 3200×1800 ou 2560×1440 minimum</p>
                                  <p className="mt-1 text-xs leading-5 text-emerald-900/80">
                                    Centre propre, bords plus riches, aucun texte dans l’image, aucun logo et pas de sujet massif dans la zone centrale.
                                  </p>
                                </div>
                                <div className="rounded-xl border border-violet-200 bg-violet-50/70 px-4 py-3">
                                  <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-violet-700">Décor recommandé</p>
                                  <p className="mt-2 text-sm font-semibold text-violet-950">PNG, WebP ou SVG léger avec transparence</p>
                                  <p className="mt-1 text-xs leading-5 text-violet-900/80">
                                    Pour le relief de card, privilégie un visuel transparent, discret, posé sur un coin sûr et jamais sur le texte, les badges ou les boutons.
                                  </p>
                                </div>
                              </div>
                            </div>
                            <div className="mt-5 rounded-xl border border-slate-200 bg-white/90 p-5">
                              <div className="flex flex-col gap-5">
                                <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                                  <div>
                                    <p className="text-sm font-semibold text-slate-950">Activation du fond</p>
                                    <p className="text-sm text-muted-foreground">
                                      L’image habille le fond, les cartes restent opaques et lisibles.
                                    </p>
                                  </div>
                                  <div className="flex items-center gap-3">
                                    <span className="text-sm text-muted-foreground">
                                      {appearanceForm.backgroundEnabled ? "Skin actif" : "Skin inactif"}
                                    </span>
                                    <Switch
                                      checked={appearanceForm.backgroundEnabled}
                                      disabled={!appearanceForm.backgroundImageUrl}
                                      onCheckedChange={(checked) =>
                                        setAppearanceForm((prev) => ({
                                          ...prev,
                                          activeSkinId: "custom",
                                          backgroundEnabled: checked,
                                        }))
                                      }
                                    />
                                  </div>
                                </div>

                                <div className="grid gap-4 2xl:grid-cols-2">
                                  <div className="space-y-2">
                                    <Label>Affichage</Label>
                                    <Select
                                      value={appearanceForm.backgroundMode}
                                      onValueChange={(value: AppearanceBackgroundMode) =>
                                        setAppearanceForm((prev) => ({
                                          ...prev,
                                          activeSkinId: "custom",
                                          backgroundMode: value,
                                        }))
                                      }
                                    >
                                      <SelectTrigger className="h-11">
                                        <SelectValue />
                                      </SelectTrigger>
                                      <SelectContent>
                                        {appearanceBackgroundModes.map((mode) => (
                                          <SelectItem key={mode} value={mode}>
                                            {appearanceBackgroundModeLabels[mode]}
                                          </SelectItem>
                                        ))}
                                      </SelectContent>
                                    </Select>
                                    <p className="text-xs leading-5 text-muted-foreground">
                                      {appearanceForm.backgroundMode === "cover"
                                        ? "Couvre l’écran."
                                        : appearanceForm.backgroundMode === "repeat"
                                          ? "Répète la texture."
                                          : "Reste fixe au scroll."}
                                    </p>
                                  </div>

                                  <div className="space-y-2">
                                    <Label>Mode par défaut</Label>
                                    <Select
                                      value={appearanceForm.backgroundPresentationMode}
                                      onValueChange={(value: AppearanceBackgroundPresentationMode) =>
                                        setAppearanceForm((prev) => ({
                                          ...prev,
                                          activeSkinId: "custom",
                                          backgroundPresentationMode: value,
                                        }))
                                      }
                                    >
                                      <SelectTrigger className="h-11">
                                        <SelectValue />
                                      </SelectTrigger>
                                      <SelectContent>
                                        {appearanceBackgroundPresentationModes.map((mode) => (
                                          <SelectItem key={mode} value={mode}>
                                            {appearanceBackgroundPresentationModeLabels[mode]}
                                          </SelectItem>
                                        ))}
                                      </SelectContent>
                                    </Select>
                                    <p className="text-xs leading-5 text-muted-foreground">
                                      {appearanceForm.backgroundPresentationMode === "edge_frame"
                                        ? "Centre neutre, bords plus illustrés."
                                        : "Illustration sur toute la zone."}
                                    </p>
                                  </div>

                                  <div className="space-y-3">
                                    <div className="flex items-center justify-between gap-4">
                                      <div>
                                        <Label>Voile global</Label>
                                        <p className="text-xs leading-5 text-muted-foreground">
                                          Ajuste la lisibilité générale.
                                        </p>
                                      </div>
                                      <span className="text-sm font-medium">{appearanceForm.backgroundOverlayOpacity}%</span>
                                    </div>
                                    <Slider
                                      value={[appearanceForm.backgroundOverlayOpacity]}
                                      min={0}
                                      max={100}
                                      step={1}
                                      onValueChange={(values) =>
                                        setAppearanceForm((prev) => ({
                                          ...prev,
                                          activeSkinId: "custom",
                                          backgroundOverlayOpacity: values[0] ?? prev.backgroundOverlayOpacity,
                                        }))
                                      }
                                    />
                                  </div>

                                  <div className="space-y-3">
                                    <div className="flex items-center justify-between gap-4">
                                      <div>
                                        <Label>Flou global</Label>
                                        <p className="text-xs leading-5 text-muted-foreground">
                                          Adoucit l’image de fond sans troubler les cartes.
                                        </p>
                                      </div>
                                      <span className="text-sm font-medium">{appearanceForm.backgroundBlurRadius}px</span>
                                    </div>
                                    <Slider
                                      value={[appearanceForm.backgroundBlurRadius]}
                                      min={0}
                                      max={24}
                                      step={1}
                                      onValueChange={(values) =>
                                        setAppearanceForm((prev) => ({
                                          ...prev,
                                          activeSkinId: "custom",
                                          backgroundBlurRadius: values[0] ?? prev.backgroundBlurRadius,
                                        }))
                                      }
                                    />
                                  </div>
                                </div>

                                <div className="rounded-2xl border border-slate-200 bg-slate-50/70 p-5">
                                  <div className="grid gap-4 lg:grid-cols-4">
                                    <div className="space-y-1.5">
                                      <p className="text-sm font-semibold text-slate-950">Voile</p>
                                      <p className="text-xs leading-5 text-muted-foreground">
                                        Régule le contraste.
                                      </p>
                                    </div>
                                    <div className="space-y-1.5">
                                      <p className="text-sm font-semibold text-slate-950">Flou</p>
                                      <p className="text-xs leading-5 text-muted-foreground">
                                        Adoucit l’illustration.
                                      </p>
                                    </div>
                                    <div className="space-y-1.5">
                                      <p className="text-sm font-semibold text-slate-950">Position</p>
                                      <p className="text-xs leading-5 text-muted-foreground">
                                        Évite les collisions visuelles.
                                      </p>
                                    </div>
                                    <div className="space-y-1.5">
                                      <p className="text-sm font-semibold text-slate-950">Bords illustrés</p>
                                      <p className="text-xs leading-5 text-muted-foreground">
                                        Centre lisible, bords immersifs.
                                      </p>
                                    </div>
                                  </div>
                                </div>

                                <AdminSectionCollapsible
                                  title="Relief visuel"
                                  description="Studio de décors immersifs: distingue clairement le fond du portail et les ornements de card."
                                  open={!adminCollapsedSections.appearanceDecor}
                                  onOpenChange={(open) => setAdminSectionExpanded("appearanceDecor", open)}
                                  className="border-slate-200 bg-white"
                                  contentClassName="space-y-4"
                                  headerBadge={(
                                    <Badge variant="outline" className="border-amber-200 bg-amber-50 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.14em] text-amber-700">
                                      {(appearanceForm.decorativeElements || []).length} décor(s)
                                    </Badge>
                                  )}
                                >
                                  <div className="flex justify-end">
                                    <Button
                                      type="button"
                                      variant="outline"
                                      className="h-11 min-w-[168px]"
                                      onClick={handleAddDecorativeElement}
                                      disabled={user?.role !== "super_admin" || (appearanceForm.decorativeElements?.length || 0) >= 6}
                                    >
                                      <ImagePlus className="mr-2 h-4 w-4" />
                                      Ajouter un décor
                                    </Button>
                                  </div>

                                  <div className="mt-4 rounded-xl border border-slate-200 bg-slate-50/70 p-4">
                                    <div className="mb-3 grid gap-3 lg:grid-cols-2">
                                      <div className="rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm">
                                        <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Aide simple</p>
                                        <p className="mt-2 text-sm font-semibold text-slate-950">Fond = page</p>
                                        <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                          Le fond habille une page, une catégorie ou un univers complet. Il pose l’ambiance générale sans passer sur le contenu.
                                        </p>
                                      </div>
                                      <div className="rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm">
                                        <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Aide simple</p>
                                        <p className="mt-2 text-sm font-semibold text-slate-950">Décor = angle de card</p>
                                        <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                          Le décor vient souligner une card sur un coin sûr, devant la card si besoin, sans passer sur le texte, les boutons ni les badges.
                                        </p>
                                      </div>
                                    </div>
                                    <div className="grid gap-3 lg:grid-cols-3">
                                      <div>
                                        <p className="text-sm font-semibold text-slate-950">Fond clair</p>
                                        <p className="text-xs leading-5 text-muted-foreground">Utilise un WebP 16:9 propre au centre.</p>
                                      </div>
                                      <div>
                                        <p className="text-sm font-semibold text-slate-950">Relief de card</p>
                                        <p className="text-xs leading-5 text-muted-foreground">Pour les cards, vise un coin sûr, 8% à 14% d’opacité et un visuel transparent léger.</p>
                                      </div>
                                      <div>
                                        <p className="text-sm font-semibold text-slate-950">Placement utile</p>
                                        <p className="text-xs leading-5 text-muted-foreground">Un fond habille la zone. Un ornement de card se pose sur un angle, jamais sur le contenu métier.</p>
                                      </div>
                                    </div>
                                  </div>

                                  <div className="space-y-4">
                                    {(appearanceForm.decorativeElements || []).length === 0 ? (
                                      <div className="rounded-xl border border-dashed border-slate-200 bg-slate-50 px-4 py-6">
                                        <p className="text-sm font-medium text-slate-700">Aucun élément décoratif pour le moment.</p>
                                        <p className="mt-1 text-sm text-muted-foreground">
                                          Ajoute un SVG, PNG ou WebP léger pour créer un relief de card ou habiller les bords du portail. Le JPG reste possible, mais il est surtout utile pour un fond de page.
                                        </p>
                                      </div>
                                    ) : (
                                      (appearanceForm.decorativeElements || []).map((decoration, index) => (
                                        <div key={decoration.id} className="rounded-2xl border border-slate-200 bg-slate-50/60 p-4">
                                          <div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
                                            <div className="space-y-1">
                                              <p className="text-sm font-semibold text-slate-950">Décor {index + 1}</p>
                                              <p className="text-xs text-muted-foreground">
                                                {appearanceDecorationLayerLabels[decoration.layer]} · {appearanceDecorationScopeLabels[decoration.scope]} · {decoration.opacity}% d’opacité
                                              </p>
                                            </div>
                                            <div className="flex flex-wrap gap-2">
                                              <Button
                                                type="button"
                                                size="sm"
                                                variant="outline"
                                                className="h-9"
                                                onClick={() => {
                                                  setPendingDecorationUploadId(decoration.id);
                                                  decorationImageInputRef.current?.click();
                                                }}
                                                disabled={user?.role !== "super_admin" || isAppearanceDecorationUploadBusy === decoration.id}
                                              >
                                                {isAppearanceDecorationUploadBusy === decoration.id ? (
                                                  <Loader2 className="mr-2 h-3.5 w-3.5 animate-spin" />
                                                ) : (
                                                  <Upload className="mr-2 h-3.5 w-3.5" />
                                                )}
                                                Importer
                                              </Button>
                                              <Button
                                                type="button"
                                                size="sm"
                                                variant="outline"
                                                className="h-9"
                                                onClick={() => updateDecorativeElement(decoration.id, { imageUrl: "" })}
                                                disabled={user?.role !== "super_admin" || !decoration.imageUrl}
                                              >
                                                <Trash2 className="mr-2 h-3.5 w-3.5" />
                                                Retirer l’image
                                              </Button>
                                              <Button
                                                type="button"
                                                size="sm"
                                                variant="outline"
                                                className="h-9"
                                                onClick={() => handleRemoveDecorativeElement(decoration.id)}
                                                disabled={user?.role !== "super_admin"}
                                              >
                                                Supprimer
                                              </Button>
                                            </div>
                                          </div>

                                          <div className="mt-4 grid gap-4 xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]">
                                            <div className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
                                              <div className="flex items-center justify-between gap-3">
                                                <div>
                                                  <p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Studio de prévisualisation</p>
                                                  <p className="mt-1 text-xs text-muted-foreground">Compare le rendu neutre et la version habillée avant d’enregistrer.</p>
                                                </div>
                                                <span className="text-[11px] text-slate-500">Avant / Après</span>
                                              </div>
                                              <div className="mt-4 grid gap-3 md:grid-cols-2">
                                                <div className="rounded-2xl border border-dashed border-slate-200 bg-[linear-gradient(135deg,rgba(241,245,249,0.92),rgba(226,232,240,0.72))] p-3">
                                                  <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">Avant</p>
                                                  <div className="mt-3 flex min-h-[140px] items-center justify-center rounded-xl border border-dashed border-slate-200 bg-white/70 p-4 text-center text-sm text-muted-foreground">
                                                    {decoration.layer === "card" ? "Card neutre" : "Fond seul"}
                                                  </div>
                                                </div>
                                                <div className="rounded-2xl border border-dashed border-slate-200 bg-[linear-gradient(135deg,rgba(241,245,249,0.92),rgba(226,232,240,0.72))] p-3">
                                                  <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">Après</p>
                                                  <div className="mt-3 min-h-[140px] overflow-hidden rounded-xl border border-dashed border-slate-200 bg-white/70 p-4">
                                                    {decoration.layer === "card" ? (
                                                      <div className="flex h-full min-h-[108px] items-center justify-center">
                                                        <div className="relative w-full max-w-[250px] overflow-hidden rounded-2xl border border-slate-200 bg-white px-4 py-4 shadow-sm">
                                                          {decoration.imageUrl ? (
                                                            <img
                                                              src={decoration.imageUrl}
                                                              alt=""
                                                              className={cn(
                                                                "pointer-events-none absolute h-auto max-w-[34%] object-contain",
                                                                decoration.cardAnchor === "top_left" && "left-0 top-0 -translate-x-[18%] -translate-y-[18%]",
                                                                decoration.cardAnchor === "top_right" && "right-0 top-0 translate-x-[18%] -translate-y-[18%]",
                                                                decoration.cardAnchor === "bottom_left" && "bottom-0 left-0 -translate-x-[18%] translate-y-[18%]",
                                                                decoration.cardAnchor === "bottom_right" && "bottom-0 right-0 translate-x-[18%] translate-y-[18%]",
                                                              )}
                                                              style={{
                                                                margin: `${decoration.cardInsetPercent}%`,
                                                                opacity: decoration.opacity / 100,
                                                                filter: `blur(${decoration.blurRadius}px)`,
                                                                width: `${Math.min(decoration.widthPercent * 1.8, 34)}%`,
                                                              }}
                                                            />
                                                          ) : null}
                                                          <div className="relative z-10 space-y-2">
                                                            <p className="text-sm font-semibold text-slate-950">Card exemple</p>
                                                            <p className="text-xs leading-5 text-muted-foreground">
                                                              Le décor reste sur l’angle pendant que le texte et les actions gardent la priorité.
                                                            </p>
                                                            <div className="inline-flex rounded-full border border-slate-200 bg-slate-50 px-2.5 py-1 text-[11px] font-medium text-slate-700">
                                                              Action principale
                                                            </div>
                                                          </div>
                                                        </div>
                                                      </div>
                                                    ) : decoration.imageUrl ? (
                                                      <div className="flex h-full items-center justify-center">
                                                        <img
                                                          src={decoration.imageUrl}
                                                          alt=""
                                                          className="max-h-28 w-auto object-contain"
                                                          style={{ opacity: decoration.opacity / 100, filter: `blur(${decoration.blurRadius}px)` }}
                                                        />
                                                      </div>
                                                    ) : (
                                                      <div className="flex h-full min-h-[108px] items-center justify-center text-center text-sm text-muted-foreground">
                                                        Importe une illustration légère
                                                      </div>
                                                    )}
                                                  </div>
                                                </div>
                                              </div>
                                            </div>

                                            <div className="grid gap-4 rounded-xl border border-slate-200 bg-white p-4 shadow-sm md:grid-cols-2">
                                              <div className="space-y-2 md:col-span-2">
                                                <Label>Preset rapide</Label>
                                                <div className="grid gap-2 xl:grid-cols-2">
                                                  {appearanceDecorationPresets.map((preset) => (
                                                    <button
                                                      key={preset.id}
                                                      type="button"
                                                      onClick={() => applyDecorativePreset(decoration.id, preset.patch)}
                                                      className="rounded-xl border border-slate-200 bg-slate-50 px-3 py-3 text-left transition hover:border-slate-300 hover:bg-slate-100"
                                                    >
                                                      <p className="text-sm font-semibold text-slate-950">{preset.label}</p>
                                                      <p className="mt-1 text-xs leading-5 text-muted-foreground">{preset.description}</p>
                                                    </button>
                                                  ))}
                                                </div>
                                              </div>

                                              <div className="space-y-2">
                                                <Label>Plan du décor</Label>
                                                <Select
                                                  value={decoration.layer}
                                                  onValueChange={(value: AppearanceDecorationLayer) =>
                                                    updateDecorativeElement(decoration.id, { layer: value })
                                                  }
                                                >
                                                  <SelectTrigger className="h-10">
                                                    <SelectValue />
                                                  </SelectTrigger>
                                                  <SelectContent>
                                                    {appearanceDecorationLayers.map((layer) => (
                                                      <SelectItem key={layer} value={layer}>
                                                        {appearanceDecorationLayerLabels[layer]}
                                                      </SelectItem>
                                                    ))}
                                                  </SelectContent>
                                                </Select>
                                                <p className="text-xs leading-5 text-muted-foreground">
                                                  {decoration.layer === "card"
                                                    ? "Mode relief: le visuel vient se poser sur un angle de card."
                                                    : "Mode fond: le visuel habille la page ou la catégorie choisie."}
                                                </p>
                                                <div className="rounded-lg border border-slate-200 bg-slate-50/70 px-3 py-2">
                                                  <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">
                                                    {decoration.layer === "card" ? "Relief de card" : "Fond de page"}
                                                  </p>
                                                  <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                                    {decoration.layer === "card"
                                                      ? "Privilégie un PNG, WebP ou SVG transparent, léger et discret."
                                                      : "Privilégie un grand visuel panoramique, net au centre et plus riche sur les bords."}
                                                  </p>
                                                </div>
                                              </div>

                                              <div className="space-y-2">
                                                <Label>Zone cible</Label>
                                                <Select
                                                  value={decoration.scope}
                                                  onValueChange={(value: AppearanceDecorationScope) =>
                                                    updateDecorativeElement(decoration.id, { scope: value })
                                                  }
                                                >
                                                  <SelectTrigger className="h-10">
                                                    <SelectValue />
                                                  </SelectTrigger>
                                                  <SelectContent>
                                                    {appearanceDecorationScopes.map((scope) => (
                                                      <SelectItem key={scope} value={scope}>
                                                        {appearanceDecorationScopeLabels[scope]}
                                                      </SelectItem>
                                                    ))}
                                                  </SelectContent>
                                                </Select>
                                                <p className="text-xs leading-5 text-muted-foreground">
                                                  Choisis ici la page exacte, ou un univers plus large si tu veux réutiliser le même décor.
                                                </p>
                                              </div>

                                              {decoration.layer === "card" ? (
                                                <>
                                                  <div className="space-y-2">
                                                    <Label>Coin de la card</Label>
                                                    <Select
                                                      value={decoration.cardAnchor}
                                                      onValueChange={(value: AppearanceCardDecorationAnchor) =>
                                                        updateDecorativeElement(decoration.id, { cardAnchor: value })
                                                      }
                                                    >
                                                      <SelectTrigger className="h-10">
                                                        <SelectValue />
                                                      </SelectTrigger>
                                                      <SelectContent>
                                                        {appearanceCardDecorationAnchors.map((anchor) => (
                                                          <SelectItem key={anchor} value={anchor}>
                                                            {appearanceCardDecorationAnchorLabels[anchor]}
                                                          </SelectItem>
                                                        ))}
                                                      </SelectContent>
                                                    </Select>
                                                    <p className="text-xs leading-5 text-muted-foreground">
                                                      Ancre le décor sur un coin sûr de la card pour créer un relief sans gêner le contenu.
                                                    </p>
                                                  </div>
                                                  <div className="space-y-3">
                                                    <div className="flex items-center justify-between">
                                                      <Label>Zone sûre</Label>
                                                      <span className="text-xs font-semibold text-slate-700">{decoration.cardInsetPercent}%</span>
                                                    </div>
                                                    <Slider value={[decoration.cardInsetPercent]} min={0} max={20} step={1} onValueChange={(values) => updateDecorativeElement(decoration.id, { cardInsetPercent: values[0] ?? decoration.cardInsetPercent })} />
                                                    <p className="text-xs leading-5 text-muted-foreground">
                                                      Plus la zone sûre est élevée, plus le décor reste éloigné du texte et des actions.
                                                    </p>
                                                  </div>
                                                </>
                                              ) : null}

                                              <div className="space-y-3">
                                                <div className="flex items-center justify-between">
                                                  <Label>{decoration.layer === "card" ? "Taille du décor" : "Largeur"}</Label>
                                                  <span className="text-xs font-semibold text-slate-700">{decoration.widthPercent}%</span>
                                                </div>
                                                <Slider value={[decoration.widthPercent]} min={6} max={38} step={1} onValueChange={(values) => updateDecorativeElement(decoration.id, { widthPercent: values[0] ?? decoration.widthPercent })} />
                                              </div>
                                              <div className="space-y-3">
                                                <div className="flex items-center justify-between">
                                                  <Label>Opacité</Label>
                                                  <span className="text-xs font-semibold text-slate-700">{decoration.opacity}%</span>
                                                </div>
                                                <Slider value={[decoration.opacity]} min={4} max={28} step={1} onValueChange={(values) => updateDecorativeElement(decoration.id, { opacity: values[0] ?? decoration.opacity })} />
                                              </div>

                                              {decoration.layer === "background" ? (
                                                <>
                                                  <div className="space-y-3">
                                                    <div className="flex items-center justify-between">
                                                      <Label>Position verticale</Label>
                                                      <span className="text-xs font-semibold text-slate-700">{decoration.topPercent}%</span>
                                                    </div>
                                                    <Slider value={[decoration.topPercent]} min={0} max={100} step={1} onValueChange={(values) => updateDecorativeElement(decoration.id, { topPercent: values[0] ?? decoration.topPercent })} />
                                                  </div>
                                                  <div className="space-y-3">
                                                    <div className="flex items-center justify-between">
                                                      <Label>Position horizontale</Label>
                                                      <span className="text-xs font-semibold text-slate-700">{decoration.leftPercent}%</span>
                                                    </div>
                                                    <Slider value={[decoration.leftPercent]} min={0} max={100} step={1} onValueChange={(values) => updateDecorativeElement(decoration.id, { leftPercent: values[0] ?? decoration.leftPercent })} />
                                                  </div>
                                                </>
                                              ) : null}

                                              <div className="space-y-3 md:col-span-2">
                                                <div className="flex items-center justify-between">
                                                  <Label>Flou léger</Label>
                                                  <span className="text-xs font-semibold text-slate-700">{decoration.blurRadius}px</span>
                                                </div>
                                                <Slider value={[decoration.blurRadius]} min={0} max={12} step={1} onValueChange={(values) => updateDecorativeElement(decoration.id, { blurRadius: values[0] ?? decoration.blurRadius })} />
                                              </div>
                                            </div>
                                          </div>
                                        </div>
                                      ))
                                    )}
                                  </div>
                                </AdminSectionCollapsible>

                                <div className="space-y-4">
                                  <div className="space-y-1.5">
                                    <Label>Mode d’application du fond</Label>
                                      <p className="text-sm text-muted-foreground">
                                        Choisis si tu veux un fond commun partout, ou une personnalisation plus fine par univers métier et par page.
                                      </p>
                                  </div>
                                  <div className="flex flex-col gap-3 rounded-xl border border-slate-200 bg-slate-50/80 px-4 py-3 lg:flex-row lg:items-center lg:justify-between">
                                    <div className="space-y-1">
                                      <p className="text-sm font-medium text-slate-950">
                                        {isBackgroundTargetingAll ? "Fond commun sur tout le portail" : "Mode individuel par catégorie"}
                                      </p>
                                      <p className="text-xs text-muted-foreground">
                                        {isBackgroundTargetingAll
                                          ? `Le même fond s’appliquera partout sur ${appearanceBackgroundTargets.length} zones du portail.`
                                          : `Tu peux cibler ${selectedBackgroundTargetCount} zone(s) et donner un fond propre à chaque catégorie, parcours ou écran métier.`}
                                      </p>
                                    </div>
                                    <div className="flex items-center gap-3">
                                      <button
                                        type="button"
                                        onClick={() =>
                                          setAppearanceForm((prev) => ({
                                            ...prev,
                                            activeSkinId: "custom",
                                            backgroundTargets: manualAppearanceBackgroundTargets,
                                          }))
                                        }
                                        className={cn(
                                          "rounded-full px-2 py-1 text-xs font-medium uppercase tracking-[0.18em] transition-colors",
                                          isBackgroundTargetingAll ? "text-slate-500 hover:text-slate-700" : "bg-white text-slate-900 shadow-sm"
                                        )}
                                      >
                                        Individuel
                                      </button>
                                      <Switch
                                        checked={isBackgroundTargetingAll}
                                        onCheckedChange={(checked) =>
                                          setAppearanceForm((prev) => {
                                            if (checked) {
                                              setManualAppearanceBackgroundTargets(resolveManualBackgroundTargets(prev.backgroundTargets));
                                            }

                                            return {
                                              ...prev,
                                              activeSkinId: "custom",
                                              backgroundTargets: checked
                                                ? [...appearanceBackgroundTargets]
                                                : manualAppearanceBackgroundTargets,
                                            };
                                          })
                                        }
                                      />
                                      <button
                                        type="button"
                                        onClick={() =>
                                          setAppearanceForm((prev) => {
                                            setManualAppearanceBackgroundTargets(resolveManualBackgroundTargets(prev.backgroundTargets));
                                            return {
                                              ...prev,
                                              activeSkinId: "custom",
                                              backgroundTargets: [...appearanceBackgroundTargets],
                                            };
                                          })
                                        }
                                        className={cn(
                                          "rounded-full px-2 py-1 text-xs font-medium uppercase tracking-[0.18em] transition-colors",
                                          isBackgroundTargetingAll ? "bg-white text-slate-900 shadow-sm" : "text-slate-700 hover:text-slate-900"
                                        )}
                                      >
                                        Global
                                      </button>
                                    </div>
                                  </div>
                                  <div className="grid gap-3 xl:grid-cols-3">
                                    <div className="rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm">
                                      <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Fond global</p>
                                      <p className="mt-2 text-sm font-semibold text-slate-950">Un seul fond pour tout le portail</p>
                                      <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                        Pratique pour poser rapidement une ambiance cohérente sur toutes les pages.
                                      </p>
                                    </div>
                                    <div className="rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm">
                                      <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Fond par catégorie</p>
                                      <p className="mt-2 text-sm font-semibold text-slate-950">Un fond par univers métier</p>
                                      <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                        Public, association ou administration peuvent partager chacun leur propre ambiance visuelle.
                                      </p>
                                    </div>
                                    <div className="rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm">
                                      <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Fond par page</p>
                                      <p className="mt-2 text-sm font-semibold text-slate-950">Réglage écran par écran</p>
                                      <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                        Utilise l’import dédié dans chaque carte ci-dessous pour habiller une page précise comme Mes documents, Bordereau ou Conformité.
                                      </p>
                                    </div>
                                  </div>
                                  {!isBackgroundTargetingAll ? (
                                    <div className="rounded-xl border border-emerald-200 bg-emerald-50/70 px-4 py-3">
                                      <p className="text-sm font-semibold text-emerald-900">Chaque carte peut recevoir son propre fond</p>
                                      <p className="mt-1 text-xs leading-5 text-emerald-800/90">
                                        Utilise l’import dédié dans chaque carte ci-dessous pour appliquer une image différente à l’accueil, à l’annuaire, au tableau de bord association, à la logistique ou à la conformité.
                                      </p>
                                    </div>
                                  ) : null}
                                  <div className="space-y-5">
                                    {appearanceBackgroundTargetGroups.map((group) => (
                                      <Collapsible
                                        key={group.id}
                                        open={!appearanceCollapsedTargetGroups[group.id]}
                                        onOpenChange={(open) =>
                                          setAppearanceCollapsedTargetGroups((prev) => ({
                                            ...prev,
                                            [group.id]: !open,
                                          }))
                                        }
                                        className="rounded-2xl border border-slate-200 bg-slate-50/70"
                                      >
                                        <div className="flex items-start justify-between gap-4 px-4 py-4">
                                          <div className="space-y-1">
                                            <p className="text-sm font-semibold text-slate-950">{group.label}</p>
                                            <p className="text-xs text-muted-foreground">{group.description}</p>
                                          </div>
                                          <CollapsibleTrigger asChild>
                                            <button
                                              type="button"
                                              className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-slate-200 bg-white text-slate-600 transition hover:bg-slate-50"
                                              aria-label={
                                                appearanceCollapsedTargetGroups[group.id]
                                                  ? `Déplier ${group.label.toLowerCase()}`
                                                  : `Replier ${group.label.toLowerCase()}`
                                              }
                                            >
                                              <ChevronDown
                                                className={cn(
                                                  "h-4 w-4 transition-transform duration-200",
                                                  appearanceCollapsedTargetGroups[group.id] ? "-rotate-90" : "rotate-0"
                                                )}
                                              />
                                            </button>
                                          </CollapsibleTrigger>
                                        </div>
                                        <CollapsibleContent className="border-t border-slate-200 px-4 pb-5 pt-5">
                                          <div className="grid gap-4 xl:grid-cols-2">
                                            {group.targets.map((target) => {
                                              const checked = appearanceForm.backgroundTargets.includes(target);
                                              const meta = backgroundTargetPreviewMeta[target];
                                              const targetImageUrl = appearanceForm.backgroundTargetImageUrls?.[target] || "";
                                              return (
                                                <label
                                                  key={target}
                                                  className="rounded-xl border border-slate-200 bg-white px-4 py-4 shadow-sm transition-shadow hover:shadow-md"
                                                >
                                                  <div className="flex items-start gap-3">
                                                    <Checkbox
                                                      className="mt-0.5"
                                                      checked={checked}
                                                      onCheckedChange={(nextChecked) =>
                                                        setAppearanceForm((prev) => {
                                                          const nextTargets = nextChecked
                                                            ? Array.from(new Set([...prev.backgroundTargets, target]))
                                                            : prev.backgroundTargets.filter((entry) => entry !== target);
                                                          setManualAppearanceBackgroundTargets(resolveManualBackgroundTargets(nextTargets));
                                                          return {
                                                            ...prev,
                                                            activeSkinId: "custom",
                                                            backgroundTargets: nextTargets,
                                                          };
                                                        })
                                                      }
                                                      disabled={isBackgroundTargetingAll}
                                                    />
                                                    <div className="min-w-0 flex-1 space-y-3">
                                                      <div className="space-y-1.5">
                                                        <div className="flex flex-wrap items-start justify-between gap-2">
                                                          <p className="text-sm font-semibold text-slate-950">
                                                            {appearanceBackgroundTargetLabels[target]}
                                                          </p>
                                                          <Badge
                                                            variant="outline"
                                                            className={cn(
                                                              "shrink-0 border-slate-200",
                                                              checked ? "bg-emerald-50 text-emerald-700" : "bg-slate-50 text-slate-600"
                                                            )}
                                                          >
                                                            {checked ? "Active" : "Inactive"}
                                                          </Badge>
                                                        </div>
                                                        <p className="text-xs leading-5 text-muted-foreground">{meta.detail}</p>
                                                      </div>

                                                      <div className="flex flex-wrap items-center gap-2">
                                                        <Badge
                                                          variant="outline"
                                                          className={cn(
                                                            "border-slate-200",
                                                            targetImageUrl ? "bg-violet-50 text-violet-700" : "bg-slate-50 text-slate-600"
                                                          )}
                                                        >
                                                          {targetImageUrl ? "Fond individuel" : "Hérite du fond global"}
                                                        </Badge>
                                                        <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                                          Voile {resolveAppearanceTargetOverlay(target)}%
                                                        </Badge>
                                                        <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                                          Flou {resolveAppearanceTargetBlur(target)}px
                                                        </Badge>
                                                        <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                                          {resolveAppearanceTargetPresentationMode(target) === "edge_frame" ? "Bords illustrés" : "Habillage complet"}
                                                        </Badge>
                                                      </div>

                                                      <div className="grid gap-3 rounded-xl border border-slate-200 bg-slate-50/70 p-3 xl:grid-cols-2 2xl:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_minmax(0,0.95fr)_minmax(0,0.95fr)]">
                                                        <div className="space-y-2">
                                                          <div className="flex items-center justify-between gap-3">
                                                            <Label className="text-[11px] uppercase tracking-[0.16em] text-slate-500">Voile</Label>
                                                            <span className="text-xs font-semibold text-slate-700">{resolveAppearanceTargetOverlay(target)}%</span>
                                                          </div>
                                                          <Slider
                                                            value={[resolveAppearanceTargetOverlay(target)]}
                                                            min={0}
                                                            max={100}
                                                            step={1}
                                                            onValueChange={(values) =>
                                                              setAppearanceForm((prev) => ({
                                                                ...prev,
                                                                activeSkinId: "custom",
                                                                backgroundTargetOverlayOpacities: {
                                                                  ...(prev.backgroundTargetOverlayOpacities || {}),
                                                                  [target]: values[0] ?? resolveAppearanceTargetOverlay(target),
                                                                },
                                                              }))
                                                            }
                                                          />
                                                        </div>

                                                        <div className="space-y-2">
                                                          <div className="flex items-center justify-between gap-3">
                                                            <Label className="text-[11px] uppercase tracking-[0.16em] text-slate-500">Flou</Label>
                                                            <span className="text-xs font-semibold text-slate-700">{resolveAppearanceTargetBlur(target)}px</span>
                                                          </div>
                                                          <Slider
                                                            value={[resolveAppearanceTargetBlur(target)]}
                                                            min={0}
                                                            max={24}
                                                            step={1}
                                                            onValueChange={(values) =>
                                                              setAppearanceForm((prev) => ({
                                                                ...prev,
                                                                activeSkinId: "custom",
                                                                backgroundTargetBlurRadii: {
                                                                  ...(prev.backgroundTargetBlurRadii || {}),
                                                                  [target]: values[0] ?? resolveAppearanceTargetBlur(target),
                                                                },
                                                              }))
                                                            }
                                                          />
                                                        </div>

                                                        <div className="space-y-2">
                                                          <Label className="text-[11px] uppercase tracking-[0.16em] text-slate-500">Position</Label>
                                                          <Select
                                                            value={resolveAppearanceTargetPosition(target)}
                                                            onValueChange={(value: AppearanceBackgroundPosition) =>
                                                              setAppearanceForm((prev) => ({
                                                                ...prev,
                                                                activeSkinId: "custom",
                                                                backgroundTargetPositions: {
                                                                  ...(prev.backgroundTargetPositions || {}),
                                                                  [target]: value,
                                                                },
                                                              }))
                                                            }
                                                          >
                                                            <SelectTrigger className="h-9 bg-white">
                                                              <SelectValue />
                                                            </SelectTrigger>
                                                            <SelectContent>
                                                              {appearanceBackgroundPositions.map((position) => (
                                                                <SelectItem key={position} value={position}>
                                                                  {appearanceBackgroundPositionLabels[position]}
                                                                </SelectItem>
                                                              ))}
                                                            </SelectContent>
                                                          </Select>
                                                        </div>

                                                        <div className="space-y-2">
                                                          <Label className="text-[11px] uppercase tracking-[0.16em] text-slate-500">Mode</Label>
                                                          <Select
                                                            value={resolveAppearanceTargetPresentationMode(target)}
                                                            onValueChange={(value: AppearanceBackgroundPresentationMode) =>
                                                              setAppearanceForm((prev) => ({
                                                                ...prev,
                                                                activeSkinId: "custom",
                                                                backgroundTargetPresentationModes: {
                                                                  ...(prev.backgroundTargetPresentationModes || {}),
                                                                  [target]: value,
                                                                },
                                                              }))
                                                            }
                                                          >
                                                            <SelectTrigger className="h-9 bg-white">
                                                              <SelectValue />
                                                            </SelectTrigger>
                                                            <SelectContent>
                                                              {appearanceBackgroundPresentationModes.map((mode) => (
                                                                <SelectItem key={mode} value={mode}>
                                                                  {appearanceBackgroundPresentationModeLabels[mode]}
                                                                </SelectItem>
                                                              ))}
                                                            </SelectContent>
                                                          </Select>
                                                        </div>
                                                      </div>

                                                      <div className="grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-end">
                                                        <div className="overflow-hidden rounded-xl border border-slate-200 bg-slate-50">
                                                          {targetImageUrl ? (
                                                            <div
                                                              className="h-20 w-full bg-cover bg-center"
                                                              style={{ backgroundImage: `url(${targetImageUrl})` }}
                                                            />
                                                          ) : (
                                                            <div className="flex h-20 items-center justify-center text-xs text-muted-foreground">
                                                              Aucun fond dédié
                                                            </div>
                                                          )}
                                                        </div>
                                                        <div className="flex flex-wrap items-center gap-2 lg:justify-end">
                                                          <Button
                                                            type="button"
                                                            size="sm"
                                                            variant="outline"
                                                            className="h-9 min-w-[124px] px-3"
                                                            onClick={(event) => {
                                                              event.preventDefault();
                                                              setPendingBackgroundTargetUpload(target);
                                                              backgroundTargetImageInputRef.current?.click();
                                                            }}
                                                            disabled={user?.role !== "super_admin" || isAppearanceTargetAssetUploadBusy === target}
                                                          >
                                                            {isAppearanceTargetAssetUploadBusy === target ? (
                                                              <Loader2 className="mr-2 h-3.5 w-3.5 animate-spin" />
                                                            ) : (
                                                              <ImagePlus className="mr-2 h-3.5 w-3.5" />
                                                            )}
                                                            Importer
                                                          </Button>
                                                          <Button
                                                            type="button"
                                                            size="sm"
                                                            variant="outline"
                                                            className="h-9 min-w-[124px] px-3"
                                                            onClick={(event) => {
                                                              event.preventDefault();
                                                              handleAppearanceTargetAssetReset(target);
                                                            }}
                                                            disabled={user?.role !== "super_admin" || !targetImageUrl}
                                                          >
                                                            <Trash2 className="mr-2 h-3.5 w-3.5" />
                                                            Retirer
                                                          </Button>
                                                        </div>
                                                      </div>
                                                    </div>
                                                  </div>
                                                </label>
                                              );
                                            })}
                                          </div>
                                        </CollapsibleContent>
                                      </Collapsible>
                                    ))}
                                  </div>
                                </div>

                                <div className="space-y-5">
                                  <Collapsible
                                    open={!appearanceCollapsedPanels.beforeAfterPreview}
                                    onOpenChange={(open) =>
                                      setAppearanceCollapsedPanels((prev) => ({
                                        ...prev,
                                        beforeAfterPreview: !open,
                                      }))
                                    }
                                    className="rounded-2xl border border-slate-200 bg-slate-50/60"
                                  >
                                    <div className="flex items-start justify-between gap-4 px-4 py-4">
                                      <div className="space-y-1">
                                        <p className="text-sm font-semibold text-slate-950">Aperçu avant / après</p>
                                        <p className="text-xs text-muted-foreground">Vérifie le rendu avant publication.</p>
                                      </div>
                                      <CollapsibleTrigger asChild>
                                        <button
                                          type="button"
                                          className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-slate-200 bg-white text-slate-600 transition hover:bg-slate-50"
                                          aria-label={appearanceCollapsedPanels.beforeAfterPreview ? "Déplier l’aperçu avant / après" : "Replier l’aperçu avant / après"}
                                        >
                                          <ChevronDown
                                            className={cn(
                                              "h-4 w-4 transition-transform duration-200",
                                              appearanceCollapsedPanels.beforeAfterPreview ? "-rotate-90" : "rotate-0"
                                            )}
                                          />
                                        </button>
                                      </CollapsibleTrigger>
                                    </div>
                                    <CollapsibleContent className="border-t border-slate-200 px-4 pb-5 pt-5">
                                        <div className="grid gap-3 lg:grid-cols-2">
                                          <div className="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
                                            <div className="border-b border-slate-200 px-4 py-3">
                                              <p className="text-sm font-semibold text-slate-950">Avant</p>
                                              <p className="text-xs text-muted-foreground">Rendu sans skin de fond</p>
                                            </div>
                                          <div className="bg-slate-50 p-4">
                                            <div className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
                                              <div className="flex items-center justify-between gap-3">
                                                <div>
                                                  <p className="text-sm font-semibold text-slate-950">Page type</p>
                                                  <p className="text-xs text-slate-500">Fond neutre institutionnel</p>
                                                </div>
                                                <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">Neutre</Badge>
                                              </div>
                                              <div className="mt-4 space-y-2">
                                                <div className="h-3 w-24 rounded-full bg-slate-200" />
                                                <div className="h-3 w-40 rounded-full bg-slate-100" />
                                                <div className="grid gap-2 sm:grid-cols-2">
                                                  <div className="h-20 rounded-lg border border-slate-200 bg-slate-50" />
                                                  <div className="h-20 rounded-lg border border-slate-200 bg-slate-50" />
                                                </div>
                                              </div>
                                            </div>
                                          </div>
                                        </div>

                                        <div className="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
                                          <div className="border-b border-slate-200 px-4 py-3">
                                            <p className="text-sm font-semibold text-slate-950">Après</p>
                                            <p className="text-xs text-muted-foreground">
                                              {appearanceForm.backgroundImageUrl
                                                ? appearanceForm.backgroundEnabled
                                                  ? `Skin actif en mode ${appearanceBackgroundModeLabels[appearanceForm.backgroundMode].toLowerCase()} avec ${appearanceBackgroundPresentationModeLabels[appearanceForm.backgroundPresentationMode].toLowerCase()}`
                                                  : "Image chargée mais skin désactivé"
                                                : "Importe une image pour prévisualiser le rendu"}
                                            </p>
                                          </div>
                                          <div
                                            className="relative overflow-hidden p-4"
                                            style={previewBackgroundStyle}
                                          >
                                            {appearanceForm.backgroundPresentationMode === "edge_frame" ? (
                                              <div className="pointer-events-none absolute inset-4 rounded-[1.35rem] bg-[linear-gradient(90deg,rgba(255,255,255,0.08)_0%,rgba(255,255,255,0.72)_24%,rgba(255,255,255,0.9)_48%,rgba(255,255,255,0.9)_52%,rgba(255,255,255,0.72)_76%,rgba(255,255,255,0.08)_100%)]" />
                                            ) : null}
                                            <div className="relative rounded-[1.35rem] border border-white/60 bg-slate-950/72 p-5 shadow-[0_18px_40px_rgba(15,23,42,0.28)]">
                                              <div className="flex flex-wrap items-start justify-between gap-3 text-white">
                                                <div className="max-w-sm space-y-1.5">
                                                  <Badge className="border-0 bg-white/14 text-[11px] uppercase tracking-[0.18em] text-emerald-100 shadow-none">
                                                    Parcours guidé
                                                  </Badge>
                                                  <p className="text-lg font-semibold leading-tight">
                                                    Une demande plus claire, étape par étape
                                                  </p>
                                                  <p className="text-xs leading-5 text-slate-200/90">
                                                    Le fond habille les bords, le centre reste lisible et les cartes métier gardent la priorité.
                                                  </p>
                                                </div>
                                                <div className="w-full max-w-[13rem] rounded-2xl border border-white/35 bg-white/92 p-3 text-slate-900 shadow-sm">
                                                  <div className="flex items-center justify-between gap-3 text-xs font-medium">
                                                    <span>Progression</span>
                                                    <span>0/3 repères</span>
                                                  </div>
                                                  <div className="mt-2 h-2 rounded-full bg-slate-200">
                                                    <div className="h-2 rounded-full" style={{ width: "34%", backgroundColor: appearanceForm.primaryColor }} />
                                                  </div>
                                                  <p className="mt-2 text-[11px] leading-5 text-slate-600">
                                                    Choisis le bon parcours, puis laisse le portail guider la suite.
                                                  </p>
                                                </div>
                                              </div>
                                              <div className="mt-5 grid gap-3 xl:grid-cols-3">
                                                {[
                                                  {
                                                    title: "Subvention de fonctionnement",
                                                    detail: "Activités courantes de l’association",
                                                  },
                                                  {
                                                    title: "Subvention de projet",
                                                    detail: "Projet ciblé avec étapes adaptées",
                                                  },
                                                  {
                                                    title: "Autorisation d’occupation",
                                                    detail: "Demande plus lisible côté terrain",
                                                  },
                                                ].map((card, index) => (
                                                  <div
                                                    key={card.title}
                                                    className={cn(
                                                      "rounded-2xl border border-slate-200 bg-white/96 p-4 text-slate-900 shadow-sm",
                                                      index === 2 ? "xl:translate-y-4" : ""
                                                    )}
                                                  >
                                                    <div className="flex items-center justify-between gap-3">
                                                      <div
                                                        className="h-10 w-10 rounded-xl"
                                                        style={{ backgroundColor: index === 1 ? `${appearanceForm.secondaryColor}22` : `${appearanceForm.primaryColor}22` }}
                                                      />
                                                      <Badge variant="outline" className="border-slate-200 bg-white text-slate-600">
                                                        Ouvrir
                                                      </Badge>
                                                    </div>
                                                    <p className="mt-4 text-sm font-semibold leading-6">{card.title}</p>
                                                    <p className="mt-1 text-xs leading-5 text-slate-500">{card.detail}</p>
                                                    <div className="mt-4 flex items-center justify-between text-[11px] text-slate-500">
                                                      <span>Formulaire adapté</span>
                                                      <span>Étape suivante</span>
                                                    </div>
                                                  </div>
                                                ))}
                                              </div>
                                              <div className="mt-4 flex flex-wrap gap-2">
                                                <Badge variant="outline" className="border-white/30 bg-white/10 text-white">
                                                  Voile {appearanceForm.backgroundOverlayOpacity}%
                                                </Badge>
                                                <Badge variant="outline" className="border-white/30 bg-white/10 text-white">
                                                  Flou {appearanceForm.backgroundBlurRadius}px
                                                </Badge>
                                                <Badge variant="outline" className="border-white/30 bg-white/10 text-white">
                                                  {appearanceBackgroundPresentationModeLabels[appearanceForm.backgroundPresentationMode]}
                                                </Badge>
                                              </div>
                                            </div>
                                          </div>
                                        </div>
                                      </div>
                                    </CollapsibleContent>
                                  </Collapsible>

                                  <Collapsible
                                    open={!appearanceCollapsedPanels.targetPreview}
                                    onOpenChange={(open) =>
                                      setAppearanceCollapsedPanels((prev) => ({
                                        ...prev,
                                        targetPreview: !open,
                                      }))
                                    }
                                    className="rounded-2xl border border-slate-200 bg-slate-50/60"
                                  >
                                    <div className="flex items-start justify-between gap-4 px-4 py-4">
                                      <div className="space-y-1">
                                        <p className="text-sm font-semibold text-slate-950">Aperçu page par page</p>
                                        <p className="text-xs text-muted-foreground">Compare le rendu neutre et le rendu habillé sur les pages les plus utiles du portail.</p>
                                      </div>
                                      <CollapsibleTrigger asChild>
                                        <button
                                          type="button"
                                          className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-slate-200 bg-white text-slate-600 transition hover:bg-slate-50"
                                          aria-label={appearanceCollapsedPanels.targetPreview ? "Déplier l’aperçu page par page" : "Replier l’aperçu page par page"}
                                        >
                                          <ChevronDown
                                            className={cn(
                                              "h-4 w-4 transition-transform duration-200",
                                              appearanceCollapsedPanels.targetPreview ? "-rotate-90" : "rotate-0"
                                            )}
                                          />
                                        </button>
                                      </CollapsibleTrigger>
                                    </div>
                                  <CollapsibleContent className="border-t border-slate-200 px-4 pb-5 pt-5">
                                      <div className="mb-4 flex flex-wrap gap-2">
                                        {(["all", "public", "association", "admin"] as const).map((family) => {
                                          const isActive = appearancePreviewFamily === family;
                                          return (
                                            <button
                                              key={family}
                                              type="button"
                                              onClick={() => setAppearancePreviewFamily(family)}
                                              className={cn(
                                                "rounded-full border px-3 py-1.5 text-xs font-semibold transition",
                                                isActive
                                                  ? "border-slate-900 bg-slate-900 text-white"
                                                  : "border-slate-200 bg-white text-slate-700 hover:bg-slate-50"
                                              )}
                                            >
                                              {appearancePreviewFamilyLabels[family]}
                                            </button>
                                          );
                                        })}
                                      </div>
                                      <div className="grid gap-3 xl:grid-cols-2">
                                        {filteredBackgroundPreviewTargets.map((target) => {
                                          const isSelected = appearanceForm.backgroundTargets.includes(target);
                                          const meta = backgroundTargetPreviewMeta[target];
                                          const targetImageUrl = resolveAppearanceTargetPreviewImage(target);
                                          const targetPreviewOverlay = `rgba(247, 250, 252, ${resolveAppearanceTargetOverlay(target) / 100})`;
                                          const targetPreviewMode = resolveAppearanceTargetPresentationMode(target);
                                          const targetPreviewStyle =
                                            targetImageUrl && appearanceForm.backgroundEnabled && isSelected
                                              ? {
                                                  backgroundImage: `linear-gradient(${targetPreviewOverlay}, ${targetPreviewOverlay}), url(${targetImageUrl})`,
                                                  backgroundSize: appearanceForm.backgroundMode === "repeat" ? "auto" : "cover",
                                                  backgroundRepeat: appearanceForm.backgroundMode === "repeat" ? "repeat" : "no-repeat",
                                                  backgroundAttachment: appearanceForm.backgroundMode === "fixed" ? "fixed" : "scroll",
                                                  backgroundPosition:
                                                    appearanceForm.backgroundMode === "repeat"
                                                      ? "top left"
                                                      : appearanceBackgroundPositionCssValues[resolveAppearanceTargetPosition(target)],
                                                }
                                              : undefined;

                                          return (
                                            <div
                                              key={target}
                                              className="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm"
                                            >
                                              <div className="flex items-start justify-between gap-3 border-b border-slate-200 px-4 py-3">
                                                <div className="min-w-0">
                                                  <p className={cn("text-sm font-semibold", meta.accent)}>{meta.label}</p>
                                                  <p className="text-xs text-muted-foreground">{meta.detail}</p>
                                                </div>
                                                <Badge
                                                  variant="outline"
                                                  className={cn(
                                                    "shrink-0 border-slate-200",
                                                    isSelected
                                                      ? "bg-emerald-50 text-emerald-700"
                                                      : "bg-slate-50 text-slate-600"
                                                  )}
                                                >
                                                  {isSelected ? "Ciblée" : "Neutre"}
                                                </Badge>
                                              </div>

                                              <div className="relative overflow-hidden p-4" style={targetPreviewStyle}>
                                                {targetPreviewMode === "edge_frame" ? (
                                                  <div className="pointer-events-none absolute inset-4 rounded-[1.15rem] bg-[linear-gradient(90deg,rgba(255,255,255,0.08)_0%,rgba(255,255,255,0.74)_23%,rgba(255,255,255,0.9)_48%,rgba(255,255,255,0.9)_52%,rgba(255,255,255,0.74)_77%,rgba(255,255,255,0.08)_100%)]" />
                                                ) : null}
                                                <div className="grid gap-3 xl:grid-cols-2">
                                                  <div className="rounded-xl border border-dashed border-slate-200 bg-white/88 p-4 shadow-sm">
                                                    <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">Avant</p>
                                                    <div className="mt-3 rounded-lg border border-slate-200 bg-white p-4">
                                                      <div className="space-y-2">
                                                        <div className="h-3 w-24 rounded-full bg-slate-200" />
                                                        <div className="h-3 w-40 rounded-full bg-slate-100" />
                                                      </div>
                                                      <div className="mt-4 grid gap-2 sm:grid-cols-2">
                                                        <div className="h-16 rounded-lg border border-slate-200 bg-slate-50" />
                                                        <div className="h-16 rounded-lg border border-slate-200 bg-slate-50" />
                                                      </div>
                                                    </div>
                                                  </div>
                                                  <div
                                                    className={cn(
                                                      "relative rounded-xl border border-slate-200 p-4 shadow-sm backdrop-blur-[1px]",
                                                      targetPreviewMode === "edge_frame" ? "bg-white/96" : "bg-white/90"
                                                    )}
                                                  >
                                                    <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">Après</p>
                                                  <div
                                                    className={cn(
                                                      "mt-3 inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em]",
                                                      meta.surface,
                                                      meta.accent
                                                    )}
                                                  >
                                                    {meta.label}
                                                  </div>
                                                    <div className="mt-2">
                                                      <Badge variant="outline" className="border-slate-200 bg-white/90 text-slate-700">
                                                        {resolveAppearanceTargetImageSourceLabel(target)}
                                                      </Badge>
                                                    </div>
                                                    <div className="mt-3 flex flex-wrap gap-2">
                                                      <Badge variant="outline" className="border-slate-200 bg-white/90 text-slate-700">
                                                        Voile {resolveAppearanceTargetOverlay(target)}%
                                                      </Badge>
                                                      <Badge variant="outline" className="border-slate-200 bg-white/90 text-slate-700">
                                                        Flou {resolveAppearanceTargetBlur(target)}px
                                                      </Badge>
                                                      <Badge variant="outline" className="border-slate-200 bg-white/90 text-slate-700">
                                                        {appearanceBackgroundPositionLabels[resolveAppearanceTargetPosition(target)]}
                                                      </Badge>
                                                    </div>
                                                    <div className="mt-4 space-y-3">
                                                      <div className="space-y-2">
                                                        <div className="h-3 w-28 rounded-full bg-slate-200" />
                                                        <div className="h-3 w-44 rounded-full bg-slate-100" />
                                                      </div>
                                                      <div className="grid gap-2 sm:grid-cols-2">
                                                        <div className="rounded-lg border border-slate-200 bg-white/84 p-3">
                                                          <div className="flex items-center justify-between gap-2">
                                                            <div className="h-3 w-16 rounded-full bg-slate-200" />
                                                            <div className="h-6 w-14 rounded-full bg-slate-100" />
                                                          </div>
                                                          <div className="mt-3 h-12 rounded-md bg-slate-50" />
                                                        </div>
                                                        <div className="rounded-lg border border-slate-200 bg-white/84 p-3">
                                                          <div className="flex items-center justify-between gap-2">
                                                            <div className="h-3 w-20 rounded-full bg-slate-200" />
                                                            <div className="h-6 w-12 rounded-full bg-slate-100" />
                                                          </div>
                                                          <div className="mt-3 h-12 rounded-md bg-slate-50" />
                                                        </div>
                                                      </div>
                                                      <p className="text-xs text-slate-500">
                                                        {appearanceForm.backgroundEnabled
                                                          ? isSelected
                                                            ? "Fond appliqué sur cette page."
                                                            : "Page laissée neutre."
                                                          : "Fond désactivé."}
                                                      </p>
                                                    </div>
                                                  </div>
                                                </div>
                                              </div>
                                            </div>
                                          );
                                        })}
                                      </div>
                                    </CollapsibleContent>
                                  </Collapsible>
                                </div>
                              </div>
                            </div>
                          </AdminSectionCollapsible>
                        </div>
		                    </AdminSectionCollapsible>
		                    </div>
		                      </AdminSectionCollapsible>

				                    <div id="appearance-hero" className="space-y-5 rounded-2xl border border-slate-200 bg-white/90 p-5 shadow-sm">
                      <div className="space-y-1">
                        <p className="text-base font-semibold text-slate-950">Bannière principale et cadrage</p>
                        <p className="text-sm text-muted-foreground">
                          Régle une seule fois le cadrage de la home. Le même positionnement est ensuite repris sur téléphone, tablette et ordinateur.
                        </p>
                      </div>

                    <div className="grid gap-6 xl:grid-cols-[1.05fr_0.95fr]">
                      <div className="space-y-3">
                        <div className="flex items-start justify-between gap-3">
                          <div>
                            <Label>Bannière principale</Label>
                            <p className="mt-1 text-sm text-muted-foreground">
                              Ajuste le visuel, puis contrôle immédiatement la position du titre et de l’accroche juste à côté des réglages.
                            </p>
                          </div>
                          <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                            Aperçu synchronisé
                          </Badge>
                        </div>
                        <div className="overflow-hidden rounded-[1.5rem] border border-slate-200 bg-white shadow-sm">
                          <div className="relative aspect-[16/10] bg-slate-950">
                            <div
                              className="absolute inset-0 bg-cover bg-no-repeat"
                              style={{
                                backgroundImage: `url(${appearanceForm.heroImageUrl || heroIllustrationUrl})`,
                                backgroundPosition: `${appearanceForm.heroImagePositionX}% ${appearanceForm.heroImagePositionY}%`,
                              }}
                            />
                            <div
                              className="absolute inset-0"
                              style={{
                                background: `linear-gradient(90deg, rgba(10, 17, 40, 0.42) 0%, rgba(10, 17, 40, 0.14) 42%, rgba(10, 17, 40, 0.3) 100%), ${appearanceForm.primaryColor}`,
                                opacity: Math.max(0.45, Math.min(0.92, appearanceForm.heroOverlayOpacity / 100)),
                              }}
                            />
                            <div className="absolute inset-0 z-10">
                              <div
                                className="absolute rounded-xl border border-white/20 bg-white/8 px-3 py-2 shadow-sm backdrop-blur-[1px]"
                                style={{
                                  left: `calc(6% + ${appearanceForm.heroTitlePositionX * 0.62}%)`,
                                  top: `calc(16% + ${appearanceForm.heroTitlePositionY * 0.55}%)`,
                                  width: `${appearanceForm.heroTitleWidthPercent}%`,
                                  maxWidth: "86%",
                                }}
                              >
                                <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-white/70">
                                  Titre du portail
                                </p>
                                <p className="mt-1 break-words text-lg font-semibold leading-tight text-white">
                                  {appearanceForm.portalTitle || "Titre du portail"}
                                </p>
                              </div>
                              <div
                                className="absolute rounded-xl border border-white/15 bg-black/10 px-3 py-2 shadow-sm backdrop-blur-[1px]"
                                style={{
                                  left: `calc(8% + ${appearanceForm.heroTaglinePositionX * 0.62}%)`,
                                  top: `calc(48% + ${appearanceForm.heroTaglinePositionY * 0.55}%)`,
                                  width: `${appearanceForm.heroTaglineWidthPercent}%`,
                                  maxWidth: "88%",
                                }}
                              >
                                <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-white/70">
                                  Accroche
                                </p>
                                <p className="mt-1 break-words text-sm leading-6 text-white/90">
                                  {appearanceForm.portalTagline || "Votre accroche apparaîtra ici dès qu’elle sera saisie."}
                                </p>
                              </div>
                              <div className="absolute bottom-3 right-3 flex flex-wrap gap-2">
                                <Badge className="border-0 bg-white/14 text-[11px] text-white shadow-none">
                                  Titre {appearanceForm.heroTitlePositionX}% / {appearanceForm.heroTitlePositionY}%
                                </Badge>
                                <Badge className="border-0 bg-white/14 text-[11px] text-white shadow-none">
                                  Accroche {appearanceForm.heroTaglinePositionX}% / {appearanceForm.heroTaglinePositionY}%
                                </Badge>
                              </div>
                            </div>
                          </div>
                        </div>
                        <p className="text-sm text-muted-foreground">
                          Image actuelle : {appearanceForm.heroImageUrl ? "personnalisée" : "illustration intégrée"}
                        </p>
                      </div>

                        <div className="space-y-4">
                          <AdminSectionCollapsible
                            title="Réglages de l’image"
                            description="Travaille le visuel d’ensemble avant de régler finement les deux textes."
                            open={!adminCollapsedSections.appearanceHeroImageSettings}
                            onOpenChange={(open) => setAdminSectionExpanded("appearanceHeroImageSettings", open)}
                            className="bg-white"
                            contentClassName="space-y-4 pt-1"
                          >
                            <div className="space-y-2">
                              <Label>Gestion de l’image</Label>
                              <div className="flex flex-wrap gap-2">
                                <Button
                                  type="button"
                                  variant="outline"
                                  onClick={() => heroImageInputRef.current?.click()}
                                  disabled={user?.role !== "super_admin" || isHeroUploadBusy}
                                >
                                  <ImagePlus className="mr-2 h-4 w-4" />
                                  {isHeroUploadBusy ? "Téléversement..." : "Choisir une image"}
                                </Button>
                                <Button
                                  type="button"
                                  variant="outline"
                                  onClick={handleHeroImageReset}
                                  disabled={user?.role !== "super_admin"}
                                >
                                  <Trash2 className="mr-2 h-4 w-4" />
                                  Revenir à l’illustration par défaut
                                </Button>
                              </div>
                              <p className="text-sm text-muted-foreground">
                                Formats recommandés : PNG, JPG, WebP ou SVG, idéalement en panorama.
                              </p>
                            </div>

                            <div className="space-y-3">
                              <div className="flex items-center justify-between gap-4">
                                <div>
                                  <Label>Cadrage horizontal de la bannière</Label>
                                  <p className="text-sm text-muted-foreground">
                                    Déplace l’image de gauche à droite pour mieux équilibrer le visuel et le texte.
                                  </p>
                                </div>
                                <span className="text-sm font-medium">{appearanceForm.heroImagePositionX}%</span>
                              </div>
                              <Slider
                                value={[appearanceForm.heroImagePositionX]}
                                min={0}
                                max={100}
                                step={1}
                                onValueChange={(values) =>
                                  setAppearanceForm(prev => ({ ...prev, heroImagePositionX: values[0] ?? prev.heroImagePositionX }))
                                }
                              />
                              <div className="flex items-center justify-between text-xs text-muted-foreground">
                                <span>0 = gauche</span>
                                <span>50 = centré</span>
                                <span>100 = droite</span>
                              </div>
                            </div>

                            <div className="space-y-3">
                              <div className="flex items-center justify-between gap-4">
                                <div>
                                  <Label>Cadrage vertical de la bannière</Label>
                                  <p className="text-sm text-muted-foreground">
                                    Déplace l’image de haut en bas pour garder le bon élément dans la zone de lecture.
                                  </p>
                                </div>
                                <span className="text-sm font-medium">{appearanceForm.heroImagePositionY}%</span>
                              </div>
                              <Slider
                                value={[appearanceForm.heroImagePositionY]}
                                min={0}
                                max={100}
                                step={1}
                                onValueChange={(values) =>
                                  setAppearanceForm(prev => ({ ...prev, heroImagePositionY: values[0] ?? prev.heroImagePositionY }))
                                }
                              />
                              <div className="flex items-center justify-between text-xs text-muted-foreground">
                                <span>0 = haut</span>
                                <span>50 = centré</span>
                                <span>100 = bas</span>
                              </div>
                            </div>

                            <div className="space-y-3">
                              <div className="flex items-center justify-between gap-4">
                                <div>
                                  <Label>Intensité du voile bleu</Label>
                                  <p className="text-sm text-muted-foreground">
                                    Plus la valeur est élevée, plus l’image reste discrète derrière le texte.
                                  </p>
                                </div>
                                <span className="text-sm font-medium">{appearanceForm.heroOverlayOpacity}%</span>
                              </div>
                              <Slider
                                value={[appearanceForm.heroOverlayOpacity]}
                                min={45}
                                max={92}
                                step={1}
                                onValueChange={(values) =>
                                  setAppearanceForm(prev => ({ ...prev, heroOverlayOpacity: values[0] ?? prev.heroOverlayOpacity }))
                                }
                              />
                            </div>
                          </AdminSectionCollapsible>

                          <AdminSectionCollapsible
                            title="Réglages du titre"
                            description="Ajuste le titre pendant que l’aperçu affiche sa position exacte sur la bannière."
                            open={!adminCollapsedSections.appearanceHeroTitleSettings}
                            onOpenChange={(open) => setAdminSectionExpanded("appearanceHeroTitleSettings", open)}
                            className="bg-white"
                            contentClassName="space-y-3 pt-1"
                          >
                            <div className="space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-4">
                              <div className="flex items-center justify-between gap-4">
                                <div>
                                  <Label>Position horizontale du titre</Label>
                                  <p className="text-sm text-muted-foreground">
                                    Décale le titre (H1) vers la gauche ou la droite.
                                  </p>
                                </div>
                                <span className="text-sm font-medium">{appearanceForm.heroTitlePositionX}%</span>
                              </div>
                              <Slider
                                value={[appearanceForm.heroTitlePositionX]}
                                min={-50}
                                max={50}
                                step={1}
                                onValueChange={(values) =>
                                  setAppearanceForm(prev => ({ ...prev, heroTitlePositionX: values[0] ?? prev.heroTitlePositionX }))
                                }
                              />
                            </div>

                            <div className="space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-4">
                              <div className="flex items-center justify-between gap-4">
                                <div>
                                  <Label>Position verticale du titre</Label>
                                  <p className="text-sm text-muted-foreground">
                                    Remonte ou descends le titre dans la bannière.
                                  </p>
                                </div>
                                <span className="text-sm font-medium">{appearanceForm.heroTitlePositionY}%</span>
                              </div>
                              <Slider
                                value={[appearanceForm.heroTitlePositionY]}
                                min={-50}
                                max={50}
                                step={1}
                                onValueChange={(values) =>
                                  setAppearanceForm(prev => ({ ...prev, heroTitlePositionY: values[0] ?? prev.heroTitlePositionY }))
                                }
                              />
                            </div>

                            <div className="space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-4">
                              <div className="flex items-center justify-between gap-4">
                                <div>
                                  <Label>Largeur du titre</Label>
                                  <p className="text-sm text-muted-foreground">
                                    Ajuste la largeur du titre pour l'adapter à ton visuel.
                                  </p>
                                </div>
                                <span className="text-sm font-medium">{appearanceForm.heroTitleWidthPercent}%</span>
                              </div>
                              <Slider
                                value={[appearanceForm.heroTitleWidthPercent]}
                                min={30}
                                max={100}
                                step={1}
                                onValueChange={(values) =>
                                  setAppearanceForm(prev => ({ ...prev, heroTitleWidthPercent: values[0] ?? prev.heroTitleWidthPercent }))
                                }
                              />
                            </div>
                          </AdminSectionCollapsible>

                          <AdminSectionCollapsible
                            title="Réglages de l’accroche"
                            description="Ajuste l’accroche individuellement, sans perdre de vue sa place ni sa largeur."
                            open={!adminCollapsedSections.appearanceHeroTaglineSettings}
                            onOpenChange={(open) => setAdminSectionExpanded("appearanceHeroTaglineSettings", open)}
                            className="bg-white"
                            contentClassName="space-y-3 pt-1"
                          >
                            <div className="space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-4">
                              <div className="flex items-center justify-between gap-4">
                                <div>
                                  <Label>Position horizontale de l'accroche</Label>
                                  <p className="text-sm text-muted-foreground">
                                    Décale le texte d'accroche vers la gauche ou la droite.
                                  </p>
                                </div>
                                <span className="text-sm font-medium">{appearanceForm.heroTaglinePositionX}%</span>
                              </div>
                              <Slider
                                value={[appearanceForm.heroTaglinePositionX]}
                                min={-50}
                                max={50}
                                step={1}
                                onValueChange={(values) =>
                                  setAppearanceForm(prev => ({ ...prev, heroTaglinePositionX: values[0] ?? prev.heroTaglinePositionX }))
                                }
                              />
                            </div>

                            <div className="space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-4">
                              <div className="flex items-center justify-between gap-4">
                                <div>
                                  <Label>Position verticale de l'accroche</Label>
                                  <p className="text-sm text-muted-foreground">
                                    Remonte ou descends le texte d'accroche dans la bannière.
                                  </p>
                                </div>
                                <span className="text-sm font-medium">{appearanceForm.heroTaglinePositionY}%</span>
                              </div>
                              <Slider
                                value={[appearanceForm.heroTaglinePositionY]}
                                min={-50}
                                max={50}
                                step={1}
                                onValueChange={(values) =>
                                  setAppearanceForm(prev => ({ ...prev, heroTaglinePositionY: values[0] ?? prev.heroTaglinePositionY }))
                                }
                              />
                            </div>

                            <div className="space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-4">
                              <div className="flex items-center justify-between gap-4">
                                <div>
                                  <Label>Largeur de l'accroche</Label>
                                  <p className="text-sm text-muted-foreground">
                                    Ajuste la largeur du texte d'accroche.
                                  </p>
                                </div>
                                <span className="text-sm font-medium">{appearanceForm.heroTaglineWidthPercent}%</span>
                              </div>
                              <Slider
                                value={[appearanceForm.heroTaglineWidthPercent]}
                                min={30}
                                max={100}
                                step={1}
                                onValueChange={(values) =>
                                  setAppearanceForm(prev => ({ ...prev, heroTaglineWidthPercent: values[0] ?? prev.heroTaglineWidthPercent }))
                                }
                              />
                            </div>
                          </AdminSectionCollapsible>
                        </div>
                    </div>
                    </div>

                    <div className="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-slate-200 bg-slate-50/70 px-4 py-4">
                      <div>
                        <p className="font-medium">
                          {user?.role === "super_admin"
                            ? "Les changements seront appliqués à tout le portail."
                            : "Tu peux consulter l’apparence, mais seul un super admin peut la modifier."}
                        </p>
                        <p className="text-sm text-muted-foreground">
                          Connexion, tableau de bord, formulaires et navigation reprendront cette identité visuelle.
                        </p>
                      </div>
                      <div className="flex flex-wrap gap-2">
                        <Button variant="outline" onClick={resetAppearanceForm}>
                          Réinitialiser le formulaire
                        </Button>
                        <Button
                          onClick={handleAppearanceSave}
                          disabled={user?.role !== "super_admin" || setAppearanceMutation.isPending}
                        >
                          {setAppearanceMutation.isPending ? (
                            <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                          ) : (
                            <Palette className="mr-2 h-4 w-4" />
                          )}
                          Enregistrer l’apparence
                        </Button>
                      </div>
                    </div>
                  </CardContent>
                </Card>
              </section>

              <section className="min-w-0 space-y-6 2xl:sticky 2xl:top-6">
                <Card>
                  <CardHeader>
                    <CardTitle>Aperçu de l’interface</CardTitle>
                    <CardDescription>
                      Une simulation unique de la bannière : le cadrage enregistré est ensuite réutilisé tel quel sur tous les appareils.
                    </CardDescription>
                  </CardHeader>
                  <CardContent>
                    <div className="mb-4 rounded-lg border bg-muted/20 px-4 py-3 text-sm text-muted-foreground">
                      Le portail utilise un seul cadrage de bannière. Règle l’image une fois, puis ce positionnement est repris sur mobile, tablette et ordinateur.
                    </div>

                    <div
                      className={`rounded-lg border overflow-hidden ${previewAppearance.themeMode === "dark" ? "bg-slate-950 text-slate-50" : "bg-white text-slate-900"}`}
                    >
                      <div
                        className="relative px-5 py-5 overflow-hidden"
                        style={{ color: "#fff" }}
                      >
                        <div
                          className="absolute inset-0 bg-cover bg-no-repeat"
                          style={{
                            backgroundImage: `url(${previewHeroImage})`,
                            backgroundPosition: `${previewAppearance.heroImagePositionX}% ${previewAppearance.heroImagePositionY}%`,
                          }}
                        />
                        <div
                          className="absolute inset-0"
                          style={{
                            background: `linear-gradient(90deg, rgba(10, 17, 40, 0.34) 0%, rgba(10, 17, 40, 0.16) 42%, rgba(10, 17, 40, 0.28) 100%), ${previewAppearance.primaryColor}`,
                            opacity: Math.max(0.45, Math.min(0.92, previewAppearance.heroOverlayOpacity / 100)),
                          }}
                        />
                        <div className="relative z-10 flex items-start justify-between gap-4">
                          <div className="space-y-2">
                            <div className="inline-flex items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-xs font-medium">
                              <ThemeModeIcon mode={previewAppearance.themeMode} />
                              Mode {previewAppearance.themeMode === "system" ? "automatique" : previewAppearance.themeMode}
                            </div>
                            <div className="flex items-center gap-3">
                              {previewAppearance.logoUrl ? (
                                <img
                                  src={previewAppearance.logoUrl}
                                  alt=""
                                  className="h-10 w-10 rounded-md bg-white/10 object-contain p-1"
                                />
                              ) : null}
                              <h3 className="text-xl font-semibold">{previewAppearance.portalTitle}</h3>
                            </div>
                            <p className="max-w-md text-sm text-white/85">
                              {previewAppearance.portalTagline}
                            </p>
                          </div>
                          <div
                            className="rounded-full px-3 py-1 text-xs font-semibold shrink-0"
                            style={{
                              backgroundColor: previewAppearance.accentColor,
                              color: "#111827",
                            }}
                          >
                            CCDS
                          </div>
                        </div>
                      </div>

                      <div className={`grid gap-4 p-5 ${previewAppearance.themeMode === "dark" ? "bg-slate-900" : "bg-slate-50"}`}>
                        <div className="grid gap-4 md:grid-cols-2">
                          <div className={`rounded-lg border p-4 ${previewAppearance.themeMode === "dark" ? "border-slate-700 bg-slate-900" : "border-slate-200 bg-white"}`}>
                            <p className="text-sm text-muted-foreground mb-2">Action principale</p>
                            <button
                              type="button"
                              className="w-full rounded-md px-4 py-3 text-sm font-semibold"
                              style={{
                                backgroundColor: previewAppearance.buttonColor,
                                color: "#ffffff",
                              }}
                            >
                              Commencer la réservation
                            </button>
                          </div>
                          <div className={`rounded-lg border p-4 ${previewAppearance.themeMode === "dark" ? "border-slate-700 bg-slate-900" : "border-slate-200 bg-white"}`}>
                            <p className="text-sm text-muted-foreground mb-2">Accent visuel</p>
                            <div
                              className="rounded-md px-4 py-3 text-sm font-medium"
                              style={{
                                backgroundColor: previewAppearance.accentColor,
                                color: "#111827",
                              }}
                            >
                              Demande de matériel validée et prête à traiter
                            </div>
                          </div>
                        </div>

                        <div className={`rounded-lg border p-4 ${previewAppearance.themeMode === "dark" ? "border-slate-700 bg-slate-900" : "border-slate-200 bg-white"}`}>
                          <div className="flex items-center justify-between gap-3 mb-4">
                            <div>
                              <p className="font-medium">Résumé administratif</p>
                              <p className="text-sm text-muted-foreground">Ce bloc simule une fiche de traitement.</p>
                            </div>
                            <span
                              className="rounded-full px-3 py-1 text-xs font-semibold shrink-0"
                              style={{
                                backgroundColor: previewAppearance.primaryColor,
                                color: "#ffffff",
                              }}
                            >
                              En attente
                            </span>
                          </div>
                          <div className="grid gap-3 md:grid-cols-3">
                            <div className={`rounded-md p-3 ${previewAppearance.themeMode === "dark" ? "bg-slate-800" : "bg-slate-50"}`}>
                              <p className="text-xs uppercase tracking-wide text-muted-foreground">Salle</p>
                              <p className="font-medium mt-1">Hall-Amphithéâtre</p>
                            </div>
                            <div className={`rounded-md p-3 ${previewAppearance.themeMode === "dark" ? "bg-slate-800" : "bg-slate-50"}`}>
                              <p className="text-xs uppercase tracking-wide text-muted-foreground">Créneau</p>
                              <p className="font-medium mt-1">09:00 - 17:00</p>
                            </div>
                            <div className={`rounded-md p-3 ${previewAppearance.themeMode === "dark" ? "bg-slate-800" : "bg-slate-50"}`}>
                              <p className="text-xs uppercase tracking-wide text-muted-foreground">Matériel</p>
                              <p className="font-medium mt-1">5 tentes 3x3</p>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                  </CardContent>
                </Card>

                <Card>
                  <CardHeader>
                    <CardTitle className="flex items-center gap-2">
                      <FileText className="h-5 w-5 text-primary" />
                      RIB MJS
                    </CardTitle>
                    <CardDescription>
                      Dépose ici le RIB de la Maison de la Jeunesse des Savanes. Il sera repris automatiquement avec la facture lors des confirmations finales des locations de salle.
                    </CardDescription>
                  </CardHeader>
                  <CardContent className="space-y-4">
                    <div className="rounded-lg border bg-muted/20 p-4 text-sm">
                      {salleBillingSettings?.ribDocumentUrl ? (
                        <div className="space-y-2">
                          <p className="font-medium">{salleBillingSettings.ribDocumentName || "RIB MJS"}</p>
                          <p className="text-muted-foreground">
                            Ajouté le {salleBillingSettings.ribUploadedAt ? formatDateTime(salleBillingSettings.ribUploadedAt) : "-"}
                          </p>
                        </div>
                      ) : (
                        <p className="text-muted-foreground">
                          Aucun RIB MJS n’est encore enregistré. Tu pourras le glisser ici dès que tu l’auras récupéré.
                        </p>
                      )}
                    </div>

                    <div className="flex flex-wrap gap-2">
                      <Button
                        type="button"
                        variant="outline"
                        onClick={() => salleRibInputRef.current?.click()}
                        disabled={(user?.role !== "admin" && user?.role !== "super_admin") || isSalleRibUploadBusy}
                      >
                        <Upload className="mr-2 h-4 w-4" />
                        {isSalleRibUploadBusy ? "Téléversement..." : salleBillingSettings?.ribDocumentUrl ? "Remplacer le RIB" : "Ajouter le RIB"}
                      </Button>
                      {salleBillingSettings?.ribDocumentUrl && (
                        <a href={salleBillingSettings.ribDocumentUrl} target="_blank" rel="noopener noreferrer">
                          <Button type="button" variant="outline">
                            <Eye className="mr-2 h-4 w-4" />
                            Voir le RIB
                          </Button>
                        </a>
                      )}
                      {salleBillingSettings?.ribDocumentUrl && (
                        <Button
                          type="button"
                          variant="outline"
                          onClick={() => clearSalleRibMutation.mutate()}
                          disabled={clearSalleRibMutation.isPending}
                        >
                          {clearSalleRibMutation.isPending ? (
                            <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                          ) : (
                            <Trash2 className="mr-2 h-4 w-4" />
                          )}
                          Retirer le RIB
                        </Button>
                      )}
                    </div>

                    <p className="text-sm text-muted-foreground">
                      Formats acceptés : PDF, PNG, JPG ou WebP.
                    </p>
                  </CardContent>
                </Card>
              </section>
            </div>
          </TabsContent>

          {(user?.role === "admin" || user?.role === "super_admin") && (
            <TabsContent value="directory" className="space-y-6">
              <input
                ref={associationImportInputRef}
                type="file"
                accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
                className="hidden"
                onChange={handleDirectoryWorkbookSelect}
              />

              <div className="grid gap-6 2xl:grid-cols-[minmax(0,0.84fr)_minmax(0,1.16fr)] 2xl:items-start 2xl:gap-8">
                <section className="min-w-0 space-y-6">
                  <Card>
                    <CardHeader>
                      <CardTitle className="flex items-center gap-2">
                        <Database className="h-5 w-5 text-primary" />
                        Bordereau des associations
                      </CardTitle>
                      <CardDescription>
                        Importe un fichier Excel pour préremplir automatiquement les espaces des associations reconnues.
                      </CardDescription>
                    </CardHeader>
                    <CardContent className="space-y-5">
                      <div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-6">
                        <AdminMetricTile label="Référentiel total" value={directorySummary?.total || 0} />
                        <AdminMetricTile label="Avec email" value={directorySummary?.withEmail || 0} />
                        <AdminMetricTile label="Sans email" value={directorySummary?.withoutEmail || 0} tone="warning" />
                        <AdminMetricTile
                          label="Dernier import"
                          value={directorySummary?.lastImportAt ? formatDateTime(directorySummary.lastImportAt) : "-"}
                          detail="Dernière source relue dans le référentiel"
                        />
                        <AdminMetricTile label="Déjà inscrites" value={directorySummary?.registered || 0} tone="success" />
                        <AdminMetricTile label="Pas encore inscrites" value={directorySummary?.unregistered || 0} tone="info" />
                      </div>

                      <div className="flex flex-wrap gap-2">
                        <Link href="/associations/carte" {...buildRoutePrefetchHandlers("/associations/carte")}>
                          <Button type="button" variant="outline">
                            <ArrowUpRight className="mr-2 h-4 w-4" />
                            Ouvrir la carte publique
                          </Button>
                        </Link>
                      </div>

                      <AdminSurface
                        title="Synchronisation contrôlée HelloAsso"
                        description="Le portail rapproche uniquement les associations déjà présentes dans le bordereau, met à jour leurs données publiques HelloAsso et n’ajoute jamais de nouvelle fiche automatiquement."
                        badge={
                          <Badge variant="outline" className={isHelloAssoConfigured ? "border-green-200 bg-green-50 text-green-700" : "border-amber-200 bg-amber-50 text-amber-700"}>
                            {isHelloAssoConfigured ? "HelloAsso configuré" : "Configuration HelloAsso manquante"}
                          </Badge>
                        }
                        className="bg-muted/20"
                      >
                        <div className="space-y-4">
                          <AdminActionButtonGroup>
                            <Button
                              type="button"
                              variant="outline"
                              onClick={handleSyncHelloAssoBatch}
                              disabled={!isHelloAssoConfigured || syncDirectoryHelloAssoBatchMutation.isPending}
                            >
                              {syncDirectoryHelloAssoBatchMutation.isPending ? (
                                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                              ) : (
                                <RefreshCw className="mr-2 h-4 w-4" />
                              )}
                              Synchroniser la vue filtrée
                            </Button>
                          </AdminActionButtonGroup>

                        {user?.role === "super_admin" ? (
                          <div className="grid gap-4 lg:grid-cols-[1.1fr_1fr_auto]">
                            <div className="space-y-2">
                              <Label htmlFor="helloasso-client-id">Client ID HelloAsso</Label>
                              <Input
                                id="helloasso-client-id"
                                value={helloAssoSettingsForm.clientId}
                                onChange={(event) =>
                                  setHelloAssoSettingsForm((prev) => ({
                                    ...prev,
                                    clientId: event.target.value,
                                  }))
                                }
                                placeholder="Client ID partenaire ou organisation"
                              />
                            </div>
                            <div className="space-y-2">
                              <Label htmlFor="helloasso-client-secret">
                                Client secret HelloAsso
                                {helloAssoSettings?.clientSecretConfigured ? " (laisser vide pour conserver l’existant)" : ""}
                              </Label>
                              <Input
                                id="helloasso-client-secret"
                                type="password"
                                value={helloAssoSettingsForm.clientSecret}
                                onChange={(event) =>
                                  setHelloAssoSettingsForm((prev) => ({
                                    ...prev,
                                    clientSecret: event.target.value,
                                  }))
                                }
                                placeholder={helloAssoSettings?.clientSecretConfigured ? "Secret déjà enregistré" : "Client secret"}
                              />
                            </div>
                            <div className="flex items-end gap-3">
                              <div className="flex items-center gap-2 rounded-lg border bg-background px-3 py-2">
                                <Switch
                                  checked={helloAssoSettingsForm.enabled}
                                  onCheckedChange={(checked) =>
                                    setHelloAssoSettingsForm((prev) => ({
                                      ...prev,
                                      enabled: checked,
                                    }))
                                  }
                                />
                                <span className="text-sm">{helloAssoSettingsForm.enabled ? "Activé" : "Désactivé"}</span>
                              </div>
                              <Button
                                type="button"
                                onClick={handleSaveHelloAssoSettings}
                                disabled={setHelloAssoSettingsMutation.isPending}
                              >
                                {setHelloAssoSettingsMutation.isPending ? (
                                  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                                ) : (
                                  <Settings className="mr-2 h-4 w-4" />
                                )}
                                Enregistrer
                              </Button>
                            </div>
                          </div>
                        ) : (
                          <p className="text-sm text-muted-foreground">
                            La configuration des identifiants HelloAsso reste réservée au super administrateur. Les administrateurs peuvent ensuite lancer la synchronisation sans créer de nouvelles associations.
                          </p>
                        )}
                        </div>
                      </AdminSurface>

                      {user?.role === "super_admin" ? (
                        <AdminSurface
                          title="Carte OpenStreetMap / MapLibre"
                          description="Choisis un style auto-hébergé pour la carte, puis affine-le dans Maputnik sans perdre les tuiles servies localement."
                          className="bg-muted/20"
                        >
                          <div className="space-y-4">
                            <div className="grid gap-4 xl:grid-cols-2">
                              <div className="space-y-3 rounded-2xl border border-slate-200 bg-white/90 p-4">
                                <div>
                                  <p className="text-sm font-semibold text-slate-950">Style public</p>
                                  <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                    Utilisé par la carte publique, l’annuaire géographique et les fiches visibles sans connexion.
                                  </p>
                                </div>
                                <div className="grid gap-3">
                                  {associationMapStylePresets
                                    .filter((preset) => preset.id !== "bright-admin")
                                    .map((preset) => (
                                      <button
                                        key={`public-${preset.id}`}
                                        type="button"
                                        onClick={() =>
                                          setAssociationMapSettingsForm((prev) => ({
                                            ...prev,
                                            publicStyleUrl: preset.url,
                                          }))
                                        }
                                        className={`rounded-2xl border p-4 text-left transition ${
                                          associationMapSettingsForm.publicStyleUrl === preset.url
                                            ? "border-primary bg-primary/5 shadow-sm"
                                            : "border-slate-200 bg-white hover:border-primary/40 hover:bg-primary/5"
                                        }`}
                                      >
                                        <p className="text-sm font-semibold text-slate-950">{preset.label}</p>
                                        <p className="mt-2 text-xs leading-5 text-muted-foreground">{preset.description}</p>
                                        <p className="mt-3 break-all font-mono text-[11px] text-slate-500">{preset.url}</p>
                                      </button>
                                    ))}
                                </div>
                              </div>

                              <div className="space-y-3 rounded-2xl border border-slate-200 bg-white/90 p-4">
                                <div>
                                  <p className="text-sm font-semibold text-slate-950">Style administration</p>
                                  <p className="mt-1 text-xs leading-5 text-muted-foreground">
                                    Réservé aux usages internes denses. Tu peux garder une lecture plus sobre sans toucher au rendu public.
                                  </p>
                                </div>
                                <div className="grid gap-3">
                                  {associationMapStylePresets.map((preset) => (
                                    <button
                                      key={`admin-${preset.id}`}
                                      type="button"
                                      onClick={() =>
                                        setAssociationMapSettingsForm((prev) => ({
                                          ...prev,
                                          adminStyleUrl: preset.url,
                                        }))
                                      }
                                      className={`rounded-2xl border p-4 text-left transition ${
                                        associationMapSettingsForm.adminStyleUrl === preset.url
                                          ? "border-primary bg-primary/5 shadow-sm"
                                          : "border-slate-200 bg-white hover:border-primary/40 hover:bg-primary/5"
                                      }`}
                                    >
                                      <p className="text-sm font-semibold text-slate-950">{preset.label}</p>
                                      <p className="mt-2 text-xs leading-5 text-muted-foreground">{preset.description}</p>
                                      <p className="mt-3 break-all font-mono text-[11px] text-slate-500">{preset.url}</p>
                                    </button>
                                  ))}
                                </div>
                              </div>
                            </div>

                            <div className="rounded-2xl border border-slate-200 bg-white/90 p-4">
                              <p className="text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Flux recommandé</p>
                              <ol className="mt-3 space-y-2 text-sm leading-6 text-slate-600">
                                <li>1. Vérifie d’abord le serveur OpenMapTiles et le TileJSON local.</li>
                                <li>2. Choisis un style public pour les usages ouverts au public.</li>
                                <li>3. Réserve si besoin un style plus dense au back-office.</li>
                                <li>4. Personnalise ensuite le rendu dans Maputnik, pas directement dans les tuiles.</li>
                              </ol>
                            </div>

                            <div className="grid gap-4 xl:grid-cols-2">
                              <div className="space-y-2">
                                <Label htmlFor="association-map-public-style-url">Style URL public</Label>
                                <Input
                                  id="association-map-public-style-url"
                                  value={associationMapSettingsForm.publicStyleUrl}
                                  onChange={(event) =>
                                    setAssociationMapSettingsForm((prev) => ({
                                      ...prev,
                                      publicStyleUrl: event.target.value,
                                    }))
                                  }
                                  placeholder={associationMapPublicDefaultStyleUrl}
                                />
                                <p className="text-xs text-muted-foreground">
                                  Recommandé pour le public : <span className="font-mono">{associationMapPublicDefaultStyleUrl}</span>
                                </p>
                              </div>

                              <div className="space-y-2">
                                <Label htmlFor="association-map-admin-style-url">Style URL administration</Label>
                                <Input
                                  id="association-map-admin-style-url"
                                  value={associationMapSettingsForm.adminStyleUrl}
                                  onChange={(event) =>
                                    setAssociationMapSettingsForm((prev) => ({
                                      ...prev,
                                      adminStyleUrl: event.target.value,
                                    }))
                                  }
                                  placeholder={associationMapAdminDefaultStyleUrl}
                                />
                                <p className="text-xs text-muted-foreground">
                                  Recommandé pour l’admin : <span className="font-mono">{associationMapAdminDefaultStyleUrl}</span>
                                </p>
                              </div>
                            </div>

                            <div className="space-y-2">
                              <p className="text-xs text-muted-foreground">
                                Guide de mise en place : <span className="font-mono">docs/openmaptiles-autonome-premium.md</span>
                              </p>
                            </div>
                            <div className="flex items-end gap-2">
                              <Button
                                type="button"
                                variant="outline"
                                onClick={() =>
                                  setAssociationMapSettingsForm({
                                    publicStyleUrl: associationMapPublicDefaultStyleUrl,
                                    adminStyleUrl: associationMapAdminDefaultStyleUrl,
                                  })
                                }
                              >
                                Réinitialiser les styles recommandés
                              </Button>
                              <Button
                                type="button"
                                onClick={handleSaveAssociationMapSettings}
                                disabled={setAssociationMapSettingsMutation.isPending}
                              >
                                {setAssociationMapSettingsMutation.isPending ? (
                                  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                                ) : (
                                  <MapPinned className="mr-2 h-4 w-4" />
                                )}
                                Enregistrer la carte
                              </Button>
                            </div>
                          </div>
                        </AdminSurface>
                      ) : null}

                      {user?.role === "super_admin" ? (
                        <AdminSurface
                          title="Importer un fichier Excel"
                          description="Colonnes attendues au minimum : Nom association, Email officiel."
                          className="border-dashed bg-white/90"
                        >
                          <AdminActionButtonGroup>
                            <Button
                              type="button"
                              variant="outline"
                              onClick={() => associationImportInputRef.current?.click()}
                              disabled={previewDirectoryImportMutation.isPending || importDirectoryMutation.isPending}
                            >
                              <Upload className="mr-2 h-4 w-4" />
                              Choisir un fichier Excel
                            </Button>
                            <Button
                              type="button"
                              onClick={handleAssociationDirectoryImport}
                              disabled={!directoryFilePayload || !directoryPreview || directoryPreview.totalRows === 0 || importDirectoryMutation.isPending}
                            >
                              {importDirectoryMutation.isPending ? (
                                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                              ) : (
                                <Database className="mr-2 h-4 w-4" />
                              )}
                              Lancer l'import
                            </Button>
                          </AdminActionButtonGroup>
                          <p className="text-sm text-muted-foreground">
                            Fichier sélectionné : {selectedDirectoryFileName || "aucun fichier"}
                          </p>
                        </AdminSurface>
                      ) : (
                        <AdminSurface
                          title="Import du bordereau"
                          description="L’import Excel reste réservé au super administrateur. En revanche, tu peux consulter le bordereau et inviter les associations."
                          className="border-dashed bg-white/90"
                        >
                          <div />
                        </AdminSurface>
                      )}

                      {user?.role === "super_admin" && directoryPreview && (
                        <div className="grid gap-4 sm:grid-cols-4">
                          <Card className="border-green-200 bg-green-50">
                            <CardHeader className="pb-2">
                              <CardTitle className="text-sm font-medium text-green-800">Lignes valides</CardTitle>
                            </CardHeader>
                            <CardContent>
                              <div className="text-2xl font-bold text-green-700">{directoryPreview.validRows}</div>
                            </CardContent>
                          </Card>
                          <Card className="border-yellow-200 bg-yellow-50">
                            <CardHeader className="pb-2">
                              <CardTitle className="text-sm font-medium text-yellow-800">Sans email</CardTitle>
                            </CardHeader>
                            <CardContent>
                              <div className="text-2xl font-bold text-yellow-700">{directoryPreview.missingEmailRows}</div>
                            </CardContent>
                          </Card>
                          <Card className="border-red-200 bg-red-50">
                            <CardHeader className="pb-2">
                              <CardTitle className="text-sm font-medium text-red-800">Emails dupliqués</CardTitle>
                            </CardHeader>
                            <CardContent>
                              <div className="text-2xl font-bold text-red-700">{directoryPreview.duplicateEmailRows}</div>
                            </CardContent>
                          </Card>
                          <Card>
                            <CardHeader className="pb-2">
                              <CardTitle className="text-sm font-medium">Lignes lues</CardTitle>
                            </CardHeader>
                            <CardContent>
                              <div className="text-2xl font-bold">{directoryPreview.totalRows}</div>
                            </CardContent>
                          </Card>
                        </div>
                      )}

                      {user?.role === "super_admin" && directoryPreview && directoryPreview.duplicateGroups.length > 0 && (
                        <div className="rounded-lg border border-red-200 bg-red-50 p-5 space-y-4">
                          <div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
                            <div>
                              <h3 className="font-medium text-red-900">Résolution des emails dupliqués</h3>
                              <p className="text-sm text-red-800 mt-1">
                                Pour chaque email partagé par plusieurs lignes, choisis l'association à conserver dans le référentiel.
                              </p>
                            </div>
                            <div className="flex flex-wrap gap-2">
                              <Badge variant="outline" className="border-red-200 bg-white text-red-800">
                                {directoryPreview.duplicateGroups.length} groupe(s) à arbitrer
                              </Badge>
                              <Button
                                type="button"
                                size="sm"
                                variant="outline"
                                className="border-red-200 bg-white text-red-900 hover:bg-red-100"
                                onClick={() => setDirectoryDuplicateSelections(buildRecommendedDuplicateSelections(directoryPreview.duplicateGroups))}
                              >
                                Appliquer les choix recommandés
                              </Button>
                            </div>
                          </div>

                          <div className="rounded-md border border-red-200 bg-white/80 px-4 py-3 text-sm text-red-900">
                            Par défaut, le portail propose la première ligne trouvée pour chaque email en doublon. Tu peux garder cette recommandation ou choisir une autre association ci-dessous.
                          </div>

                          <div className="space-y-4">
                            {directoryPreview.duplicateGroups.map((group) => {
                              const selectedRow = directoryDuplicateSelections[group.emailOfficielNormalise];

                              return (
                                <div key={group.emailOfficielNormalise} className="rounded-lg border border-red-200 bg-white p-4 space-y-3">
                                  <div className="flex flex-col gap-2 lg:flex-row lg:items-start lg:justify-between">
                                    <div>
                                      <p className="font-medium">{group.emailOfficiel}</p>
                                      <p className="text-sm text-muted-foreground">
                                        {group.options.length} lignes détectées : {group.rowNumbers.join(", ")}
                                      </p>
                                    </div>
                                    <Badge variant="outline" className="w-fit border-red-200 bg-red-50 text-red-800">
                                      {selectedRow ? `Ligne ${selectedRow} sélectionnée` : "Sélection requise"}
                                    </Badge>
                                  </div>

                                  <div className="grid gap-3">
                                    {group.options.map((option) => {
                                      const isSelected = selectedRow === option.rowNumber;
                                      return (
                                        <button
                                          key={`${group.emailOfficielNormalise}-${option.rowNumber}`}
                                          type="button"
                                          className={cn(
                                            "rounded-lg border p-3 text-left transition-colors",
                                            isSelected
                                              ? "border-primary bg-primary/5 ring-1 ring-primary/20"
                                              : "border-border bg-background hover:border-primary/40 hover:bg-primary/5"
                                          )}
                                          onClick={() => {
                                            setDirectoryDuplicateSelections((prev) => ({
                                              ...prev,
                                              [group.emailOfficielNormalise]: option.rowNumber,
                                            }));
                                          }}
                                        >
                                          <div className="flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
                                            <div>
                                              <p className="font-medium text-foreground">{option.nomAssociation}</p>
                                              <p className="text-sm text-muted-foreground">
                                                {option.ville || "ville non renseignée"} • {option.sheetName} • ligne {option.rowNumber}
                                              </p>
                                            </div>
                                            {isSelected ? (
                                              <Badge className="w-fit">Conservée</Badge>
                                            ) : (
                                              <Badge variant="outline" className="w-fit">Choisir</Badge>
                                            )}
                                          </div>
                                        </button>
                                      );
                                    })}
                                  </div>
                                </div>
                              );
                            })}
                          </div>
                        </div>
                      )}
                    </CardContent>
                  </Card>
                </section>

                <section className="min-w-0 space-y-6">
                  <Card>
                    <CardHeader>
                      <CardTitle>Aperçu avant import</CardTitle>
                      <CardDescription>
                        Les 100 premières lignes analysées avec leur statut.
                      </CardDescription>
                    </CardHeader>
                    <CardContent>
                      {user?.role === "super_admin" && directoryPreview ? (
                        <ScrollArea className="h-[520px] pr-4">
                          <div className="space-y-3">
                            {directoryPreview.previewRows.map((row) => (
                              <div key={`${row.rowNumber}-${row.emailOfficiel ?? row.nomAssociation}`} className="rounded-lg border p-3">
                                <div className="flex items-start justify-between gap-3">
                                  <div>
                                    <p className="font-medium">{row.nomAssociation}</p>
                                    <p className="text-sm text-muted-foreground">
                                      {row.sheetName} • ligne {row.rowNumber} • {row.emailOfficiel || "email manquant"} • {row.ville || "ville non renseignée"}
                                    </p>
                                  </div>
                                  <Badge
                                    variant="outline"
                                    className={
                                      row.status === "valid"
                                        ? "bg-green-50 text-green-700 border-green-200"
                                        : row.status === "missing_email"
                                          ? "bg-yellow-50 text-yellow-700 border-yellow-200"
                                          : "bg-red-50 text-red-700 border-red-200"
                                    }
                                  >
                                    {row.status === "valid" ? "Valide" : row.status === "missing_email" ? "Sans email" : "Dupliqué"}
                                  </Badge>
                                </div>
                                <p className="text-sm mt-2 text-muted-foreground">{row.message}</p>
                              </div>
                            ))}
                          </div>
                        </ScrollArea>
                      ) : user?.role === "super_admin" ? (
                        <div className="text-center py-16 text-muted-foreground">
                          <Database className="h-12 w-12 mx-auto mb-4 opacity-50" />
                          <p>Aucun aperçu pour le moment</p>
                        </div>
                      ) : (
                        <div className="text-center py-16 text-muted-foreground">
                          <Database className="h-12 w-12 mx-auto mb-4 opacity-50" />
                          <p>Aperçu réservé au super administrateur</p>
                        </div>
                      )}
                    </CardContent>
                  </Card>

                  <Card>
                    <CardHeader>
                      <CardTitle className="text-xl">Référentiel du bordereau</CardTitle>
                      <CardDescription>
                        Les associations importées restent visibles ici, même avant leur inscription sur le portail.
                      </CardDescription>
                    </CardHeader>
                    <CardContent className="space-y-5">
                      <div className="rounded-2xl border bg-muted/20 p-4">
                        <div className="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
                          <div className="space-y-1">
                            <p className="text-sm font-semibold text-foreground">Pilotage du référentiel</p>
                            <p className="text-sm text-muted-foreground">
                              Crée une fiche, synchronise la vue active et traite les rapprochements depuis un seul espace de travail.
                            </p>
                          </div>
                          <div className="flex flex-col gap-2 lg:flex-row lg:flex-wrap xl:flex-nowrap">
                            <Button type="button" onClick={() => openDirectoryEntryCreation()} className="w-full lg:w-auto xl:min-w-[210px]">
                              <Plus className="mr-2 h-4 w-4" />
                              Nouvelle association
                            </Button>
                            <Button
                              type="button"
                              variant="outline"
                              onClick={() =>
                                syncDirectoryReferenceBatchMutation.mutate({
                                  search: directorySearch || undefined,
                                  registrationStatus: directoryStatusFilter,
                                  commune: directoryCommuneFilter,
                                  thematique: directoryThematiqueFilter,
                                })
                              }
                              disabled={syncDirectoryReferenceBatchMutation.isPending}
                              className="w-full lg:w-auto xl:min-w-[250px]"
                            >
                              {syncDirectoryReferenceBatchMutation.isPending ? (
                                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                              ) : (
                                <RefreshCw className="mr-2 h-4 w-4" />
                              )}
                              Rechercher des mises à jour
                            </Button>
                            <Button
                              type="button"
                              variant="outline"
                              onClick={handleSyncHelloAssoBatch}
                              disabled={!isHelloAssoConfigured || syncDirectoryHelloAssoBatchMutation.isPending}
                              className="w-full lg:w-auto xl:min-w-[210px]"
                            >
                              {syncDirectoryHelloAssoBatchMutation.isPending ? (
                                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                              ) : (
                                <RefreshCw className="mr-2 h-4 w-4" />
                              )}
                              Synchroniser la vue filtrée
                            </Button>
                          </div>
                        </div>
                      </div>

                      <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-5">
                        {associationCommuneOptions.map((option) => {
                          const count = associationCommuneCounts?.[option.value] ?? 0;
                          const isActive = directoryCommuneFilter === option.value;

                          return (
                            <button
                              key={`directory-summary-${option.value}`}
                              type="button"
                              onClick={() => setDirectoryCommuneFilter(option.value)}
                              className={cn(
                                "rounded-2xl border p-4 text-left shadow-sm transition-all",
                                isActive
                                  ? "border-primary/30 bg-primary/10 shadow-md ring-2 ring-primary/15"
                                  : "border-slate-200 bg-white hover:-translate-y-0.5 hover:border-primary/20 hover:shadow-md"
                              )}
                            >
                              <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Commune</p>
                              <div className="mt-3 flex items-end justify-between gap-3">
                                <div className="min-w-0">
                                  <p className="truncate text-base font-semibold text-slate-950">{option.label}</p>
                                  <p className="mt-1 text-xs text-slate-600">
                                    {option.value === "all" ? "Vue territoriale complète" : "Associations importées dans ce périmètre"}
                                  </p>
                                </div>
                                <span className="text-3xl font-semibold leading-none text-slate-950">{count}</span>
                              </div>
                            </button>
                          );
                        })}
                      </div>

                      {directoryReviews?.length ? (
                        <div className="rounded-2xl border border-amber-200 bg-gradient-to-br from-amber-50 via-white to-amber-50/70 p-5 shadow-sm">
                          <div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
                            <div className="space-y-1">
                              <p className="text-lg font-semibold text-amber-950">Correspondances à valider</p>
                              <p className="max-w-3xl text-sm leading-6 text-amber-900/90">
                                Les inscriptions non rapprochées automatiquement restent ici jusqu’à validation humaine. Cette vue permet de comparer clairement les informations reçues avec les fiches déjà présentes dans le bordereau.
                              </p>
                            </div>
                            <Badge variant="outline" className="w-fit border-amber-300 bg-white text-amber-800">
                              {directoryReviews.length} en attente
                            </Badge>
                          </div>
                          <div className="mt-5 space-y-4">
                            {(directoryReviews as DirectoryReviewItem[]).map((review) => {
                              const reviewStatus = getDirectoryReviewStatusConfig(review.matchStatus);

                              return (
                                <div key={review.id} className="rounded-2xl border border-amber-200 bg-white p-4 shadow-sm md:p-5">
                                  <div className="space-y-4">
                                    <div className="flex flex-col gap-4 rounded-2xl border border-slate-200 bg-slate-50/70 p-4 lg:flex-row lg:items-start lg:justify-between">
                                      <div className="min-w-0 space-y-3">
                                        <div className="flex flex-wrap items-center gap-2">
                                          <p className="text-lg font-semibold text-slate-950">{review.proposedNomAssociation}</p>
                                          <Badge variant="outline">
                                            {review.sourceType === "portal_signup" ? "Inscription portail" : "HelloAsso"}
                                          </Badge>
                                          <Badge variant="outline" className={reviewStatus.className}>
                                            {reviewStatus.label}
                                          </Badge>
                                        </div>
                                        <p className="text-sm text-muted-foreground">
                                          {buildDirectoryReviewSummary(review) || "Informations partielles"}
                                        </p>
                                        <div className="rounded-xl border border-amber-200 bg-amber-50/70 px-4 py-3 text-sm text-amber-950">
                                          {review.reason || "Validation manuelle requise."}
                                        </div>
                                      </div>

                                      <div className="flex w-full flex-col gap-2 sm:flex-row lg:w-auto lg:shrink-0">
                                        <Button type="button" size="sm" className="w-full sm:w-auto" onClick={() => openDirectoryEntryCreation(review)}>
                                          <Plus className="mr-2 h-4 w-4" />
                                          Créer la fiche
                                        </Button>
                                        <AdminOverflowMenu label={`Actions de validation pour ${review.proposedNomAssociation || "cette inscription"}`}>
                                          <DropdownMenuItem
                                            onClick={() => ignoreDirectoryReviewMutation.mutate({ reviewId: review.id })}
                                            disabled={ignoreDirectoryReviewMutation.isPending}
                                          >
                                            <XCircle className="h-4 w-4" />
                                            Ignorer
                                          </DropdownMenuItem>
                                        </AdminOverflowMenu>
                                      </div>
                                    </div>

                                    <div className="grid gap-4 2xl:grid-cols-[minmax(0,0.94fr)_minmax(0,1.06fr)]">
                                      <div className="min-w-0 rounded-2xl border border-slate-200 bg-slate-50/80 p-4">
                                        <div className="flex items-center justify-between gap-3">
                                          <p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500">
                                            Inscription reçue
                                          </p>
                                          <Badge variant="outline" className="border-slate-200 bg-white text-slate-700">
                                            Source entrante
                                          </Badge>
                                        </div>
                                        <div className="mt-4 grid gap-3 sm:grid-cols-2">
                                          {[
                                            ["Association", review.proposedNomAssociation || "-"],
                                            ["Représentant", review.proposedNomRepresentant || "-"],
                                            ["Téléphone", review.proposedTelephone || "-"],
                                            ["Email", review.proposedEmail || "-"],
                                            ["Ville", review.proposedVille || "-"],
                                            [
                                              "Identifiants",
                                              [review.proposedSiret && `SIRET ${review.proposedSiret}`, review.proposedRna && `RNA ${review.proposedRna}`]
                                                .filter(Boolean)
                                                .join(" • ") || "-",
                                            ],
                                          ].map(([label, value]) => (
                                            <div key={`${review.id}-${label}`} className="rounded-xl border border-slate-200 bg-white px-3 py-3">
                                              <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">{label}</p>
                                              <p className="mt-2 break-words text-sm font-medium text-slate-900">{value}</p>
                                            </div>
                                          ))}
                                        </div>
                                      </div>

                                      <div className="min-w-0 rounded-2xl border border-amber-200 bg-amber-50/60 p-4">
                                        <div className="flex flex-wrap items-center justify-between gap-2">
                                          <div>
                                            <p className="text-xs font-semibold uppercase tracking-[0.2em] text-amber-800">
                                              Fiches candidates
                                            </p>
                                            <p className="mt-1 text-sm text-amber-900/85">
                                              Le portail met en avant les fiches les plus proches pour faciliter la décision.
                                            </p>
                                          </div>
                                          {review.candidates?.length ? (
                                            <span className="rounded-full border border-emerald-200 bg-emerald-50 px-2.5 py-1 text-xs font-medium text-emerald-700">
                                              {review.candidates.length} proposition{review.candidates.length > 1 ? "s" : ""}
                                            </span>
                                          ) : null}
                                        </div>

                                        {review.candidates?.length ? (
                                          <div className="mt-4 space-y-3">
                                            {review.candidates.map((candidate, candidateIndex) => (
                                              <div key={`${review.id}-${candidate.id}`} className="min-w-0 rounded-xl border border-amber-200 bg-white p-4">
                                                <div className="flex flex-col gap-4">
                                                  <div className="min-w-0">
                                                    <div className="flex flex-wrap items-center gap-2">
                                                      <p className="font-semibold text-slate-900">{candidate.nomAssociation || `Fiche #${candidate.id}`}</p>
                                                      <Badge variant="outline">#{candidate.id}</Badge>
                                                      {candidateIndex === 0 ? (
                                                        <Badge className="border-emerald-200 bg-emerald-50 text-emerald-700 hover:bg-emerald-50">
                                                          Recommandé
                                                        </Badge>
                                                      ) : null}
                                                    </div>
                                                    <p className="mt-2 text-sm text-muted-foreground">
                                                      {[candidate.emailOfficiel, candidate.telephone, candidate.ville].filter(Boolean).join(" • ") || "Informations partielles"}
                                                    </p>
                                                  </div>
                                                  <div className="grid gap-3 md:grid-cols-2">
                                                    <div className="rounded-xl bg-slate-50 px-3 py-3">
                                                      <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Représentant</p>
                                                      <p className="mt-2 text-sm text-slate-900">
                                                        {candidate.nomRepresentant || "Non renseigné"}
                                                      </p>
                                                    </div>
                                                    <div className="rounded-xl bg-slate-50 px-3 py-3">
                                                      <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Identifiants</p>
                                                      <p className="mt-2 text-sm text-slate-900">
                                                        {[candidate.siret && `SIRET ${candidate.siret}`, candidate.rna && `RNA ${candidate.rna}`]
                                                          .filter(Boolean)
                                                          .join(" • ") || "Non renseignés"}
                                                      </p>
                                                    </div>
                                                  </div>
                                                  <div className="flex flex-col gap-2 sm:flex-row sm:justify-end">
                                                    <Button
                                                      type="button"
                                                      size="sm"
                                                      variant={candidateIndex === 0 ? "default" : "outline"}
                                                      className="w-full sm:w-auto sm:min-w-[170px]"
                                                      onClick={() =>
                                                        resolveDirectoryReviewLinkMutation.mutate({
                                                          reviewId: review.id,
                                                          directoryEntryId: candidate.id,
                                                        })
                                                      }
                                                      disabled={resolveDirectoryReviewLinkMutation.isPending}
                                                    >
                                                      Lier à la fiche
                                                    </Button>
                                                  </div>
                                                </div>
                                              </div>
                                            ))}
                                          </div>
                                        ) : (
                                          <div className="mt-4 rounded-xl border border-dashed border-amber-300 bg-white/80 px-4 py-4 text-sm leading-6 text-amber-950">
                                            Aucune fiche du bordereau ne correspond de façon assez fiable à cette association. Vous pouvez créer une nouvelle fiche sans risque de fusion automatique.
                                          </div>
                                        )}
                                      </div>
                                    </div>
                                  </div>
                                </div>
                              );
                            })}
                          </div>
                        </div>
                      ) : null}

                      <div className="rounded-2xl border bg-gradient-to-br from-slate-50 via-white to-slate-50/70 p-4 shadow-sm">
                        <div className="flex flex-col gap-4">
                          <div className="flex flex-col gap-3 xl:flex-row xl:items-end xl:justify-between">
                            <div className="space-y-1">
                              <p className="text-sm font-semibold text-foreground">Filtres du référentiel</p>
                              <p className="text-sm text-muted-foreground">
                                Affine la vue par recherche, statut d’inscription, commune, thématique et provenance métier.
                              </p>
                            </div>
                            <div className="relative w-full xl:max-w-[340px]">
                              <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
                              <Input
                                placeholder="Rechercher une association, un SIRET ou un RNA..."
                                value={directorySearch}
                                onChange={(e) => setDirectorySearch(e.target.value)}
                                className="h-11 rounded-xl border-slate-200 bg-white pl-9"
                              />
                            </div>
                          </div>

                          <div className="grid gap-4 xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]">
                            <div className="space-y-3">
                              <div className="space-y-2">
                                <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Statut de compte</p>
                                <div className="flex flex-wrap gap-2">
                                  {[
                                    { value: "all", label: "Toutes" },
                                    { value: "registered", label: "Inscrites" },
                                    { value: "unregistered", label: "Pas inscrites" },
                                  ].map((option) => (
                                    <Button
                                      key={option.value}
                                      type="button"
                                      size="sm"
                                      variant={directoryStatusFilter === option.value ? "default" : "outline"}
                                      className="rounded-full"
                                      onClick={() => setDirectoryStatusFilter(option.value as "all" | "registered" | "unregistered")}
                                    >
                                      {option.label}
                                    </Button>
                                  ))}
                                </div>
                              </div>

                              <div className="space-y-2">
                                <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Commune</p>
                                <div className="flex flex-wrap gap-2">
                                  {associationCommuneOptions.map((option) => (
                                    <Button
                                      key={`directory-${option.value}`}
                                      type="button"
                                      variant={directoryCommuneFilter === option.value ? "default" : "outline"}
                                      size="sm"
                                      className="rounded-full"
                                      onClick={() => setDirectoryCommuneFilter(option.value)}
                                    >
                                      {option.label}
                                    </Button>
                                  ))}
                                </div>
                              </div>
                            </div>

                            <div className="space-y-3">
                              <div className="space-y-2">
                                <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Thématique</p>
                                <div className="max-w-sm">
                                  <Select
                                    value={directoryThematiqueFilter}
                                    onValueChange={(value) => setDirectoryThematiqueFilter(value as AssociationThematic | "all")}
                                  >
                                    <SelectTrigger className="h-11 rounded-xl border-slate-200 bg-white">
                                      <SelectValue placeholder="Filtrer par catégorie" />
                                    </SelectTrigger>
                                    <SelectContent>
                                      <SelectItem value="all">Toutes les thématiques</SelectItem>
                                      {associationThematicOptions.map((option) => (
                                        <SelectItem key={option.value} value={option.value}>
                                          {option.label}
                                        </SelectItem>
                                      ))}
                                    </SelectContent>
                                  </Select>
                                </div>
                              </div>
                            </div>
                          </div>

                          <div className="rounded-2xl border border-slate-200 bg-white/90 p-3">
                            <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
                              <div className="min-w-0">
                                <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Vue active</p>
                                <p className="mt-1 text-sm text-slate-700">
                                  {filteredDirectoryEntries.length} association(s) visibles
                                  {directoryProvenanceFilter !== "all" ? " dans le segment sélectionné" : " dans le référentiel courant"}
                                </p>
                              </div>
                              <div className="flex flex-wrap gap-2">
                                {directoryStatusFilter !== "all" ? (
                                  <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                    {directoryStatusFilter === "registered" ? "Inscrites" : "Pas inscrites"}
                                  </Badge>
                                ) : null}
                                {directoryCommuneFilter !== "all" ? (
                                  <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                    {getAssociationCommuneLabel(directoryCommuneFilter)}
                                  </Badge>
                                ) : null}
                                {directoryThematiqueFilter !== "all" ? (
                                  <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                    {getAssociationThematicLabel(directoryThematiqueFilter)}
                                  </Badge>
                                ) : null}
                                {directoryProvenanceFilter !== "all" ? (
                                  <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                    {({
                                      auto_linked: "Rattachement auto",
                                      pending_validation: "À valider",
                                      manual_entry: "Création manuelle",
                                      imported: "Import bordereau",
                                      portal_enriched: "Enrichie portail",
                                      helloasso_synced: "HelloAsso",
                                    } as Record<DirectoryProvenanceStatus, string>)[directoryProvenanceFilter]}
                                  </Badge>
                                ) : null}
                                {directorySearch.trim() ? (
                                  <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                                    Recherche : {directorySearch.trim()}
                                  </Badge>
                                ) : null}
                              </div>
                            </div>
                            {(directoryStatusFilter !== "all" ||
                              directoryCommuneFilter !== "all" ||
                              directoryThematiqueFilter !== "all" ||
                              directoryProvenanceFilter !== "all" ||
                              directorySearch.trim()) && (
                              <div className="mt-3 flex justify-end">
                                <Button
                                  type="button"
                                  size="sm"
                                  variant="ghost"
                                  className="text-slate-600 hover:text-slate-900"
                                  onClick={() => {
                                    setDirectoryStatusFilter("all");
                                    setDirectoryCommuneFilter("all");
                                    setDirectoryThematiqueFilter("all");
                                    setDirectoryProvenanceFilter("all");
                                    setDirectorySearch("");
                                  }}
                                >
                                  Réinitialiser les filtres
                                </Button>
                              </div>
                            )}
                          </div>
                        </div>
                      </div>

                      <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
                        {[
                          { value: "auto_linked", label: "Rattachées automatiquement", count: directoryProvenanceSummary.auto_linked, className: "border-emerald-200 bg-emerald-50 text-emerald-700" },
                          { value: "pending_validation", label: "À valider", count: directoryProvenanceSummary.pending_validation, className: "border-amber-200 bg-amber-50 text-amber-800" },
                          { value: "manual_entry", label: "Créées manuellement", count: directoryProvenanceSummary.manual_entry, className: "border-violet-200 bg-violet-50 text-violet-700" },
                          { value: "imported", label: "Importées", count: directoryProvenanceSummary.imported, className: "border-slate-200 bg-slate-100 text-slate-700" },
                          { value: "portal_enriched", label: "Enrichies portail", count: directoryProvenanceSummary.portal_enriched, className: "border-blue-200 bg-blue-50 text-blue-700" },
                          { value: "helloasso_synced", label: "HelloAsso", count: directoryProvenanceSummary.helloasso_synced, className: "border-sky-200 bg-sky-50 text-sky-700" },
                        ].map((item) => (
                          <button
                            key={item.value}
                            type="button"
                            onClick={() => setDirectoryProvenanceFilter((prev) => (prev === item.value ? "all" : item.value as DirectoryProvenanceStatus))}
                            className={cn(
                              "rounded-2xl border p-4 text-left shadow-sm transition-all",
                              item.className,
                              directoryProvenanceFilter === item.value ? "ring-2 ring-primary/25 shadow-md" : "opacity-95 hover:-translate-y-0.5 hover:opacity-100 hover:shadow-md"
                            )}
                          >
                            <p className="text-[11px] font-semibold uppercase tracking-[0.18em]">Provenance</p>
                            <div className="mt-3 flex items-start justify-between gap-3">
                              <div>
                                <p className="font-semibold">{item.label}</p>
                                <p className="mt-1 text-xs opacity-80">
                                  {item.value === "auto_linked"
                                    ? "Comptes déjà reliés à une fiche connue."
                                    : item.value === "pending_validation"
                                      ? "Demandent encore une vérification humaine."
                                      : item.value === "manual_entry"
                                        ? "Créées ou reprises directement par l’administration."
                                        : item.value === "imported"
                                          ? "Présentes dans le bordereau sans compte relié."
                                          : item.value === "portal_enriched"
                                            ? "Fiches enrichies depuis le portail."
                                            : "Mises à jour via la source HelloAsso."}
                                </p>
                              </div>
                              <span className="text-2xl font-semibold leading-none">{item.count}</span>
                            </div>
                          </button>
                        ))}
                      </div>

                      <div className="flex flex-wrap gap-2">
                        <Button
                          type="button"
                          size="sm"
                          variant={directoryProvenanceFilter === "all" ? "default" : "outline"}
                          className="rounded-full"
                          onClick={() => setDirectoryProvenanceFilter("all")}
                        >
                          Toutes provenances
                        </Button>
                        {([
                          ["auto_linked", "Rattachée auto"],
                          ["pending_validation", "À valider"],
                          ["manual_entry", "Créée manuellement"],
                          ["imported", "Importée"],
                          ["portal_enriched", "Enrichie portail"],
                          ["helloasso_synced", "HelloAsso"],
                        ] as Array<[DirectoryProvenanceStatus, string]>).map(([value, label]) => (
                          <Button
                            key={value}
                            type="button"
                            size="sm"
                            variant={directoryProvenanceFilter === value ? "default" : "outline"}
                            className="rounded-full"
                            onClick={() => setDirectoryProvenanceFilter(value)}
                          >
                            {label}
                          </Button>
                        ))}
                      </div>

                      <p className="text-sm text-muted-foreground">
                        {filteredDirectoryEntries.length} association(s) dans cette vue
                        {directoryStatusFilter === "registered"
                          ? " déjà rattachée(s) à un compte"
                          : directoryStatusFilter === "unregistered"
                            ? " encore non enregistrée(s)"
                            : ""}
                        {directoryCommuneFilter !== "all" ? ` • ${getAssociationCommuneLabel(directoryCommuneFilter)}` : ""}
                        {directoryThematiqueFilter !== "all" ? ` • ${getAssociationThematicLabel(directoryThematiqueFilter)}` : ""}
                        {directoryProvenanceFilter !== "all"
                          ? ` • ${(
                              {
                                auto_linked: "rattachement automatique",
                                pending_validation: "validation requise",
                                manual_entry: "création manuelle",
                                imported: "import bordereau",
                                portal_enriched: "enrichissement portail",
                                helloasso_synced: "synchronisation HelloAsso",
                              } as Record<DirectoryProvenanceStatus, string>
                            )[directoryProvenanceFilter]}`
                          : ""}
                      </p>

                      <ScrollArea className="h-[420px] xl:h-[680px] pr-4">
                        <div className="space-y-3">
                          {filteredDirectoryEntries.map((entry) => (
                            <div key={entry.id} className="rounded-2xl border border-slate-200 bg-gradient-to-br from-white via-white to-slate-50/70 p-4 shadow-sm transition-all hover:border-primary/25 hover:shadow-md">
                              {(() => {
                                const geoStatus = getDirectoryGeoStatus(entry);
                                const provenance = getDirectoryProvenanceConfig(entry);

                                return (
                              <div className="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
                                <div className="min-w-0 flex-1">
                                  <div className="flex flex-wrap items-center gap-2">
                                    <p className="text-base font-semibold text-slate-950">{entry.nomAssociation}</p>
                                    <Badge variant="outline" className={cn("font-medium", geoStatus.className)}>
                                      {geoStatus.label}
                                    </Badge>
                                    <Badge variant="outline" className={cn("font-medium", provenance.className)}>
                                      {provenance.label}
                                    </Badge>
                                  </div>
                                  <p className="mt-2 text-sm leading-6 text-slate-600">
                                    {provenance.description}
                                  </p>
                                  <div className="mt-3 grid gap-3 lg:grid-cols-[minmax(0,1.2fr)_minmax(0,0.8fr)]">
                                    <div className="min-w-0 rounded-xl border border-slate-200 bg-white/80 px-3 py-3">
                                      <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Coordonnées utiles</p>
                                      <p className="mt-2 break-words text-sm text-slate-800">
                                        {entry.emailOfficiel || "Aucun email"}{entry.ville ? ` • ${entry.ville}` : ""}
                                      </p>
                                      <div className="mt-3 flex flex-wrap gap-2">
                                        {entry.siret ? (
                                          <span className="rounded-full border border-slate-200 bg-white px-2.5 py-1 text-xs font-medium text-slate-600">SIRET : {entry.siret}</span>
                                        ) : null}
                                        {entry.rna ? (
                                          <span className="rounded-full border border-slate-200 bg-white px-2.5 py-1 text-xs font-medium text-slate-600">RNA : {entry.rna}</span>
                                        ) : null}
                                        {!entry.siret && !entry.rna ? (
                                          <span className="rounded-full border border-dashed border-slate-200 bg-slate-50 px-2.5 py-1 text-xs text-slate-500">
                                            Identifiants non renseignés
                                          </span>
                                        ) : null}
                                      </div>
                                    </div>
                                    <div className="min-w-0 rounded-xl border border-slate-200 bg-slate-50/70 px-3 py-3">
                                      <div className="flex flex-wrap items-center gap-2">
                                      <Badge
                                        variant="outline"
                                        className={entry.registered ? "border-green-200 bg-green-50 text-green-700" : "border-blue-200 bg-blue-50 text-blue-700"}
                                      >
                                        {entry.registered ? "Inscrite" : "Pas encore inscrite"}
                                      </Badge>
                                      <Badge variant="outline" className="border-slate-200 bg-white text-slate-700">#{entry.id}</Badge>
                                      </div>
                                      <p className="mt-3 text-xs leading-5 text-slate-600">
                                        Importé le {formatDateTime(entry.importedAt)}{entry.sourceFileName ? ` via ${entry.sourceFileName}` : ""}
                                      </p>
                                    </div>
                                  </div>
                                  {entry.thematiques?.length ? (
                                    <div className="mt-3 flex flex-wrap gap-2">
                                      {getAssociationThematicLabels(entry.thematiques).map((label) => (
                                        <span key={label} className="rounded-full border border-primary/15 bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary">
                                          {label}
                                        </span>
                                      ))}
                                    </div>
                                  ) : null}
                                </div>
                              </div>
                                );
                              })()}
                              <div className="mt-4 border-t border-slate-200/80 pt-3">
                              {entry.registeredAssociationId ? (
                                <div className="mt-3 flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
                                  <p className="text-xs font-medium text-green-700">
                                    Compte créé le {formatDate(entry.registeredAt)} • association #{entry.registeredAssociationId}
                                  </p>
                                  <div className="flex flex-wrap gap-2">
                                    <Button
                                      type="button"
                                      size="sm"
                                      variant="outline"
                                      disabled={!isHelloAssoConfigured || syncDirectoryHelloAssoMutation.isPending}
                                      onClick={() => syncDirectoryHelloAssoMutation.mutate({ id: entry.id })}
                                    >
                                      {syncDirectoryHelloAssoMutation.isPending ? (
                                        <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                                      ) : (
                                        <RefreshCw className="mr-2 h-4 w-4" />
                                      )}
                                      Synchroniser HelloAsso
                                    </Button>
                                    <Button
                                      type="button"
                                      size="sm"
                                      variant="outline"
                                      onClick={() => setSelectedDirectoryEntryId(entry.id)}
                                    >
                                      <Eye className="mr-2 h-4 w-4" />
                                      Voir la fiche
                                    </Button>
                                  </div>
                                </div>
                              ) : (
                                <div className="mt-3 space-y-3">
                                  <div className="rounded-xl border border-slate-200 bg-slate-50 px-3 py-3 text-xs text-muted-foreground">
                                    <p className="font-medium text-foreground">État d’invitation : {getInvitationStatusLabel(entry)}</p>
                                    {entry.invitationStatus?.sentAt ? (
                                      <p>Envoyée le {formatDateTime(entry.invitationStatus.sentAt)}</p>
                                    ) : null}
                                    {entry.invitationStatus?.expiresAt ? (
                                      <p>Expire le {formatDateTime(entry.invitationStatus.expiresAt)}</p>
                                    ) : null}
                                    {entry.invitationStatus?.status === "sent" && !entry.invitationStatus.emailSent ? (
                                      <p className="text-amber-700">Email non envoyé automatiquement, lien sécurisé disponible.</p>
                                    ) : null}
                                  </div>
                                  <div className="flex flex-wrap gap-2">
                                    <Button
                                      type="button"
                                      size="sm"
                                      variant="outline"
                                      disabled={!isHelloAssoConfigured || syncDirectoryHelloAssoMutation.isPending}
                                      onClick={() => syncDirectoryHelloAssoMutation.mutate({ id: entry.id })}
                                    >
                                      {syncDirectoryHelloAssoMutation.isPending ? (
                                        <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                                      ) : (
                                        <RefreshCw className="mr-2 h-4 w-4" />
                                      )}
                                      Synchroniser HelloAsso
                                    </Button>
                                    <Button
                                      type="button"
                                      size="sm"
                                      variant="outline"
                                      onClick={() => setSelectedDirectoryEntryId(entry.id)}
                                    >
                                      <Eye className="mr-2 h-4 w-4" />
                                      Voir la fiche
                                    </Button>
                                    <Button
                                      type="button"
                                      size="sm"
                                      disabled={!entry.emailOfficiel || sendDirectoryInvitationMutation.isPending}
                                      onClick={() => sendDirectoryInvitationMutation.mutate({ directoryEntryId: entry.id })}
                                    >
                                      <Mail className="mr-2 h-4 w-4" />
                                      Inviter par email
                                    </Button>
                                    <Button
                                      type="button"
                                      size="sm"
                                      variant="outline"
                                      disabled={!entry.emailOfficiel || secureDirectoryInvitationLinkMutation.isPending}
                                      onClick={() => copyDirectorySecureLink(entry.id)}
                                    >
                                      <Copy className="mr-2 h-4 w-4" />
                                      Copier le lien sécurisé
                                    </Button>
                                  </div>
                                </div>
                              )}
                              </div>
                            </div>
                          ))}
                          {filteredDirectoryEntries.length === 0 && (
                            <div className="text-center py-12 text-muted-foreground">
                              <Database className="h-12 w-12 mx-auto mb-4 opacity-50" />
                              <p>Aucune association ne correspond aux filtres actuels</p>
                            </div>
                          )}
                        </div>
                      </ScrollArea>
                    </CardContent>
                  </Card>
                </section>
	              </div>
	            </TabsContent>
	          )}
	          {/* Audit Log Tab */}
          <TabsContent value="audit" className="space-y-4">
            <AdminSurface
              title="Journal d’audit"
              description="Historique des actions administratives pour garder une trace claire des opérations sensibles, des entités touchées et de leur chronologie."
              badge={
                <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                  {auditLogs?.data?.length || 0} entrée(s)
                </Badge>
              }
            >
              <div className="grid gap-4 md:grid-cols-3">
                <AdminMetricTile
                  label="Entrées visibles"
                  value={auditLogs?.data?.length || 0}
                  tone="info"
                  detail="Historique remonté dans la session actuelle"
                />
                <AdminMetricTile
                  label="Avec IP"
                  value={(auditLogs?.data || []).filter((log) => Boolean(log.ipAddress)).length}
                  detail="Événements enrichis d’une source réseau"
                />
                <AdminMetricTile
                  label="Dernière trace"
                  value={auditLogs?.data?.[0]?.createdAt ? formatDate(auditLogs.data[0].createdAt) : "-"}
                  tone="warning"
                  detail="Dernier événement disponible"
                />
              </div>
            </AdminSurface>

            <AdminSectionCollapsible
              title="Historique détaillé"
              description="Déplie ce bloc pour relire les dernières actions sensibles sans perdre la vue d’ensemble du journal."
              open={!adminCollapsedSections.auditHistory}
              onOpenChange={(open) => setAdminSectionExpanded("auditHistory", open)}
              className="bg-white"
              contentClassName="pt-1"
              headerBadge={
                <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                  Fenêtre de 20 événements
                </Badge>
              }
            >
              <ScrollArea className="h-[500px]">
                <div className="space-y-3">
                  {auditLogs?.data?.map((log) => (
                    <div key={log.id} className="flex items-start gap-4 rounded-2xl border border-border/70 bg-white/95 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:bg-muted/40 hover:shadow-sm">
                      <div className="flex-1">
                        <div className="flex flex-wrap items-center gap-2">
                          <Badge variant="outline">{log.action}</Badge>
                          <span className="text-sm">{log.entityType}</span>
                          {log.entityId && (
                            <span className="text-sm text-muted-foreground">#{log.entityId}</span>
                          )}
                        </div>
                        <p className="text-xs text-muted-foreground mt-1">
                          Par utilisateur #{log.userId}
                          {log.ipAddress ? ` • ${log.ipAddress}` : ""}
                          {" • "}
                          {formatDateTime(log.createdAt)}
                        </p>
                      </div>
                    </div>
                  ))}
                  {(!auditLogs?.data || auditLogs.data.length === 0) && (
                    <div className="text-center py-12 text-muted-foreground">
                      <FileText className="h-12 w-12 mx-auto mb-4 opacity-50" />
                      <p>Aucune entrée dans le journal</p>
                    </div>
                  )}
                </div>
              </ScrollArea>
            </AdminSectionCollapsible>
          </TabsContent>
          <TabsContent value="conformite" className="space-y-4">
            <AdminSurface
              title="Centre conformité RGPD & ISO 27001"
              description="Vue de pilotage interne pour suivre l’écart entre le portail actuel, les mesures déjà en place et les chantiers restant à démontrer."
              badge={
                <Badge variant="outline" className="border-violet-200 bg-violet-50 text-violet-700">
                  {complianceSnapshot?.summary.readinessLabel || "Chargement..."}
                </Badge>
              }
              className="border-primary/15 bg-primary/[0.03]"
            >
              <div className="space-y-4">
                <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                  <AdminMetricTile
                    label="Niveau de lecture"
                    value={complianceSnapshot?.summary.readinessLabel || "Chargement..."}
                    tone="info"
                    detail="État synthétique du socle conformité"
                  />
                  <AdminMetricTile
                    label="Mesures en place"
                    value={complianceSnapshot?.summary.implementedCount ?? 0}
                    tone="success"
                    detail="Contrôles déjà branchés ou documentés"
                  />
                  <AdminMetricTile
                    label="Points partiels"
                    value={complianceSnapshot?.summary.partialCount ?? 0}
                    tone="warning"
                    detail="Chantiers engagés mais encore incomplets"
                  />
                  <AdminMetricTile
                    label="Écarts prioritaires"
                    value={complianceSnapshot?.summary.missingCount ?? 0}
                    detail="Éléments encore attendus pour l’audit"
                  />
                </div>

                <div className="grid gap-4 md:grid-cols-3">
                  <AdminMetricTile
                    label="Utilisateurs total"
                    value={complianceSnapshot?.platform.totalUsers ?? 0}
                    detail="Base de comptes suivie par le portail"
                  />
                  <AdminMetricTile
                    label="Comptes internes actifs"
                    value={complianceSnapshot?.platform.activeInternalUsers ?? 0}
                    tone="info"
                    detail="Population la plus sensible côté exploitation"
                  />
                  <AdminMetricTile
                    label="Entrées du journal d’audit"
                    value={complianceSnapshot?.platform.auditEntries ?? 0}
                    detail="Trace disponible pour les contrôles internes"
                  />
                </div>

                <div className="rounded-lg border bg-white/80 p-4 space-y-4">
                  <div className="flex flex-wrap items-start justify-between gap-3">
                    <div>
                      <p className="text-sm text-muted-foreground">Politique MFA interne</p>
                      <p className="mt-2 text-lg font-semibold">Authenticator cible par role sensible</p>
                      <p className="mt-1 text-sm text-muted-foreground">
                        MFA obligatoire pour les rôles internes, avec bascule progressive vers Authenticator comme méthode de référence.
                      </p>
                    </div>
                    <div className="flex flex-wrap gap-2">
                      <Badge className="bg-green-100 text-green-700">
                        Cible : {complianceSnapshot?.mfaPolicy?.targetMethod === "authenticator_app" ? "Authenticator" : "MFA"}
                      </Badge>
                      <Badge variant="outline">
                        Secours : {complianceSnapshot?.mfaPolicy?.fallbackMethod === "email_otp" ? "Code email" : "Aucun"}
                      </Badge>
                    </div>
                  </div>

                  <div className="grid gap-4 md:grid-cols-4">
                    <div className="rounded-lg border p-3">
                      <p className="text-xs text-muted-foreground">Comptes internes actifs</p>
                      <p className="mt-2 text-xl font-semibold">{complianceSnapshot?.mfaPolicy?.totpCoverage?.activeInternalUsers ?? 0}</p>
                    </div>
                    <div className="rounded-lg border p-3">
                      <p className="text-xs text-muted-foreground">Authenticator actif</p>
                      <p className="mt-2 text-xl font-semibold">{complianceSnapshot?.mfaPolicy?.totpCoverage?.authenticatorEnabled ?? 0}</p>
                    </div>
                    <div className="rounded-lg border p-3">
                      <p className="text-xs text-muted-foreground">Super admins</p>
                      <p className="mt-2 text-xl font-semibold">{complianceSnapshot?.mfaPolicy?.totpCoverage?.superAdmins ?? 0}</p>
                    </div>
                    <div className="rounded-lg border p-3">
                      <p className="text-xs text-muted-foreground">Super admins alignes</p>
                      <p className="mt-2 text-xl font-semibold">{complianceSnapshot?.mfaPolicy?.totpCoverage?.superAdminsAligned ?? 0}</p>
                    </div>
                  </div>

                  <div className="grid gap-3 md:grid-cols-3">
                    {(complianceSnapshot?.mfaPolicy?.phases || []).map((phase) => (
                      <div key={phase} className="rounded-lg border p-3 text-sm text-muted-foreground">
                        {phase}
                      </div>
                    ))}
                  </div>
                </div>

                <p className="text-sm text-muted-foreground">{complianceSnapshot?.disclaimer}</p>
              </div>
            </AdminSurface>

            {([
              ["Technique", "complianceTechnique", complianceSnapshot?.checks.technique || []],
              ["Juridique", "complianceJuridique", complianceSnapshot?.checks.juridique || []],
              ["Organisation", "complianceOrganisation", complianceSnapshot?.checks.organisation || []],
            ] as const).map(([sectionLabel, collapseKey, items]) => (
              <AdminSectionCollapsible
                key={sectionLabel}
                title={sectionLabel}
                description={`Lecture détaillée des contrôles ${sectionLabel.toLowerCase()}s pour ouvrir uniquement le niveau de détail utile.`}
                open={!adminCollapsedSections[collapseKey]}
                onOpenChange={(open) => setAdminSectionExpanded(collapseKey, open)}
                className="bg-white"
                contentClassName="space-y-3 pt-1"
                headerBadge={
                  <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                    {items.length} point(s)
                  </Badge>
                }
              >
                {items.map((item) => (
                  <div key={item.id} className="rounded-lg border p-4">
                    <div className="flex flex-wrap items-center justify-between gap-3">
                      <div>
                        <p className="font-medium">{item.label}</p>
                        <p className="text-sm text-muted-foreground mt-1">{item.summary}</p>
                        {item.detail ? (
                          <p className="text-xs text-muted-foreground mt-2">{item.detail}</p>
                        ) : null}
                      </div>
                      <Badge
                        className={cn(
                          item.status === "implemented" && "bg-green-100 text-green-700",
                          item.status === "partial" && "bg-amber-100 text-amber-800",
                          item.status === "missing" && "bg-red-100 text-red-700",
                        )}
                      >
                        {item.status === "implemented"
                          ? "En place"
                          : item.status === "partial"
                            ? "Partiel"
                            : "A faire"}
                      </Badge>
                    </div>
                  </div>
                ))}
              </AdminSectionCollapsible>
            ))}

            <AdminSectionCollapsible
              title="Publications et dossiers de référence"
              description="Base documentaire intégrée et liens publics déjà exposables dans le portail."
              open={!adminCollapsedSections.compliancePublications}
              onOpenChange={(open) => setAdminSectionExpanded("compliancePublications", open)}
              className="bg-white"
              contentClassName="grid gap-3 pt-1 md:grid-cols-2 xl:grid-cols-4"
            >
              <Link href="/confidentialite?returnTo=%2Fadmin%3Ftab%3Dcompliance" {...buildRoutePrefetchHandlers("/confidentialite?returnTo=%2Fadmin%3Ftab%3Dcompliance")}><Button variant="outline" className="w-full justify-between"><span>Confidentialité</span><ArrowUpRight className="h-4 w-4" /></Button></Link>
              <Link href="/mentions-legales?returnTo=%2Fadmin%3Ftab%3Dcompliance" {...buildRoutePrefetchHandlers("/mentions-legales?returnTo=%2Fadmin%3Ftab%3Dcompliance")}><Button variant="outline" className="w-full justify-between"><span>Mentions légales</span><ArrowUpRight className="h-4 w-4" /></Button></Link>
              <Link href="/cookies?returnTo=%2Fadmin%3Ftab%3Dcompliance" {...buildRoutePrefetchHandlers("/cookies?returnTo=%2Fadmin%3Ftab%3Dcompliance")}><Button variant="outline" className="w-full justify-between"><span>Cookies</span><ArrowUpRight className="h-4 w-4" /></Button></Link>
              <Link href="/cgu?returnTo=%2Fadmin%3Ftab%3Dcompliance" {...buildRoutePrefetchHandlers("/cgu?returnTo=%2Fadmin%3Ftab%3Dcompliance")}><Button variant="outline" className="w-full justify-between"><span>CGU</span><ArrowUpRight className="h-4 w-4" /></Button></Link>
            </AdminSectionCollapsible>

            <AdminSectionCollapsible
              title="Suite recommandée"
              description="Les prochains chantiers utiles pour garder le pilotage conformité lisible et actionnable."
              open={!adminCollapsedSections.complianceNextSteps}
              onOpenChange={(open) => setAdminSectionExpanded("complianceNextSteps", open)}
              className="bg-white"
              contentClassName="space-y-2 pt-1 text-sm text-muted-foreground"
            >
              {(complianceSnapshot?.nextSteps || []).map((step) => (
                <div key={step} className="rounded-lg border p-3">
                  {step}
                </div>
              ))}
            </AdminSectionCollapsible>

            <AdminSectionCollapsible
              title="Centre opérationnel de conformité"
              description="Déplie ce bloc quand tu veux piloter le détail opérationnel, puis replie-le pour garder l’entrée conformité plus respirante."
              open={!adminCollapsedSections.complianceOperations}
              onOpenChange={(open) => setAdminSectionExpanded("complianceOperations", open)}
              className="bg-white"
              contentClassName="pt-1"
              headerBadge={
                <Badge variant="outline" className="border-violet-200 bg-violet-50 text-violet-700">
                  Pilotage avancé
                </Badge>
              }
            >
              <Suspense fallback={<AdminPanelLoader compact />}>
                <ComplianceOperationsPanel />
              </Suspense>
            </AdminSectionCollapsible>
          </TabsContent>
          {/* Calendrier Tab */}
          <TabsContent value="calendrier" className="space-y-4">
            <AdminSurface
              title="Calendrier d’exploitation"
              description="Lecture consolidée des réservations, disponibilités et charges à venir pour garder une vue terrain claire sur desktop comme sur tablette."
            >
              <AdminSectionCollapsible
                title="Calendrier détaillé"
                description="Garde ce bloc repliable pour revenir vite à la synthèse, puis ouvre-le quand tu as besoin du planning complet."
                open={!adminCollapsedSections.calendarBoard}
                onOpenChange={(open) => setAdminSectionExpanded("calendarBoard", open)}
                className="border-0 bg-transparent shadow-none"
                contentClassName="px-0 pb-0 pt-1"
                headerBadge={
                  <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                    Vue complète
                  </Badge>
                }
              >
                <Suspense fallback={<AdminPanelLoader />}>
                  <ReservationCalendar />
                </Suspense>
              </AdminSectionCollapsible>
            </AdminSurface>
          </TabsContent>

          <TabsContent value="logistique" className="space-y-6">
            <AdminSurface
              title="Règles logistiques et stock matériel"
              description="Réglages structurants pour l’assignation terrain, les délais de restitution, le stock théorique et les alertes de conflit."
              badge={
                <Badge variant="outline" className="border-orange-200 bg-orange-50 text-orange-700">
                  {materialEventItems.length} article(s) piloté(s)
                </Badge>
              }
            >
              <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                <AdminMetricTile
                  label="Envoi avant restitution"
                  value={`${logisticsSettings?.materialReturnLeadDays ?? 4} jour(s)`}
                  tone="info"
                  detail="Déclenchement automatique du lien terrain"
                />
                <AdminMetricTile
                  label="Délai de restitution"
                  value={`${logisticsSettings?.materialReturnGraceDays ?? 3} jour(s)`}
                  tone="warning"
                  detail="Avant passage en alerte / retard"
                />
                <AdminMetricTile
                  label="Articles pilotés"
                  value={materialEventItems.length}
                  tone="success"
                  detail="Matériels suivis dans le portail"
                />
                <AdminMetricTile
                  label="Dernière mise à jour"
                  value={formatDate(logisticsSettings?.updatedAt || null)}
                  detail="Synchronisée avec calendrier et formulaires"
                />
              </div>
            </AdminSurface>

            <AdminSectionCollapsible
              title="Réglages détaillés et stock"
              description="Garde ce bloc fermé quand tu pilotes seulement les retours, puis ouvre-le pour modifier les délais, le stock et les valeurs de remplacement."
              open={!adminCollapsedSections.logisticsSettings}
              onOpenChange={(open) => setAdminSectionExpanded("logisticsSettings", open)}
              className="bg-white"
              contentClassName="pt-1"
              headerBadge={
                <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                  Dernière mise à jour {formatDate(logisticsSettings?.updatedAt || null)}
                </Badge>
              }
            >
              <div className="space-y-6">
                <div className="grid gap-6 xl:grid-cols-[0.9fr_1.1fr]">
                  <section className="space-y-4">
                    <div className="grid gap-4 md:grid-cols-2">
                      <div className="space-y-2">
                        <Label htmlFor="materialReturnLeadDays">Envoi automatique J-n</Label>
                        <Input
                          id="materialReturnLeadDays"
                          type="number"
                          min={1}
                          max={30}
                          value={logisticsSettingsForm.materialReturnLeadDays}
                          onChange={(e) =>
                            setLogisticsSettingsForm((prev) => ({
                              ...prev,
                              materialReturnLeadDays: Number.parseInt(e.target.value || "0", 10) || 1,
                            }))
                          }
                          disabled={user?.role !== "super_admin"}
                        />
                      </div>
                      <div className="space-y-2">
                        <Label htmlFor="materialReturnGraceDays">Délai max après restitution</Label>
                        <Input
                          id="materialReturnGraceDays"
                          type="number"
                          min={0}
                          max={30}
                          value={logisticsSettingsForm.materialReturnGraceDays}
                          onChange={(e) =>
                            setLogisticsSettingsForm((prev) => ({
                              ...prev,
                              materialReturnGraceDays: Number.parseInt(e.target.value || "0", 10) || 0,
                            }))
                          }
                          disabled={user?.role !== "super_admin"}
                        />
                      </div>
                    </div>

                    <div className="rounded-lg border border-dashed p-4 text-sm text-muted-foreground">
                      Le portail utilisera ces valeurs pour l’assignation logistique, le scheduler J-n, les retards, la date de restitution proposée aux associations et les alertes du calendrier.
                    </div>
                  </section>

                  <section className="space-y-4">
                    <div className="grid gap-4 md:grid-cols-2">
                      {materialEventItems.map((item) => (
                        <div key={item.key} className="rounded-lg border p-4 space-y-2">
                          <Label htmlFor={`inventory-${item.key}`}>{item.label}</Label>
                          <Input
                            id={`inventory-${item.key}`}
                            type="number"
                            min={0}
                            value={logisticsSettingsForm.inventory[item.key] ?? ""}
                            onChange={(e) =>
                              setLogisticsSettingsForm((prev) => ({
                                ...prev,
                                inventory: {
                                  ...prev.inventory,
                                  [item.key]: e.target.value,
                                },
                              }))
                            }
                            placeholder={item.key === "autres" ? "Laisser vide pour suivi libre" : "0"}
                            disabled={user?.role !== "super_admin"}
                          />
                          <p className="text-xs text-muted-foreground">
                            {item.key === "autres"
                              ? "Vide = quantité non pilotée par le portail."
                              : "Quantité théorique disponible pour les réservations et conflits."}
                          </p>
                          <div className="pt-2 space-y-2">
                            <Label htmlFor={`replacement-${item.key}`}>Valeur de remplacement à neuf (EUR)</Label>
                            <Input
                              id={`replacement-${item.key}`}
                              type="number"
                              min={0}
                              step="0.01"
                              value={logisticsSettingsForm.replacementValues[item.key] ?? ""}
                              onChange={(e) =>
                                setLogisticsSettingsForm((prev) => ({
                                  ...prev,
                                  replacementValues: {
                                    ...prev.replacementValues,
                                    [item.key]: e.target.value,
                                  },
                                }))
                              }
                              placeholder="Ex. 450,00"
                              disabled={user?.role !== "super_admin"}
                            />
                            <p className="text-xs text-muted-foreground">
                              Utilisee pour pre-chiffrer les litiges et arbitrages materiels.
                            </p>
                          </div>
                        </div>
                      ))}
                    </div>
                  </section>
                </div>

                <AdminActionButtonGroup>
                  <Button
                    onClick={handleLogisticsSettingsSave}
                    disabled={user?.role !== "super_admin" || setLogisticsSettingsMutation.isPending}
                  >
                    {setLogisticsSettingsMutation.isPending ? (
                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                    ) : (
                      <Database className="mr-2 h-4 w-4" />
                    )}
                    Enregistrer les réglages logistiques
                  </Button>
                </AdminActionButtonGroup>
              </div>
            </AdminSectionCollapsible>

            <AdminSectionCollapsible
              title="Assignation & suivi logistique"
              description="Le tableau de bord des restitutions matériel pour piloter l’attribution, les relances et la clôture terrain."
              open={!adminCollapsedSections.logisticsBoard}
              onOpenChange={(open) => setAdminSectionExpanded("logisticsBoard", open)}
              className="bg-white"
              contentClassName="space-y-4"
              headerBadge={
                <Badge variant="outline" className="border-orange-200 bg-orange-50 text-orange-700">
                  {logisticsOverview.latestRestitutionEntry
                    ? `Dernier retour ${formatDate(logisticsOverview.latestRestitutionEntry.restitutionDate)}`
                    : "Aucun retour récent"}
                </Badge>
              }
            >
              <div className="grid gap-2 sm:grid-cols-2 xl:grid-cols-3">
                {Object.entries(materialReturnBoardConfig).map(([key, config]) => (
                  <button
                    type="button"
                    key={key}
                    onClick={() => openLogisticsBoardState(key as "a_attribuer" | "planifie" | "terrain" | "retard" | "conforme" | "litige")}
                    className={cn(
                      "rounded-lg border px-3 py-2 text-left transition-colors hover:bg-muted/40",
                      selectedLogisticsBoardState === key ? "border-primary bg-primary/5 ring-1 ring-primary/20" : "bg-muted/20"
                    )}
                  >
                    <div className="flex items-center gap-2">
                      <Badge className={config.className}>{config.label}</Badge>
                      <span className="text-sm font-semibold">
                        {logisticsBoardColumns[key as keyof typeof logisticsBoardColumns]?.length || 0}
                      </span>
                    </div>
                    <p className="mt-2 text-xs text-muted-foreground">{config.description}</p>
                  </button>
                ))}
              </div>
              <div className="grid gap-4 xl:grid-cols-[minmax(0,1.35fr)_320px]">
                <div className="overflow-hidden rounded-2xl border border-slate-200 bg-white/95 shadow-sm">
                  <div className="flex flex-col gap-3 border-b border-slate-200 px-4 py-4 sm:flex-row sm:items-start sm:justify-between">
                    <div className="space-y-1">
                      <div className="flex flex-wrap items-center gap-2">
                        <Badge variant="outline" className="border-sky-200 bg-sky-50 text-sky-700">
                          Carte interne
                        </Badge>
                        <Badge variant="outline" className="border-slate-200 bg-slate-50 text-slate-700">
                          Style admin dense
                        </Badge>
                      </div>
                      <h3 className="text-base font-semibold text-slate-950">Lecture cartographique logistique</h3>
                      <p className="text-sm leading-6 text-slate-600">
                        La carte interne reprend le filtre métier actif pour visualiser rapidement les dossiers terrain, les retours attendus et les points de vigilance.
                      </p>
                    </div>
                    <Badge className="border-orange-200 bg-orange-50 text-orange-700">
                      {adminLogisticsMapEvents.length} mission(s)
                    </Badge>
                  </div>
                  <Suspense fallback={<AdminPanelLoader />}>
                    <AssociationPublicMap
                      associations={logisticsMapAssociations?.data || []}
                      mode="logistique"
                      logisticsEvents={adminLogisticsMapEvents}
                      selectedLogisticsRequestId={selectedLogisticsMapRequestId}
                      onSelectLogistics={setSelectedLogisticsMapRequestId}
                      onOpenLogisticsRequest={(requestId) => {
                        setLocation(
                          `/dashboard/requests/${requestId}?returnTo=${encodeURIComponent(
                            buildAdminTabHref("logistique", { logisticsState: selectedLogisticsBoardState })
                          )}`
                        );
                      }}
                      logisticsMarkerClickMode="open-direct"
                      styleUrl={associationMapSettings?.adminStyleUrl || associationMapSettings?.publicStyleUrl || associationMapSettings?.styleUrl}
                      heightClassName="h-[360px] md:h-[430px] xl:h-[500px]"
                    />
                  </Suspense>
                </div>
                <div className="rounded-2xl border border-slate-200 bg-white/95 p-4 shadow-sm">
                  <div className="flex items-start justify-between gap-3">
                    <div className="space-y-1">
                      <p className="text-sm font-semibold text-slate-950">Focus terrain</p>
                      <p className="text-sm text-slate-600">
                        La sélection suit la carte et les dossiers ci-dessous pour garder un repère opérationnel unique.
                      </p>
                    </div>
                    <MapPinned className="h-5 w-5 text-slate-400" />
                  </div>

                  {selectedAdminLogisticsMapEvent ? (
                    <div className="mt-4 space-y-4">
                      <div className="rounded-2xl border border-slate-200 bg-slate-50/80 p-4">
                        <div className="flex items-start justify-between gap-3">
                          <div>
                            <p className="font-semibold text-slate-950">{selectedAdminLogisticsMapEvent.associationName}</p>
                            <p className="text-sm text-slate-600">
                              {selectedAdminLogisticsMapEvent.commune || "Commune non renseignée"}
                            </p>
                          </div>
                          <Badge className={materialReturnBoardConfig[selectedAdminLogisticsMapEvent.boardState as keyof typeof materialReturnBoardConfig]?.className || "border-slate-200 bg-slate-50 text-slate-700"}>
                            {selectedAdminLogisticsMapEvent.boardStateLabel}
                          </Badge>
                        </div>
                        <div className="mt-4 space-y-2 text-sm">
                          <div className="flex items-center justify-between gap-4">
                            <span className="text-slate-500">Usage</span>
                            <span className="text-right font-medium text-slate-900">
                              {formatDate(selectedAdminLogisticsMapEvent.useStartDate)}
                              {selectedAdminLogisticsMapEvent.useEndDate && selectedAdminLogisticsMapEvent.useEndDate !== selectedAdminLogisticsMapEvent.useStartDate
                                ? ` au ${formatDate(selectedAdminLogisticsMapEvent.useEndDate)}`
                                : ""}
                            </span>
                          </div>
                          <div className="flex items-center justify-between gap-4">
                            <span className="text-slate-500">Retrait</span>
                            <span className="text-right font-medium text-slate-900">{formatDate(selectedAdminLogisticsMapEvent.pickupDate)}</span>
                          </div>
                          <div className="flex items-center justify-between gap-4">
                            <span className="text-slate-500">Restitution</span>
                            <span className="text-right font-medium text-slate-900">{formatDate(selectedAdminLogisticsMapEvent.restitutionDate)}</span>
                          </div>
                          <div className="flex items-center justify-between gap-4">
                            <span className="text-slate-500">Service</span>
                            <span className="text-right font-medium text-slate-900">
                              {selectedAdminLogisticsMapEvent.logisticServiceLabel || "Non attribué"}
                            </span>
                          </div>
                        </div>
                      </div>
                      <div className="rounded-2xl border border-dashed border-slate-200 bg-white px-4 py-3">
                        <p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Matériel</p>
                        <p className="mt-2 text-sm leading-6 text-slate-700">
                          {selectedAdminLogisticsMapEvent.requestedItems.map((item) => `${item.label} x${item.quantity}`).join(" • ") || "Matériel non renseigné"}
                        </p>
                      </div>
                      <div className="flex flex-col gap-2">
                        {selectedAdminLogisticsDirections ? (
                          <DirectionsMenu
                            latitude={selectedAdminLogisticsDirections.latitude}
                            longitude={selectedAdminLogisticsDirections.longitude}
                            destinationLabel={selectedAdminLogisticsDirections.destinationLabel}
                            label="Ouvrir avec"
                            usageContext="admin-logistics"
                            align="end"
                            className="w-full justify-between"
                          />
                        ) : null}
                        <Link
                          href={`/dashboard/requests/${selectedAdminLogisticsMapEvent.requestId}?returnTo=${encodeURIComponent(
                            buildAdminTabHref("logistique", { logisticsState: selectedLogisticsBoardState })
                          )}`}
                          {...buildRoutePrefetchHandlers(`/dashboard/requests/${selectedAdminLogisticsMapEvent.requestId}`)}
                        >
                          <Button className="w-full justify-between">
                            Ouvrir le dossier
                            <ArrowUpRight className="h-4 w-4" />
                          </Button>
                        </Link>
                        {selectedAdminLogisticsMapEvent.finalPdfUrl ? (
                          <a
                            href={buildMaterialReturnFinalPdfViewerHref({
                              url: selectedAdminLogisticsMapEvent.finalPdfUrl,
                              returnTo: buildAdminTabHref("logistique", { logisticsState: selectedLogisticsBoardState }),
                              title: "Consulter le PDF final de restitution",
                              downloadName: `fiche-finale-restitution-${selectedAdminLogisticsMapEvent.requestId}.pdf`,
                            })}
                          >
                            <Button variant="outline" className="w-full justify-between">
                              Voir le PDF final
                              <Download className="h-4 w-4" />
                            </Button>
                          </a>
                        ) : null}
                      </div>
                    </div>
                  ) : (
                    <div className="mt-4 rounded-2xl border border-dashed border-slate-200 bg-slate-50/70 px-4 py-10 text-center text-sm text-slate-500">
                      Aucune mission visible pour le filtre logistique actif.
                    </div>
                  )}
                </div>
              </div>
              {materialReturnBoardLoading ? (
                <div className="flex items-center justify-center py-12">
                  <Loader2 className="h-6 w-6 animate-spin" />
                </div>
              ) : (
                <div className="space-y-4">
                  <div className="flex flex-wrap items-center gap-2">
                    <Button
                      type="button"
                      variant={selectedLogisticsBoardState === "all" ? "default" : "outline"}
                      size="sm"
                      onClick={() => openLogisticsBoardState("all")}
                    >
                      Voir tout
                    </Button>
                    {selectedLogisticsBoardState !== "all" ? (
                      <p className="text-sm text-muted-foreground">
                        Filtre actif : {materialReturnBoardConfig[selectedLogisticsBoardState].label}
                      </p>
                    ) : null}
                  </div>
                  <div className={cn("grid gap-4", selectedLogisticsBoardState === "all" ? "xl:grid-cols-3 2xl:grid-cols-6" : "grid-cols-1")}>
                  {visibleLogisticsBoardStates.map((columnKey) => {
                    const config = materialReturnBoardConfig[columnKey];
                    const entries = logisticsBoardColumns[columnKey];
                    const logisticsReturnTo = buildAdminTabHref("logistique", { logisticsState: columnKey });
                    const primaryEntries = columnKey === "conforme" || columnKey === "a_attribuer" ? entries.slice(0, 1) : entries;
                    const secondaryEntries = columnKey === "conforme" || columnKey === "a_attribuer" ? entries.slice(1) : [];
                    const isOverflowColumn = columnKey === "conforme" || columnKey === "a_attribuer";
                    const overflowCollapseKey = columnKey === "a_attribuer" ? "logisticsAssignOverflow" : "logisticsConformeOverflow";
                    const overflowTitle = columnKey === "a_attribuer" ? "Autres dossiers à attribuer" : "Autres retours conformes";
                    const overflowActionLabel = columnKey === "a_attribuer" ? "Voir les autres dossiers" : "Voir les autres";
                    const overflowDescription =
                      columnKey === "a_attribuer"
                        ? `${secondaryEntries.length} dossier(s) supplémentaire(s) replié(s) pour laisser la priorité au premier dossier à traiter.`
                        : `${secondaryEntries.length} retour(s) supplémentaire(s) replié(s) pour garder la colonne lisible.`;

                    const renderEntryCard = (entry: typeof entries[number]) => (
                      <div
                        key={entry.requestId}
                        onMouseEnter={() => setSelectedLogisticsMapRequestId(entry.requestId)}
                        onFocusCapture={() => setSelectedLogisticsMapRequestId(entry.requestId)}
                        className={cn(
                          "space-y-4 rounded-2xl border border-border/70 bg-white/95 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:bg-white hover:shadow-sm",
                          selectedLogisticsMapRequestId === entry.requestId && "border-primary/50 ring-1 ring-primary/20"
                        )}
                      >
                        <div className="space-y-1">
                          <div className="flex items-start justify-between gap-3">
                            <p className="font-medium leading-tight">{entry.title}</p>
                            <Badge className={config.className}>{entry.boardStateLabel}</Badge>
                          </div>
                          <p className="text-sm text-muted-foreground">{entry.associationName}</p>
                          <p className="text-xs text-muted-foreground">
                            {entry.commune || "Commune non renseignée"} • restitution le {formatDate(entry.restitutionDate)}
                          </p>
                        </div>

                        <div className="grid gap-2 sm:grid-cols-2 xl:grid-cols-1 2xl:grid-cols-2">
                          <div className="rounded-xl border bg-slate-50/70 px-3 py-2">
                            <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Service terrain</p>
                            <p className="mt-1 text-sm font-medium text-slate-900">{entry.serviceLabel || "Non attribué"}</p>
                          </div>
                          <div className="rounded-xl border bg-slate-50/70 px-3 py-2">
                            <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">État du lien</p>
                            <div className="mt-1">
                              <Badge
                                variant="outline"
                                className={
                                  materialReturnUploadLinkStateClasses[entry.uploadLinkState || "expired"]
                                  || materialReturnUploadLinkStateClasses.expired
                                }
                              >
                                {materialReturnUploadLinkStateLabels[entry.uploadLinkState || "expired"] || "Expiré"}
                              </Badge>
                            </div>
                          </div>
                          <div className="rounded-xl border bg-slate-50/70 px-3 py-2">
                            <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Dernier envoi</p>
                            <p className="mt-1 text-sm font-medium text-slate-900">{formatDateTime(entry.sentAt)}</p>
                          </div>
                          <div className="rounded-xl border bg-slate-50/70 px-3 py-2">
                            <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Échéance</p>
                            <p className="mt-1 text-sm font-medium text-slate-900">{formatDateTime(entry.uploadTokenExpiresAt)}</p>
                          </div>
                        </div>

                        <div className="space-y-2 text-sm">
                          <div className="flex justify-between gap-4">
                            <span className="text-muted-foreground">Service terrain / récupération</span>
                            <span className="text-right font-medium">{entry.serviceLabel || "Non attribué"}</span>
                          </div>
                          <div className="flex justify-between gap-4">
                            <span className="text-muted-foreground">Destinataires terrain</span>
                            <span className="text-right font-medium">
                              {Array.isArray(entry.recipientEmails) && entry.recipientEmails.length > 0
                                ? entry.recipientEmails.join(", ")
                                : "Aucun"}
                            </span>
                          </div>
                          <div className="flex justify-between gap-4">
                            <span className="text-muted-foreground">Service interne de contrôle</span>
                            <span className="text-right font-medium">{entry.supervisionServiceLabel || "Aucun"}</span>
                          </div>
                          <div className="flex justify-between gap-4">
                            <span className="text-muted-foreground">Destinataires suivi</span>
                            <span className="text-right font-medium">
                              {Array.isArray(entry.supervisionRecipientEmails) && entry.supervisionRecipientEmails.length > 0
                                ? entry.supervisionRecipientEmails.join(", ")
                                : "Aucun"}
                            </span>
                          </div>
                          <div className="flex justify-between gap-4">
                            <span className="text-muted-foreground">Lien terrain</span>
                            <span className="text-right font-medium">
                              {entry.uploadLink ? "Généré" : "À générer"}
                            </span>
                          </div>
                          <div className="flex justify-between gap-4">
                            <span className="text-muted-foreground">Échéance de dépôt</span>
                            <span className="text-right font-medium">{formatDateTime(entry.uploadTokenExpiresAt)}</span>
                          </div>
                          <div className="flex justify-between gap-4">
                            <span className="text-muted-foreground">Dernier envoi</span>
                            <span className="text-right font-medium">{formatDateTime(entry.sentAt)}</span>
                          </div>
                          <div className="flex justify-between gap-4">
                            <span className="text-muted-foreground">Dernière relance</span>
                            <span className="text-right font-medium">{formatDateTime(entry.lastReminderSentAt)}</span>
                          </div>
                          <div className="flex justify-between gap-4">
                            <span className="text-muted-foreground">Suivi rouvert le</span>
                            <span className="text-right font-medium">{formatDateTime(entry.reactivatedAt)}</span>
                          </div>
                        </div>

                        <div className="flex flex-wrap gap-2">
                          {entry.boardState !== "conforme" ? (
                            <Button
                              size="sm"
                              variant="default"
                              onClick={() => openLogisticsAssignmentDialog(entry)}
                            >
                            {entry.boardState === "a_attribuer" ? "Assigner" : "Réassigner"}
                            </Button>
                          ) : (
                            <Link href={`/dashboard/requests/${entry.requestId}?returnTo=${encodeURIComponent(logisticsReturnTo)}`} {...buildRoutePrefetchHandlers(`/dashboard/requests/${entry.requestId}`)}>
                              <Button size="sm" variant="outline">
                                <Eye className="mr-1.5 h-3.5 w-3.5" />
                                Dossier
                              </Button>
                            </Link>
                          )}
                          <AdminOverflowMenu label={`Autres actions pour le dossier ${entry.requestId}`}>
                            {entry.boardState !== "conforme" ? (
                              <DropdownMenuItem
                                onClick={() =>
                                  handleReactivateMaterialReturn(
                                    entry,
                                    entry.uploadLinkState === "expired"
                                      ? "Suivi rouvert et lien de restitution renvoyé"
                                      : "Lien de restitution renvoyé"
                                  )
                                }
                                disabled={reactivateMaterialReturnMutation.isPending || entry.boardState === "litige"}
                              >
                                {entry.uploadLinkState === "expired" ? (
                                  <RotateCcw className="h-4 w-4" />
                                ) : (
                                  <Send className="h-4 w-4" />
                                )}
                                {entry.uploadLinkState === "expired" ? "Rouvrir et renvoyer le lien" : "Renvoyer le lien actif"}
                              </DropdownMenuItem>
                            ) : null}
                            <DropdownMenuItem onClick={() => setLocation(`/dashboard/requests/${entry.requestId}?returnTo=${encodeURIComponent(logisticsReturnTo)}`)}>
                              <Eye className="h-4 w-4" />
                              Ouvrir le dossier
                            </DropdownMenuItem>
                            {entry.uploadLink ? (
                              <DropdownMenuItem asChild>
                                <a href={appendReturnToParam(entry.uploadLink, logisticsReturnTo)} target="_blank" rel="noopener noreferrer">
                                  <ArrowUpRight className="h-4 w-4" />
                                  Ouvrir le terrain
                                </a>
                              </DropdownMenuItem>
                            ) : null}
                            {entry.finalPdfUrl ? (
                              <DropdownMenuItem asChild>
                                <a
                                  href={buildMaterialReturnFinalPdfViewerHref({
                                    url: entry.finalPdfUrl,
                                    returnTo: logisticsReturnTo,
                                    title: "Consulter le PDF final de restitution",
                                    downloadName: `fiche-finale-restitution-${entry.requestId}.pdf`,
                                  })}
                                >
                                  <Download className="h-4 w-4" />
                                  Voir le PDF final
                                </a>
                              </DropdownMenuItem>
                            ) : null}
                          </AdminOverflowMenu>
                        </div>
                      </div>
                    );

                    return (
                      <div key={columnKey} className="space-y-3 rounded-2xl border border-slate-200 bg-slate-50/70 p-4 shadow-sm">
                        <div className="flex items-center justify-between gap-3">
                          <div>
                            <h3 className="font-semibold">{config.label}</h3>
                            <p className="text-xs text-muted-foreground">{config.description}</p>
                          </div>
                          <Badge className={config.className}>{entries.length}</Badge>
                        </div>

                        <div className="space-y-3">
                          {entries.length === 0 ? (
                            <div className="rounded-lg border border-dashed bg-background px-3 py-6 text-center text-sm text-muted-foreground">
                              Aucun dossier
                            </div>
                          ) : (
                            <>
                              {primaryEntries.map(renderEntryCard)}
                              {isOverflowColumn && secondaryEntries.length > 0 ? (
                                <Collapsible
                                  open={!adminCollapsedSections[overflowCollapseKey]}
                                  onOpenChange={(open) => setAdminSectionExpanded(overflowCollapseKey, open)}
                                  className="rounded-2xl border border-slate-200 bg-white/80"
                                >
                                  <div className="flex items-center justify-between gap-3 px-4 py-3">
                                    <div>
                                      <p className="text-sm font-semibold text-slate-900">{overflowTitle}</p>
                                      <p className="text-xs text-muted-foreground">
                                        {overflowDescription}
                                      </p>
                                    </div>
                                    <CollapsibleTrigger asChild>
                                      <Button type="button" variant="outline" size="sm" className="gap-2">
                                        {adminCollapsedSections[overflowCollapseKey] ? overflowActionLabel : "Réduire"}
                                        <ChevronDown className={cn("h-4 w-4 transition-transform duration-200", adminCollapsedSections[overflowCollapseKey] ? "-rotate-90" : "rotate-0")} />
                                      </Button>
                                    </CollapsibleTrigger>
                                  </div>
                                  <CollapsibleContent className="border-t border-slate-200 px-4 pb-4 pt-4">
                                    <div className="space-y-3">
                                      {secondaryEntries.map(renderEntryCard)}
                                    </div>
                                  </CollapsibleContent>
                                </Collapsible>
                              ) : null}
                            </>
                          )}
                        </div>
                      </div>
                    );
                  })}
                </div>
                </div>
              )}
            </AdminSectionCollapsible>
          </TabsContent>

          <TabsContent value="ccds-directory" className="space-y-6">
            <AdminSurface
              title="Annuaire interne CCDS"
              description="Référentiel interne des contacts et structures CCDS avec la même base visuelle que le reste de l’administration."
            >
              <Suspense fallback={<AdminPanelLoader />}>
                <CCDSInternalDirectory
                  backHref="/admin?tab=dashboard"
                  backLabel="Retour au tableau de bord"
                  canEdit={user?.role === "super_admin"}
                />
              </Suspense>
            </AdminSurface>
          </TabsContent>

        </Tabs>
      </main>

      <Dialog
        open={Boolean(logisticsAssignment)}
        onOpenChange={(open) => {
          if (!open) {
            setLogisticsAssignment(null);
            setLogisticsDetailVisible(false);
          }
        }}
      >
        <DialogContent className="max-w-3xl max-h-[90vh]">
          <DialogHeader>
            <DialogTitle>{logisticsAssignment?.boardState === "a_attribuer" ? "Assigner le service terrain" : "Réassigner et relancer"}</DialogTitle>
            <DialogDescription>
              Désigne le service ou l’agent qui prendra en charge la restitution terrain, puis envoie immédiatement un lien sécurisé actif.
            </DialogDescription>
          </DialogHeader>

          {logisticsAssignment ? (
            <ScrollArea className="max-h-[62vh] pr-4">
              <div className="space-y-4">
                <div className="rounded-lg border bg-muted/20 p-4">
                  <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
                    <div>
                      <p className="font-medium">{logisticsAssignment.title}</p>
                      <p className="text-sm text-muted-foreground">{logisticsAssignment.associationName}</p>
                    </div>
                    <div className="flex flex-wrap gap-2">
                      <Button
                        type="button"
                        variant="outline"
                        size="sm"
                        onClick={() => setLogisticsDetailVisible((prev) => !prev)}
                      >
                        <Eye className="mr-2 h-4 w-4" />
                        {logisticsDetailVisible ? "Masquer le détail" : "Voir le détail de la demande"}
                      </Button>
                      <Link
                        href={`/dashboard/requests/${logisticsAssignment.requestId}?returnTo=${encodeURIComponent(
                          buildAdminTabHref("logistique", { logisticsState: (logisticsAssignment.boardState as string) || "all" })
                        )}`}
                      >
                        <Button type="button" variant="ghost" size="sm">
                          <ArrowUpRight className="mr-2 h-4 w-4" />
                          Ouvrir le dossier
                        </Button>
                      </Link>
                    </div>
                  </div>
                </div>

                {logisticsDetailVisible ? (
                  <div className="rounded-lg border bg-background p-4 space-y-4">
                    {logisticsAssignmentRequestLoading ? (
                      <div className="flex items-center justify-center py-8">
                        <Loader2 className="h-5 w-5 animate-spin" />
                      </div>
                    ) : logisticsAssignmentRequest ? (
                      <>
                        <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
                          <div className="rounded-lg border bg-muted/20 p-3">
                            <p className="text-xs text-muted-foreground">Type</p>
                            <p className="mt-1 font-semibold">{requestTypeLabels[logisticsAssignmentRequest.type] || logisticsAssignmentRequest.type}</p>
                          </div>
                          <div className="rounded-lg border bg-muted/20 p-3">
                            <p className="text-xs text-muted-foreground">Statut</p>
                            <p className="mt-1 font-semibold">{statusConfig[logisticsAssignmentRequest.status]?.label || logisticsAssignmentRequest.status}</p>
                          </div>
                          <div className="rounded-lg border bg-muted/20 p-3">
                            <p className="text-xs text-muted-foreground">Association</p>
                            <p className="mt-1 font-semibold">{logisticsAssignmentRequest.associationInfo?.nomAssociation || logisticsAssignment.associationName}</p>
                          </div>
                          <div className="rounded-lg border bg-muted/20 p-3">
                            <p className="text-xs text-muted-foreground">Soumise le</p>
                            <p className="mt-1 font-semibold">{formatDateTime(logisticsAssignmentRequest.dateSubmission || logisticsAssignmentRequest.createdAt)}</p>
                          </div>
                        </div>

                        {logisticsAssignmentIsMateriel ? (
                          <div className="space-y-4">
                            <div className="grid gap-4 md:grid-cols-2">
                              <div className="rounded-lg border p-4 space-y-2">
                                <p className="text-sm font-semibold">Contexte de l’événement</p>
                                <p className="text-sm text-muted-foreground">
                                  {[
                                    logisticsAssignmentFormData?.commune,
                                    logisticsAssignmentFormData?.direction,
                                    logisticsAssignmentFormData?.service,
                                  ].filter(Boolean).join(" • ") || "Contexte non renseigné"}
                                </p>
                                <p className="text-sm">
                                  <span className="text-muted-foreground">Demandeur :</span>{" "}
                                  <span className="font-medium">{logisticsAssignmentFormData?.demandeurNomPrenom || "-"}</span>
                                </p>
                                <p className="text-sm">
                                  <span className="text-muted-foreground">Manifestation :</span>{" "}
                                  <span className="font-medium">
                                    {formatDate(logisticsAssignmentFormData?.dateDebutManifestation || logisticsAssignmentFormData?.dateManifestation)}
                                    {logisticsAssignmentFormData?.dateFinManifestation &&
                                    logisticsAssignmentFormData?.dateFinManifestation !== logisticsAssignmentFormData?.dateDebutManifestation
                                      ? ` au ${formatDate(logisticsAssignmentFormData?.dateFinManifestation)}`
                                      : ""}
                                  </span>
                                </p>
                                <p className="text-sm">
                                  <span className="text-muted-foreground">Retrait / restitution :</span>{" "}
                                  <span className="font-medium">
                                    {formatDate(logisticsAssignmentFormData?.datePriseEnCharge)} {"->"} {formatDate(logisticsAssignmentFormData?.dateRestitution)}
                                  </span>
                                </p>
                              </div>
                              <div className="rounded-lg border p-4 space-y-2">
                                <p className="text-sm font-semibold">Matériel demandé</p>
                                {logisticsAssignmentRequestedMaterialItems.length > 0 ? (
                                  <div className="space-y-2">
                                    {logisticsAssignmentRequestedMaterialItems.map((item) => (
                                      <div key={item.key} className="flex items-center justify-between rounded-md bg-muted/20 px-3 py-2 text-sm">
                                        <span>{item.label}</span>
                                        <span className="font-semibold">
                                          {logisticsAssignmentFormData?.quantitesDemandees?.[item.key] || "-"}
                                        </span>
                                      </div>
                                    ))}
                                  </div>
                                ) : (
                                  <p className="text-sm text-muted-foreground">Aucun matériel renseigné.</p>
                                )}
                              </div>
                            </div>
                            {logisticsAssignmentFormData?.motifDemande ? (
                              <div className="rounded-lg border p-4">
                                <p className="text-sm font-semibold mb-2">Motif de la demande</p>
                                <p className="text-sm text-muted-foreground whitespace-pre-wrap">
                                  {logisticsAssignmentFormData.motifDemande}
                                </p>
                              </div>
                            ) : null}
                          </div>
                        ) : logisticsAssignmentIsSalle ? (
                          <div className="space-y-4">
                            <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                              <div className="rounded-lg border p-4">
                                <p className="text-xs text-muted-foreground">Salle(s)</p>
                                <p className="mt-1 font-semibold">
                                  {(logisticsAssignmentFormData?.sallesSelectionnees as string[])?.join(", ") || "-"}
                                </p>
                              </div>
                              <div className="rounded-lg border p-4">
                                <p className="text-xs text-muted-foreground">Période</p>
                                <p className="mt-1 font-semibold">{logisticsAssignmentReservationPeriodSummary}</p>
                              </div>
                              <div className="rounded-lg border p-4">
                                <p className="text-xs text-muted-foreground">Horaires</p>
                                <p className="mt-1 font-semibold">{logisticsAssignmentReservationScheduleSummary}</p>
                              </div>
                              <div className="rounded-lg border p-4">
                                <p className="text-xs text-muted-foreground">Participants</p>
                                <p className="mt-1 font-semibold">
                                  {logisticsAssignmentFormData?.nombreParticipants
                                    ? `${logisticsAssignmentFormData.nombreParticipants} personnes`
                                    : "-"}
                                </p>
                              </div>
                            </div>
                            <div className="rounded-lg border p-4">
                              <p className="text-sm font-semibold mb-2">Motif</p>
                              <p className="text-sm text-muted-foreground whitespace-pre-wrap">
                                {logisticsAssignmentFormData?.motifReservation || logisticsAssignmentRequest.description || "-"}
                              </p>
                            </div>
                          </div>
                        ) : (
                          <div className="rounded-lg border p-4">
                            <p className="text-sm font-semibold mb-2">Description du dossier</p>
                            <p className="text-sm text-muted-foreground whitespace-pre-wrap">
                              {logisticsAssignmentRequest.description || "Aucune description renseignée."}
                            </p>
                          </div>
                        )}
                      </>
                    ) : (
                      <p className="text-sm text-muted-foreground">Impossible de charger le détail du dossier.</p>
                    )}
                  </div>
                ) : null}

                <Separator />

                <div className="space-y-2">
                  <Label htmlFor="logisticsServiceDirectory">Répertoire du service terrain</Label>
                  <select
                    id="logisticsServiceDirectory"
                    value={logisticsAssignment.recapServiceId}
                    onChange={(event) => applyOperationalRecapServiceToLogisticsAssignment(event.target.value)}
                    className="border-input focus-visible:border-ring focus-visible:ring-ring/50 dark:bg-input/30 flex h-9 w-full rounded-md border bg-transparent px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px]"
                  >
                    <option value="">Sélectionner un service enregistré</option>
                    {terrainOperationalRecapServices
                      .filter((service) => service.actif)
                      .map((service) => (
                        <option key={service.id} value={String(service.id)}>
                          {service.label}
                        </option>
                      ))}
                  </select>
                </div>

                <div className="space-y-2">
                  <Label htmlFor="logisticsServiceLabel">Service terrain / récupération</Label>
                  <Input
                    id="logisticsServiceLabel"
                    value={logisticsAssignment.recapServiceLabel}
                    onChange={(event) =>
                      setLogisticsAssignment((prev) =>
                        prev ? { ...prev, recapServiceLabel: event.target.value } : prev
                      )
                    }
                    placeholder="Ex. Service technique Sinnamary"
                  />
                </div>

                <div className="space-y-2">
                  <Label htmlFor="logisticsRecipients">Destinataires terrain</Label>
                  <Textarea
                    id="logisticsRecipients"
                    value={logisticsAssignment.recapRecipients}
                    onChange={(event) =>
                      setLogisticsAssignment((prev) =>
                        prev ? { ...prev, recapRecipients: event.target.value } : prev
                      )
                    }
                    rows={4}
                    placeholder="agent@collectivite.fr, service@collectivite.fr"
                  />
                  <p className="text-sm text-muted-foreground">
                    Le portail enverra le lien terrain, la fiche préremplie et suivra ensuite le dossier en temps réel.
                  </p>
                </div>

                <Separator />

                <div className="space-y-2">
                  <Label htmlFor="logisticsSupervisionServiceDirectory">Service interne de contrôle</Label>
                  <select
                    id="logisticsSupervisionServiceDirectory"
                    value={logisticsAssignment.supervisionServiceId}
                    onChange={(event) => applyOperationalSupervisionServiceToLogisticsAssignment(event.target.value)}
                    className="border-input focus-visible:border-ring focus-visible:ring-ring/50 dark:bg-input/30 flex h-9 w-full rounded-md border bg-transparent px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px]"
                  >
                    <option value="">Sélectionner un service interne</option>
                    {controlOperationalRecapServices
                      .filter((service) => service.actif)
                      .map((service) => (
                        <option key={service.id} value={String(service.id)}>
                          {service.label}
                        </option>
                      ))}
                  </select>
                </div>

                <div className="space-y-2">
                  <Label htmlFor="logisticsSupervisionServiceLabel">Libellé du service interne de contrôle</Label>
                  <Input
                    id="logisticsSupervisionServiceLabel"
                    value={logisticsAssignment.supervisionServiceLabel}
                    onChange={(event) =>
                      setLogisticsAssignment((prev) =>
                        prev ? { ...prev, supervisionServiceLabel: event.target.value } : prev
                      )
                    }
                    placeholder="Ex. DSU / cellule de contrôle"
                  />
                </div>

                <div className="space-y-2">
                  <Label htmlFor="logisticsSupervisionRecipients">Destinataires du contrôle interne</Label>
                  <Textarea
                    id="logisticsSupervisionRecipients"
                    value={logisticsAssignment.supervisionRecipients}
                    onChange={(event) =>
                      setLogisticsAssignment((prev) =>
                        prev ? { ...prev, supervisionRecipients: event.target.value } : prev
                      )
                    }
                    rows={3}
                    placeholder="dsu@collectivite.fr, controle@collectivite.fr"
                  />
                  <p className="text-sm text-muted-foreground">
                    Ce service recevra les confirmations de restitution, les retards et les alertes de suivi.
                  </p>
                </div>
              </div>
            </ScrollArea>
          ) : null}

          <DialogFooter>
            <Button variant="outline" onClick={() => setLogisticsAssignment(null)}>
              Annuler
            </Button>
            <Button onClick={handleAssignMaterialReturn} disabled={assignMaterialReturnMutation.isPending}>
              {assignMaterialReturnMutation.isPending ? (
                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
              ) : (
                <Send className="mr-2 h-4 w-4" />
              )}
              {logisticsAssignment?.boardState === "a_attribuer" ? "Valider l'assignation" : "Réassigner et relancer"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog open={selectedDirectoryEntryId !== null} onOpenChange={(open) => !open && setSelectedDirectoryEntryId(null)}>
        <DialogContent className="max-w-4xl max-h-[90vh]">
          <DialogHeader>
            <DialogTitle>Fiche annuaire de l'association</DialogTitle>
            <DialogDescription>
              Vue consolidée du bordereau, du profil portail et des pièces déposées.
            </DialogDescription>
          </DialogHeader>
          <ScrollArea className="h-[70vh] pr-4">
            {selectedDirectoryEntryLoading ? (
              <div className="flex justify-center py-12">
                <Loader2 className="h-6 w-6 animate-spin" />
              </div>
            ) : selectedDirectoryEntry ? (
              <div className="space-y-6 py-1">
                {(() => {
                  const identifierMeta = getDirectoryIdentifierMeta(selectedDirectoryEntry);

                  return (
                    <Card>
                      <CardHeader>
                        <CardTitle>Identifiants reconnus</CardTitle>
                        <CardDescription>
                          Numéros retenus pour identifier l'association à partir du bordereau et, si disponible, de son espace portail.
                        </CardDescription>
                      </CardHeader>
                      <CardContent className="space-y-4">
                        <div className="flex flex-wrap gap-2">
                          <Button type="button" variant="outline" onClick={openDirectoryEntryEdition}>
                            <Pencil className="mr-2 h-4 w-4" />
                            Modifier la fiche
                          </Button>
                          <Button
                            type="button"
                            onClick={() => syncDirectoryReferenceMutation.mutate({ id: selectedDirectoryEntry.entry.id })}
                            disabled={syncDirectoryReferenceMutation.isPending}
                          >
                            {syncDirectoryReferenceMutation.isPending ? (
                              <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                            ) : (
                              <RefreshCw className="mr-2 h-4 w-4" />
                            )}
                            Rechercher des mises à jour
                          </Button>
                          <Button
                            type="button"
                            variant="outline"
                            onClick={() => syncDirectoryHelloAssoMutation.mutate({ id: selectedDirectoryEntry.entry.id })}
                            disabled={!isHelloAssoConfigured || syncDirectoryHelloAssoMutation.isPending}
                          >
                            {syncDirectoryHelloAssoMutation.isPending ? (
                              <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                            ) : (
                              <RefreshCw className="mr-2 h-4 w-4" />
                            )}
                            Synchroniser HelloAsso
                          </Button>
                        </div>

                        <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                          <div className="rounded-lg border bg-muted/20 p-4">
                            <p className="text-sm font-medium text-muted-foreground">Statut reconnu</p>
                            <p className="mt-2 text-lg font-semibold">
                              {getAssociationStatusLabel(selectedDirectoryEntry.entry.associationStatus)}
                            </p>
                          </div>
                          <div className="rounded-lg border bg-muted/20 p-4">
                            <p className="text-sm font-medium text-muted-foreground">Dernière mise à jour source</p>
                            <p className="mt-2 text-lg font-semibold">
                              {formatDateTime(selectedDirectoryEntry.entry.registryLastUpdatedAt)}
                            </p>
                          </div>
                          <div className="rounded-lg border bg-muted/20 p-4">
                            <p className="text-sm font-medium text-muted-foreground">Dernière vérification portail</p>
                            <p className="mt-2 text-lg font-semibold">
                              {formatDateTime(selectedDirectoryEntry.entry.referenceLastCheckedAt)}
                            </p>
                          </div>
                          <div className="rounded-lg border bg-muted/20 p-4">
                            <p className="text-sm font-medium text-muted-foreground">Source de référence</p>
                            <p className="mt-2 text-sm font-semibold">
                              {selectedDirectoryEntry.entry.referenceSourceLabel || "-"}
                            </p>
                          </div>
                        </div>

                        <div className="grid gap-4 md:grid-cols-2">
                        <div className="rounded-lg border bg-muted/20 p-4">
                          <div className="flex items-center justify-between gap-3">
                            <p className="text-sm font-medium text-muted-foreground">Numéro SIRET</p>
                            {identifierMeta.siret.source ? (
                              <Badge variant="outline">{identifierMeta.siret.source}</Badge>
                            ) : null}
                          </div>
                          <p className="mt-2 text-lg font-semibold">{identifierMeta.siret.value || "-"}</p>
                          {identifierMeta.siret.secondaryValue ? (
                            <p className="mt-1 text-xs text-muted-foreground">
                              Valeur bordereau également trouvée : {identifierMeta.siret.secondaryValue}
                            </p>
                          ) : (
                            <p className="mt-1 text-xs text-muted-foreground">
                              {identifierMeta.siret.value
                                ? "Identifiant reconnu dans la fiche association."
                                : "Aucun numéro SIRET reconnu pour cette association."}
                            </p>
                          )}
                        </div>

                        <div className="rounded-lg border bg-muted/20 p-4">
                          <div className="flex items-center justify-between gap-3">
                            <p className="text-sm font-medium text-muted-foreground">Numéro RNA</p>
                            {identifierMeta.rna.source ? (
                              <Badge variant="outline">{identifierMeta.rna.source}</Badge>
                            ) : null}
                          </div>
                          <p className="mt-2 text-lg font-semibold">{identifierMeta.rna.value || "-"}</p>
                          {identifierMeta.rna.secondaryValue ? (
                            <p className="mt-1 text-xs text-muted-foreground">
                              Valeur bordereau également trouvée : {identifierMeta.rna.secondaryValue}
                            </p>
                          ) : (
                            <p className="mt-1 text-xs text-muted-foreground">
                              {identifierMeta.rna.value
                                ? "Identifiant reconnu dans la fiche association."
                                : "Aucun numéro RNA reconnu pour cette association."}
                            </p>
                          )}
                        </div>
                        </div>
                      </CardContent>
                    </Card>
                  );
                })()}

                {pendingReferenceProposal ? (
                  <Card className="border-amber-200 bg-gradient-to-br from-amber-50 via-white to-amber-50/70">
                    <CardHeader>
                      <div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
                        <div className="space-y-1">
                          <CardTitle className="text-xl">Proposition d’enrichissement officiel</CardTitle>
                          <CardDescription>
                            Le bordereau reste la source de vérité. Les données officielles ne sont proposées ici qu’après comparaison.
                          </CardDescription>
                        </div>
                        <Badge variant="outline" className="w-fit border-amber-300 bg-white text-amber-800">
                          À valider
                        </Badge>
                      </div>
                    </CardHeader>
                    <CardContent className="space-y-4">
                      <div className="rounded-xl border border-amber-200 bg-white/90 p-4">
                        <p className="text-sm font-semibold text-slate-900">{pendingReferenceProposal.summary || "Des différences ont été détectées."}</p>
                        <p className="mt-2 text-sm text-slate-600">
                          Source : {pendingReferenceProposal.sourceLabel || "Sources officielles"} • détectée le {formatDateTime(pendingReferenceProposal.createdAt)}
                        </p>
                      </div>

                      <div className="grid gap-3 md:grid-cols-2">
                        {(pendingReferenceProposal.changes || []).map((change) => (
                          <div key={`${pendingReferenceProposal.id}-${change.field}`} className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
                            <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">{change.label}</p>
                            <div className="mt-3 space-y-3">
                              <div>
                                <p className="text-xs font-medium text-slate-500">Actuel</p>
                                <p className="mt-1 text-sm text-slate-900 break-words">{change.currentValue}</p>
                              </div>
                              <div>
                                <p className="text-xs font-medium text-emerald-700">Proposé</p>
                                <p className="mt-1 text-sm font-medium text-emerald-900 break-words">{change.proposedValue}</p>
                              </div>
                            </div>
                          </div>
                        ))}
                      </div>

                      <div className="flex flex-col gap-2 sm:flex-row sm:justify-end">
                        <Button
                          type="button"
                          variant="outline"
                          onClick={() => dismissDirectoryUpdateProposalMutation.mutate({ proposalId: pendingReferenceProposal.id })}
                          disabled={dismissDirectoryUpdateProposalMutation.isPending}
                        >
                          Ignorer
                        </Button>
                        <Button
                          type="button"
                          onClick={() => applyDirectoryUpdateProposalMutation.mutate({ proposalId: pendingReferenceProposal.id })}
                          disabled={applyDirectoryUpdateProposalMutation.isPending}
                        >
                          {applyDirectoryUpdateProposalMutation.isPending ? (
                            <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                          ) : (
                            <CheckCircle2 className="mr-2 h-4 w-4" />
                          )}
                          Valider et appliquer
                        </Button>
                      </div>
                    </CardContent>
                  </Card>
                ) : null}

                <div className="rounded-xl border bg-muted/30 p-4">
                  <div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
                    <div className="space-y-1">
                      <div className="flex flex-wrap items-center gap-2">
                        <h3 className="text-xl font-semibold">{selectedDirectoryEntry.entry.nomAssociation}</h3>
                        <Badge
                          variant="outline"
                          className={selectedDirectoryEntry.association ? "bg-green-50 text-green-700 border-green-200" : "bg-blue-50 text-blue-700 border-blue-200"}
                        >
                          {selectedDirectoryEntry.association ? "Inscrite sur le portail" : "Pas encore inscrite"}
                        </Badge>
                        <Badge variant="outline">#{selectedDirectoryEntry.entry.id}</Badge>
                      </div>
                      <p className="text-sm text-muted-foreground">
                        Bordereau importé le {formatDateTime(selectedDirectoryEntry.entry.importedAt)}
                        {selectedDirectoryEntry.entry.sourceFileName ? ` via ${selectedDirectoryEntry.entry.sourceFileName}` : ""}
                      </p>
                    </div>
                    {selectedDirectoryEntry.association ? (
                      <div className="rounded-lg border bg-background px-3 py-2 text-sm">
                        <p className="font-medium">Association portail #{selectedDirectoryEntry.association.id}</p>
                        <p className="text-muted-foreground">
                          Compte créé le {formatDate(selectedDirectoryEntry.association.createdAt)}
                        </p>
                      </div>
                    ) : null}
                  </div>
                </div>

                <div className="grid gap-6 lg:grid-cols-2">
                  <Card>
                    <CardHeader>
                      <CardTitle>Référentiel du bordereau</CardTitle>
                      <CardDescription>
                        Informations actuellement portées par la base associative.
                      </CardDescription>
                    </CardHeader>
                    <CardContent className="space-y-4 text-sm">
                      <div className="grid gap-3 sm:grid-cols-2">
                        <div>
                          <p className="text-muted-foreground">Email officiel</p>
                          <p className="font-medium">{selectedDirectoryEntry.entry.emailOfficiel || "-"}</p>
                        </div>
                        <div>
                          <p className="text-muted-foreground">Téléphone</p>
                          <p className="font-medium">{selectedDirectoryEntry.entry.telephone || "-"}</p>
                        </div>
                        <div className="sm:col-span-2">
                          <p className="text-muted-foreground">Adresse</p>
                          <p className="font-medium">
                            {selectedDirectoryEntry.entry.adresse || "-"}
                            {selectedDirectoryEntry.entry.codePostal || selectedDirectoryEntry.entry.ville
                              ? `, ${[selectedDirectoryEntry.entry.codePostal, selectedDirectoryEntry.entry.ville].filter(Boolean).join(" ")}`
                              : ""}
                          </p>
                        </div>
                        <div>
                          <p className="text-muted-foreground">SIRET</p>
                          <p className="font-medium">{selectedDirectoryEntry.entry.siret || "-"}</p>
                        </div>
                        <div>
                          <p className="text-muted-foreground">RNA</p>
                          <p className="font-medium">{selectedDirectoryEntry.entry.rna || "-"}</p>
                        </div>
                        <div>
                          <p className="text-muted-foreground">Date de création</p>
                          <p className="font-medium">{formatDate(selectedDirectoryEntry.entry.dateCreation)}</p>
                        </div>
                        <div>
                          <p className="text-muted-foreground">Site web</p>
                          <p className="font-medium">{selectedDirectoryEntry.entry.siteWeb || "-"}</p>
                        </div>
                        <div>
                          <p className="text-muted-foreground">Représentant</p>
                          <p className="font-medium">
                            {buildDisplayName(selectedAssociationGovernance?.representantLegal) || selectedDirectoryEntry.entry.nomRepresentant || "-"}
                          </p>
                        </div>
                        <div>
                          <p className="text-muted-foreground">Fonction</p>
                          <p className="font-medium">
                            {(selectedAssociationGovernance?.representantLegal?.fonction
                              ? legalRepresentativeRoleLabels[selectedAssociationGovernance.representantLegal.fonction as keyof typeof legalRepresentativeRoleLabels] || selectedAssociationGovernance.representantLegal.fonction
                              : selectedDirectoryEntry.entry.fonctionRepresentant) || "-"}
                          </p>
                        </div>
                        {selectedAssociationGovernance?.membres?.length ? (
                          <div className="sm:col-span-2">
                            <p className="text-muted-foreground">Autres membres de gouvernance</p>
                            <div className="mt-2 space-y-2">
                              {selectedAssociationGovernance.membres.map((member, index) => (
                                <div key={`${member.nom}-${member.prenom}-${index}`} className="rounded-lg border bg-muted/20 px-3 py-2">
                                  <p className="font-medium">{buildDisplayName(member) || "-"}</p>
                                  <p className="text-sm text-muted-foreground">
                                    {governanceMemberRoleLabels[member.fonction as keyof typeof governanceMemberRoleLabels] || member.fonction || "-"}
                                  </p>
                                </div>
                              ))}
                            </div>
                          </div>
                        ) : null}
                        <div className="sm:col-span-2">
                          <p className="text-muted-foreground">Objet de l'association</p>
                          <p className="font-medium whitespace-pre-wrap">{selectedDirectoryEntry.entry.objetAssociation || "-"}</p>
                        </div>
                      </div>
                    </CardContent>
                  </Card>

                  <Card>
                    <CardHeader>
                      <CardTitle>Espace portail</CardTitle>
                      <CardDescription>
                        Informations enrichies par l'association depuis son propre espace.
                      </CardDescription>
                    </CardHeader>
                    <CardContent className="space-y-4 text-sm">
                      {selectedDirectoryEntry.association ? (
                        <>
                          <div className="grid gap-3 sm:grid-cols-2">
                            <div>
                              <p className="text-muted-foreground">Email de contact</p>
                              <p className="font-medium">{selectedDirectoryEntry.association.emailContact || "-"}</p>
                            </div>
                            <div>
                              <p className="text-muted-foreground">Profil complet</p>
                              <p className="font-medium">{selectedDirectoryEntry.association.profileComplete ? "Oui" : "À compléter"}</p>
                            </div>
                            <div>
                              <p className="text-muted-foreground">Statut du compte</p>
                              <p className="font-medium">{selectedDirectoryEntry.association.isActive ? "Actif" : "Inactif"}</p>
                            </div>
                            <div>
                              <p className="text-muted-foreground">Dernière mise à jour</p>
                              <p className="font-medium">{formatDateTime(selectedDirectoryEntry.association.updatedAt)}</p>
                            </div>
                          </div>
                          <div className="grid gap-3 sm:grid-cols-3">
                            <div className="rounded-lg border bg-muted/30 p-3">
                              <p className="text-xs text-muted-foreground">Documents</p>
                              <p className="mt-1 text-2xl font-semibold">{selectedDirectoryEntry.documents.length}</p>
                            </div>
                            <div className="rounded-lg border bg-muted/30 p-3">
                              <p className="text-xs text-muted-foreground">Demandes</p>
                              <p className="mt-1 text-2xl font-semibold">{selectedDirectoryEntry.requests.length}</p>
                            </div>
                            <div className="rounded-lg border bg-muted/30 p-3">
                              <p className="text-xs text-muted-foreground">Ville retenue</p>
                              <p className="mt-1 text-base font-semibold">{selectedDirectoryEntry.association.ville || selectedDirectoryEntry.entry.ville || "-"}</p>
                            </div>
                          </div>
                        </>
                      ) : (
                        <div className="rounded-lg border border-dashed p-4 text-sm text-muted-foreground">
                          Cette association n'a pas encore activé son espace. Le bordereau reste visible et servira de base à la première inscription.
                        </div>
                      )}
                    </CardContent>
                  </Card>
                </div>

                <Card>
                  <CardHeader>
                    <CardTitle className="flex items-center gap-2">
                      <MapPinned className="h-5 w-5 text-primary" />
                      Géolocalisation publique
                    </CardTitle>
                    <CardDescription>
                      Position de l'association sur la carte publique et état de la dernière synchronisation.
                    </CardDescription>
                  </CardHeader>
                  <CardContent className="space-y-4">
                    {(() => {
                      const geoStatus = getDirectoryGeoStatus(selectedDirectoryEntry.entry);

                      return (
                        <div className="flex flex-wrap items-center gap-2">
                          <Badge variant="outline" className={geoStatus.className}>
                            {geoStatus.label}
                          </Badge>
                          <span className="text-sm text-muted-foreground">
                            {selectedDirectoryEntry.entry.geoSource
                              ? associationGeoSourceLabels[selectedDirectoryEntry.entry.geoSource as keyof typeof associationGeoSourceLabels]
                              : "Aucune source cartographique active"}
                          </span>
                        </div>
                      );
                    })()}

                    <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
                      <div className="rounded-lg border bg-muted/20 p-4">
                        <p className="text-xs uppercase tracking-wide text-muted-foreground">Latitude</p>
                        <p className="mt-2 font-medium">{selectedDirectoryEntry.entry.latitude || "-"}</p>
                      </div>
                      <div className="rounded-lg border bg-muted/20 p-4">
                        <p className="text-xs uppercase tracking-wide text-muted-foreground">Longitude</p>
                        <p className="mt-2 font-medium">{selectedDirectoryEntry.entry.longitude || "-"}</p>
                      </div>
                      <div className="rounded-lg border bg-muted/20 p-4">
                        <p className="text-xs uppercase tracking-wide text-muted-foreground">Source</p>
                        <p className="mt-2 font-medium">
                          {selectedDirectoryEntry.entry.geoSource
                            ? associationGeoSourceLabels[selectedDirectoryEntry.entry.geoSource as keyof typeof associationGeoSourceLabels]
                            : "Non calculée"}
                        </p>
                      </div>
                      <div className="rounded-lg border bg-muted/20 p-4">
                        <p className="text-xs uppercase tracking-wide text-muted-foreground">Dernière synchro</p>
                        <p className="mt-2 font-medium">{formatDateTime(selectedDirectoryEntry.entry.geoLastSyncedAt)}</p>
                      </div>
                    </div>

                    <div className="grid gap-4 lg:grid-cols-[1fr_1fr_220px_auto]">
                      <div className="space-y-2">
                        <Label htmlFor="directory-manual-latitude">Latitude manuelle</Label>
                        <Input
                          id="directory-manual-latitude"
                          value={manualGeoForm.latitude}
                          onChange={(event) =>
                            setManualGeoForm((prev) => ({
                              ...prev,
                              latitude: event.target.value,
                            }))
                          }
                          placeholder="5.167389"
                        />
                      </div>
                      <div className="space-y-2">
                        <Label htmlFor="directory-manual-longitude">Longitude manuelle</Label>
                        <Input
                          id="directory-manual-longitude"
                          value={manualGeoForm.longitude}
                          onChange={(event) =>
                            setManualGeoForm((prev) => ({
                              ...prev,
                              longitude: event.target.value,
                            }))
                          }
                          placeholder="-52.654221"
                        />
                      </div>
                      <div className="space-y-2">
                        <Label htmlFor="directory-manual-precision">Précision publique</Label>
                        <Select
                          value={manualGeoForm.geoPrecision}
                          onValueChange={(value) =>
                            setManualGeoForm((prev) => ({
                              ...prev,
                              geoPrecision: value as "exact_address" | "commune_center" | "hidden",
                            }))
                          }
                        >
                          <SelectTrigger id="directory-manual-precision">
                            <SelectValue />
                          </SelectTrigger>
                          <SelectContent>
                            {Object.entries(associationGeoPrecisionLabels).map(([value, label]) => (
                              <SelectItem key={value} value={value}>
                                {label}
                              </SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                      </div>
                      <div className="flex items-end">
                        <Button
                          type="button"
                          variant="outline"
                          onClick={handleSaveManualCoordinates}
                          disabled={setDirectoryManualCoordinatesMutation.isPending}
                        >
                          {setDirectoryManualCoordinatesMutation.isPending ? (
                            <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                          ) : (
                            <MapPinned className="mr-2 h-4 w-4" />
                          )}
                          Enregistrer la position
                        </Button>
                      </div>
                    </div>

                    <div className="flex flex-wrap gap-2">
                        <Button
                          type="button"
                          onClick={() => syncDirectoryGeoMutation.mutate({ id: selectedDirectoryEntry.entry.id })}
                          disabled={syncDirectoryGeoMutation.isPending}
                        >
                          {syncDirectoryGeoMutation.isPending ? (
                            <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                          ) : (
                            <MapPinned className="mr-2 h-4 w-4" />
                          )}
                          Relancer la géolocalisation
                        </Button>
                        <Link href="/associations/carte" {...buildRoutePrefetchHandlers("/associations/carte")}>
                          <Button type="button" variant="outline">
                            <ArrowUpRight className="mr-2 h-4 w-4" />
                            Voir la carte publique
                          </Button>
                        </Link>
                    </div>

                    <div className="rounded-lg border border-dashed p-4 text-sm text-muted-foreground">
                      État d'enrichissement : {selectedDirectoryEntry.entry.externalSourceStatus || "non lancé"}.
                      {selectedDirectoryEntry.entry.externalSourceLabel ? ` Source affichée : ${selectedDirectoryEntry.entry.externalSourceLabel}.` : ""}
                    </div>
                  </CardContent>
                </Card>

                <Card>
                  <CardHeader>
                    <CardTitle>Documents de l'association</CardTitle>
                    <CardDescription>
                      Les pièces ajoutées dans l'espace portail enrichissent immédiatement la lecture administrative.
                    </CardDescription>
                  </CardHeader>
                  <CardContent>
                    {selectedDirectoryEntry.documents.length > 0 ? (
                      <div className="space-y-3">
                        {selectedDirectoryEntry.documents.map((document) => (
                          <div key={document.id} className="flex flex-col gap-2 rounded-lg border p-3 sm:flex-row sm:items-center sm:justify-between">
                            <div>
                              <p className="font-medium">{document.nom}</p>
                              <p className="text-sm text-muted-foreground">
                                {document.type || "Document"} • ajouté le {formatDateTime(document.uploadedAt)}
                              </p>
                            </div>
                            {selectedDirectoryEntry.association ? (
                              <Button type="button" size="sm" variant="outline" onClick={() => setSelectedAssociation(selectedDirectoryEntry.association)}>
                                <FileText className="mr-2 h-4 w-4" />
                                Voir les documents
                              </Button>
                            ) : null}
                          </div>
                        ))}
                      </div>
                    ) : (
                      <div className="rounded-lg border border-dashed p-4 text-sm text-muted-foreground">
                        Aucun document n'a encore été déposé par l'association.
                      </div>
                    )}
                  </CardContent>
                </Card>

                <Card>
                  <CardHeader>
                    <CardTitle>Demandes liées</CardTitle>
                    <CardDescription>
                      Vue rapide de l'activité récente de l'association sur le portail.
                    </CardDescription>
                  </CardHeader>
                  <CardContent>
                    {selectedDirectoryEntry.requests.length > 0 ? (
                      <div className="space-y-3">
                        {selectedDirectoryEntry.requests.slice(0, 8).map((request) => (
                          <div key={request.id} className="flex flex-col gap-2 rounded-lg border p-3 sm:flex-row sm:items-center sm:justify-between">
                            <div className="min-w-0">
                              <p className="font-medium">{request.titre}</p>
                              <p className="text-sm text-muted-foreground">
                                {requestTypeLabels[request.type] || request.type} • créée le {formatDate(request.createdAt)}
                              </p>
                            </div>
                            <div className="flex items-center gap-2">
                              <Badge variant="outline" className={statusConfig[request.status]?.className}>
                                {statusConfig[request.status]?.label || request.status}
                              </Badge>
                              <Link href={`/dashboard/requests/${request.id}`} {...buildRoutePrefetchHandlers(`/dashboard/requests/${request.id}`)}>
                                <Button type="button" size="sm" variant="outline">
                                  <ArrowUpRight className="mr-2 h-4 w-4" />
                                  Ouvrir
                                </Button>
                              </Link>
                            </div>
                          </div>
                        ))}
                      </div>
                    ) : (
                      <div className="rounded-lg border border-dashed p-4 text-sm text-muted-foreground">
                        Aucune demande n'a encore été déposée depuis le portail.
                      </div>
                    )}
                  </CardContent>
                </Card>
              </div>
            ) : (
              <div className="rounded-lg border border-dashed p-6 text-center text-sm text-muted-foreground">
                Impossible de charger cette fiche annuaire.
              </div>
            )}
          </ScrollArea>
          <DialogFooter>
            <Button variant="outline" onClick={() => setSelectedDirectoryEntryId(null)}>
              Fermer
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog open={editingDirectoryEntry !== null} onOpenChange={(open) => !open && setEditingDirectoryEntry(null)}>
        <DialogContent className="max-w-3xl max-h-[90vh]">
          <DialogHeader>
            <DialogTitle>
              {editingDirectoryEntry?.id ? "Modifier la fiche du bordereau" : "Nouvelle association du bordereau"}
            </DialogTitle>
            <DialogDescription>
              Le bordereau reste la source de vérité interne. Toute modification ici se répercute dans l’annuaire public.
            </DialogDescription>
          </DialogHeader>
          {editingDirectoryEntry ? (
            <ScrollArea className="h-[70vh] pr-4">
              <div className="grid gap-4 py-1 md:grid-cols-2">
                <div className="md:col-span-2">
                  <Label>Nom de l'association</Label>
                  <Input
                    value={editingDirectoryEntry.nomAssociation}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, nomAssociation: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Email officiel</Label>
                  <Input
                    value={editingDirectoryEntry.emailOfficiel}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, emailOfficiel: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Téléphone</Label>
                  <Input
                    value={editingDirectoryEntry.telephone}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, telephone: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>SIRET</Label>
                  <Input
                    value={editingDirectoryEntry.siret}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, siret: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>RNA</Label>
                  <Input
                    value={editingDirectoryEntry.rna}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, rna: e.target.value } : prev)}
                  />
                </div>
                <div className="md:col-span-2">
                  <Label>Adresse</Label>
                  <Textarea
                    rows={2}
                    value={editingDirectoryEntry.adresse}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, adresse: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Code postal</Label>
                  <Input
                    value={editingDirectoryEntry.codePostal}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, codePostal: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Ville</Label>
                  <Input
                    value={editingDirectoryEntry.ville}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, ville: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Date de création</Label>
                  <Input
                    type="date"
                    value={editingDirectoryEntry.dateCreation}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, dateCreation: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Statut juridique</Label>
                  <Select
                    value={editingDirectoryEntry.statutJuridique}
                    onValueChange={(value) =>
                      setEditingDirectoryEntry((prev) =>
                        prev ? { ...prev, statutJuridique: value as DirectoryEntryFormState["statutJuridique"] } : prev
                      )
                    }
                  >
                    <SelectTrigger>
                      <SelectValue />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="association_loi_1901">Association loi 1901</SelectItem>
                      <SelectItem value="association_reconnue_utilite_publique">Association reconnue d'utilité publique</SelectItem>
                      <SelectItem value="fondation">Fondation</SelectItem>
                      <SelectItem value="autre">Autre</SelectItem>
                    </SelectContent>
                  </Select>
                </div>
                <div>
                  <Label>Site web</Label>
                  <Input
                    value={editingDirectoryEntry.siteWeb}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, siteWeb: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Facebook</Label>
                  <Input
                    value={editingDirectoryEntry.facebookUrl}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, facebookUrl: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Instagram</Label>
                  <Input
                    value={editingDirectoryEntry.instagramUrl}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, instagramUrl: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Nom du représentant</Label>
                  <Input
                    value={editingDirectoryEntry.nomRepresentant}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, nomRepresentant: e.target.value } : prev)}
                  />
                </div>
                <div>
                  <Label>Fonction du représentant</Label>
                  <Input
                    value={editingDirectoryEntry.fonctionRepresentant}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, fonctionRepresentant: e.target.value } : prev)}
                  />
                </div>
                <div className="md:col-span-2">
                  <Label>Thématiques</Label>
                  <div className="mt-2 grid gap-2 md:grid-cols-2">
                    {associationThematicOptions.map((option) => {
                      const checked = editingDirectoryEntry.thematiques.includes(option.value);
                      return (
                        <label key={option.value} className="flex items-start gap-3 rounded-lg border p-3 text-sm">
                          <Checkbox
                            checked={checked}
                            onCheckedChange={(nextChecked) =>
                              setEditingDirectoryEntry((prev) =>
                                prev
                                  ? {
                                      ...prev,
                                      thematiques: nextChecked
                                        ? Array.from(new Set([...prev.thematiques, option.value]))
                                        : prev.thematiques.filter((entry) => entry !== option.value),
                                    }
                                  : prev
                              )
                            }
                          />
                          <div>
                            <p className="font-medium">{option.label}</p>
                            <p className="text-muted-foreground">{option.description}</p>
                          </div>
                        </label>
                      );
                    })}
                  </div>
                </div>
                <div className="md:col-span-2">
                  <Label>Objet de l'association</Label>
                  <Textarea
                    rows={4}
                    value={editingDirectoryEntry.objetAssociation}
                    onChange={(e) => setEditingDirectoryEntry((prev) => prev ? { ...prev, objetAssociation: e.target.value } : prev)}
                  />
                </div>
                <div className="md:col-span-2 flex items-center justify-between rounded-lg border p-3">
                  <div>
                    <p className="font-medium">Fiche active</p>
                    <p className="text-sm text-muted-foreground">Une fiche inactive reste archivée mais n’est plus retenue comme référence active.</p>
                  </div>
                  <Switch
                    checked={editingDirectoryEntry.isActive}
                    onCheckedChange={(checked) => setEditingDirectoryEntry((prev) => prev ? { ...prev, isActive: checked } : prev)}
                  />
                </div>
              </div>
            </ScrollArea>
          ) : null}
          <DialogFooter>
            <Button variant="outline" onClick={() => setEditingDirectoryEntry(null)}>
              Annuler
            </Button>
            <Button
              onClick={handleSaveDirectoryEntry}
              disabled={createManualDirectoryEntryMutation.isPending || updateManualDirectoryEntryMutation.isPending}
            >
              {(createManualDirectoryEntryMutation.isPending || updateManualDirectoryEntryMutation.isPending) ? (
                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
              ) : (
                <Save className="mr-2 h-4 w-4" />
              )}
              Enregistrer la fiche
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Process dialog */}
      <Dialog open={!!selectedRequest} onOpenChange={() => setSelectedRequest(null)}>
        <DialogContent
          className={
            selectedRequestIsSalle
              ? "max-h-[92vh] max-w-[calc(100%-1rem)] overflow-hidden p-0 sm:max-h-[90vh] sm:max-w-4xl"
              : selectedRequestIsMateriel
                ? "max-h-[92vh] max-w-[calc(100%-1rem)] overflow-hidden p-0 sm:max-h-[90vh] sm:max-w-2xl"
                : "max-h-[92vh] max-w-[calc(100%-1rem)] overflow-hidden p-0 sm:max-h-[90vh] sm:max-w-md"
          }
        >
          <DialogHeader className="border-b bg-background px-4 py-4 text-left sm:px-6">
            <DialogTitle>Traiter la demande</DialogTitle>
            <DialogDescription>
              {selectedRequest?.titre}
            </DialogDescription>
          </DialogHeader>
          <div className={cn("max-h-[calc(92vh-150px)] space-y-4 overflow-y-auto px-4 py-4 sm:max-h-[calc(90vh-160px)] sm:px-6", isMobile && "pb-28")}>
            <div className="space-y-2">
              <Label>Décision *</Label>
              <Select
                value={processData.status}
                onValueChange={(value) => setProcessData(prev => ({ ...prev, status: value }))}
              >
                <SelectTrigger>
                  <SelectValue placeholder="Sélectionnez une décision" />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="en_cours_traitement">En cours de traitement</SelectItem>
                  <SelectItem value="information_complementaire">Demander des informations</SelectItem>
                  <SelectItem value="validee">Valider</SelectItem>
                  <SelectItem value="refusee">Refuser</SelectItem>
                </SelectContent>
              </Select>
            </div>

            {processData.status === 'validee' && selectedRequest?.montantDemande && (
              <div className="space-y-2">
                <Label htmlFor="montantAccorde">Montant accordé (€)</Label>
                <Input
                  id="montantAccorde"
                  type="number"
                  value={processData.montantAccorde}
                  onChange={(e) => setProcessData(prev => ({ ...prev, montantAccorde: e.target.value }))}
                />
              </div>
            )}

            {(selectedRequestIsSalle || selectedRequestIsMateriel) && (
              <>
                <Separator />
                <div className="space-y-4 rounded-lg border bg-muted/20 p-4">
                  <div className="flex items-start gap-2">
                    <Shield className="h-4 w-4 text-primary" />
                    <div>
                      <h4 className="text-sm font-semibold">Cadre DSU</h4>
                      <p className="text-xs text-muted-foreground">
                        Cette partie accompagne le traitement administratif de la demande.
                      </p>
                    </div>
                  </div>

                  <div className="grid gap-4 md:grid-cols-2">
                    <div className="space-y-2">
                      <Label htmlFor="adminDsuDateReception">Date de réception</Label>
                      <Input
                        id="adminDsuDateReception"
                        type="date"
                        value={dsuProcessData.dateReception}
                        onChange={(e) => setDsuProcessData((prev) => ({ ...prev, dateReception: e.target.value }))}
                      />
                    </div>
                    <div className="space-y-2">
                      <Label htmlFor="adminDsuDateDecision">Date de décision</Label>
                      <Input
                        id="adminDsuDateDecision"
                        type="date"
                        value={dsuProcessData.dateDecision}
                        onChange={(e) => setDsuProcessData((prev) => ({ ...prev, dateDecision: e.target.value }))}
                      />
                    </div>
                  </div>

                  <div className="space-y-2">
                    <Label htmlFor="adminDsuResponsable">Responsable DSU *</Label>
                    <Input
                      id="adminDsuResponsable"
                      value={dsuProcessData.responsableDSU}
                      onChange={(e) => setDsuProcessData((prev) => ({ ...prev, responsableDSU: e.target.value }))}
                      placeholder="Nom et prénom du responsable"
                    />
                  </div>

                  {selectedRequestIsMateriel && (
                    <div className="space-y-3 rounded-lg border bg-muted/30 p-4">
                      <div>
                        <h4 className="text-sm font-semibold">Vue de la demande</h4>
                        <p className="text-xs text-muted-foreground">
                          L'administration voit ici exactement ce qui est demandé, puis répond sur ce qu'elle peut accorder.
                        </p>
                      </div>

                      {selectedRequestedMaterialItems.length > 0 ? (
                        <div className="grid gap-4 md:grid-cols-2">
                          {selectedRequestedMaterialItems.map((item) => (
                            <div key={item.key} className="rounded-lg border border-primary/20 bg-white p-4 space-y-3">
                              <div className="flex items-start justify-between gap-3">
                                <div>
                                  <p className="font-medium">{item.label}</p>
                                  <p className="text-xs text-muted-foreground mt-1">Matériel demandé</p>
                                </div>
                                <span className="rounded-full bg-amber-100 px-2.5 py-1 text-xs font-medium text-amber-900">
                                  Demandé
                                </span>
                              </div>
                              <div className="rounded-lg border border-dashed bg-amber-50 px-4 py-3">
                                <p className="text-xs text-muted-foreground">Quantité demandée</p>
                                <p className="text-3xl font-bold mt-1">{selectedRequestFormData.quantitesDemandees?.[item.key] || "-"}</p>
                              </div>
                              <div className="flex flex-col gap-3 pt-1 sm:flex-row sm:items-center">
                                <Switch
                                  checked={Boolean(dsuProcessData.materielEvent.itemsAccordes[item.key])}
                                  onCheckedChange={(checked) =>
                                    setDsuProcessData((prev) => ({
                                      ...prev,
                                      materielEvent: {
                                        ...prev.materielEvent,
                                        itemsAccordes: {
                                          ...prev.materielEvent.itemsAccordes,
                                          [item.key]: Boolean(checked),
                                        },
                                        quantitesAccordees: {
                                          ...prev.materielEvent.quantitesAccordees,
                                          [item.key]: checked
                                            ? (prev.materielEvent.quantitesAccordees[item.key] || String(parseMaterialEventQuantity(selectedRequestFormData.quantitesDemandees?.[item.key]) || 1))
                                            : '',
                                        },
                                      },
                                    }))
                                  }
                                />
                                <span className="text-sm font-medium">Accorder ce matériel</span>
                              </div>
                              <div className="space-y-2">
                                <Label>
                                  Quantité accordée
                                  {selectedRequestFormData.quantitesDemandees?.[item.key] ? ` sur ${selectedRequestFormData.quantitesDemandees[item.key]} demandée(s)` : ''}
                                </Label>
                                <Input
                                  type="number"
                                  min={0}
                                  value={dsuProcessData.materielEvent.quantitesAccordees[item.key]}
                                  onChange={(e) =>
                                    setDsuProcessData((prev) => ({
                                      ...prev,
                                      materielEvent: {
                                        ...prev.materielEvent,
                                        quantitesAccordees: {
                                          ...prev.materielEvent.quantitesAccordees,
                                          [item.key]: e.target.value,
                                        },
                                      },
                                    }))
                                  }
                                  disabled={!dsuProcessData.materielEvent.itemsAccordes[item.key]}
                                  placeholder="..."
                                />
                              </div>
                            </div>
                          ))}
                        </div>
                      ) : (
                        <p className="text-sm text-muted-foreground">Aucun matériel demandé sur cette fiche.</p>
                      )}

                      {selectedNonRequestedMaterialItems.length > 0 && (
                        <div className="space-y-3">
                          <Label className="text-sm font-semibold">Autres matériels disponibles</Label>
                          <div className="grid gap-4 md:grid-cols-2">
                            {selectedNonRequestedMaterialItems.map((item) => (
                              <div key={item.key} className="rounded-lg border border-dashed p-4 space-y-3 opacity-80">
                                <div className="flex items-center justify-between gap-3">
                                  <span className="font-medium">{item.label}</span>
                                  <span className="rounded-full bg-muted px-2.5 py-1 text-xs text-muted-foreground">
                                    Non demandé
                                  </span>
                                </div>
                                <div className="flex flex-col gap-3 sm:flex-row sm:items-center">
                                  <Switch
                                    checked={Boolean(dsuProcessData.materielEvent.itemsAccordes[item.key])}
                                    onCheckedChange={(checked) =>
                                      setDsuProcessData((prev) => ({
                                        ...prev,
                                        materielEvent: {
                                          ...prev.materielEvent,
                                          itemsAccordes: {
                                            ...prev.materielEvent.itemsAccordes,
                                            [item.key]: Boolean(checked),
                                          },
                                          quantitesAccordees: {
                                            ...prev.materielEvent.quantitesAccordees,
                                            [item.key]: checked ? (prev.materielEvent.quantitesAccordees[item.key] || '1') : '',
                                          },
                                        },
                                      }))
                                    }
                                  />
                                  <span className="text-sm font-medium">Accorder malgré tout</span>
                                </div>
                                <div className="space-y-2">
                                  <Label>Quantité accordée</Label>
                                  <Input
                                    type="number"
                                    min={0}
                                    value={dsuProcessData.materielEvent.quantitesAccordees[item.key]}
                                    onChange={(e) =>
                                      setDsuProcessData((prev) => ({
                                        ...prev,
                                        materielEvent: {
                                          ...prev.materielEvent,
                                          quantitesAccordees: {
                                            ...prev.materielEvent.quantitesAccordees,
                                            [item.key]: e.target.value,
                                          },
                                        },
                                      }))
                                    }
                                    disabled={!dsuProcessData.materielEvent.itemsAccordes[item.key]}
                                    placeholder="..."
                                  />
                                </div>
                              </div>
                            ))}
                          </div>
                        </div>
                      )}

                      {Boolean(dsuProcessData.materielEvent.itemsAccordes.autres) && (
                        <div className="space-y-2">
                          <Label htmlFor="adminAutresPrecisions">Précisions sur "Autres"</Label>
                          <Textarea
                            id="adminAutresPrecisions"
                            value={dsuProcessData.materielEvent.autresPrecisions}
                            onChange={(e) =>
                              setDsuProcessData((prev) => ({
                                ...prev,
                                materielEvent: {
                                  ...prev.materielEvent,
                                  autresPrecisions: e.target.value,
                                },
                              }))
                            }
                            rows={3}
                          />
                        </div>
                      )}
                    </div>
                  )}

                  {selectedRequestNeedsFinancialDecision && (
                    <div className="rounded-lg border border-primary/20 bg-primary/5 p-4 space-y-4">
                      <div className="flex items-center gap-2">
                        <FileText className="h-4 w-4 text-primary" />
                        <div>
                          <h4 className="text-sm font-semibold">Conditions financières / location</h4>
                          <p className="text-xs text-muted-foreground">
                            Ce choix est obligatoire au moment de la validation d’une {selectedRequestIsSalle ? 'demande de salle' : 'demande de matériel'}.
                          </p>
                        </div>
                      </div>

                      {selectedRequestIsSalle && (
                        <>
                          <div className="grid gap-4 md:grid-cols-2">
                            <div className="space-y-2">
                              <Label>Type d'usage interne</Label>
                              <Select
                                value={dsuProcessData.salleReservation.usageType}
                                onValueChange={(value: SalleUsageType) =>
                                  setDsuProcessData((prev) => ({
                                    ...prev,
                                    salleReservation: {
                                      ...prev.salleReservation,
                                      usageType: value,
                                    },
                                  }))
                                }
                              >
                                <SelectTrigger>
                                  <SelectValue />
                                </SelectTrigger>
                                <SelectContent>
                                  <SelectItem value="conventionne">Conventionné</SelectItem>
                                  <SelectItem value="occasionnel">Occasionnel</SelectItem>
                                </SelectContent>
                              </Select>
                            </div>

                            <div className="space-y-2">
                              <Label>Fréquence de tarification</Label>
                              <Select
                                value={dsuProcessData.salleReservation.frequency}
                                onValueChange={(value: SalleReservationFrequency) =>
                                  setDsuProcessData((prev) => ({
                                    ...prev,
                                    salleReservation: {
                                      ...prev.salleReservation,
                                      frequency: value,
                                    },
                                  }))
                                }
                              >
                                <SelectTrigger>
                                  <SelectValue />
                                </SelectTrigger>
                                <SelectContent>
                                  <SelectItem value="demi_journee">Demi-journée</SelectItem>
                                  <SelectItem value="journee">Journée</SelectItem>
                                  <SelectItem value="mensuel">Mensuel</SelectItem>
                                </SelectContent>
                              </Select>
                            </div>
                          </div>

                          <div className="rounded-lg border bg-white p-4 space-y-3">
                            <div className="flex items-center justify-between gap-3">
                              <div>
                                <h5 className="text-sm font-semibold">Décision administrative prioritaire</h5>
                                <p className="text-xs text-muted-foreground">
                                  Le texte officiel est généré automatiquement à partir de la salle, des dates et des horaires demandés.
                                </p>
                              </div>
                              <Badge variant="outline">
                                {SALLE_USAGE_TYPE_LABELS[dsuProcessData.salleReservation.usageType]} • {SALLE_FREQUENCY_LABELS[dsuProcessData.salleReservation.frequency]}
                              </Badge>
                            </div>
                            <Textarea value={selectedSalleDecisionAdministrativeText} readOnly rows={8} className="bg-muted/20" />
                          </div>

                          <div className="rounded-lg border bg-white p-4 space-y-3">
                            <div>
                              <h5 className="text-sm font-semibold">Calcul automatique du devis</h5>
                              <p className="text-xs text-muted-foreground">
                                Le montant est calculé automatiquement selon la grille tarifaire CCDS que tu as fournie.
                              </p>
                            </div>
                            {selectedSallePricingPreview && selectedSallePricingPreview.unsupportedSalles.length === 0 ? (
                              <>
                                <div className="space-y-2">
                                  {selectedSallePricingPreview.lineItems.map((line) => (
                                    <div key={`${line.salleId}-${line.frequency}`} className="flex items-start justify-between gap-4 rounded-md border px-3 py-2 text-sm">
                                      <div>
                                        <p className="font-medium">{line.salleNom}</p>
                                        <p className="text-muted-foreground">
                                          {line.categoryLabel} • {line.quantity} x {formatAmount(line.unitAmountCents)}
                                        </p>
                                      </div>
                                      <span className="font-semibold">{formatAmount(line.totalAmountCents)}</span>
                                    </div>
                                  ))}
                                </div>
                                <div className="flex items-center justify-between rounded-md border border-primary/20 bg-primary/5 px-3 py-2">
                                  <span className="text-sm font-medium">Total estimé du devis</span>
                                  <span className="text-base font-semibold text-primary">
                                    {formatAmount(selectedSallePricingPreview.totalAmountCents)}
                                  </span>
                                </div>
                              </>
                            ) : (
                              <div className="rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900">
                                {selectedSallePricingPreview?.unsupportedSalles?.length
                                  ? `Aucune tarification automatique n'est disponible pour : ${selectedSallePricingPreview.unsupportedSalles.map((item) => item.salleNom).join(', ')} avec cette combinaison.`
                                  : "Le devis sera disponible dès qu'une combinaison salle / fréquence compatible sera choisie."}
                              </div>
                            )}
                          </div>

                          <div className="rounded-lg border bg-white p-4 space-y-3">
                            <div>
                              <h5 className="text-sm font-semibold">Conditions financières officielles</h5>
                              <p className="text-xs text-muted-foreground">
                                Ce texte sera repris dans le dossier transmis à l'association.
                              </p>
                            </div>
                            <Textarea value={CONDITIONS_FINANCIERES_SALLE_TEXT} readOnly rows={12} className="bg-muted/20" />
                          </div>
                        </>
                      )}

                      <div className="space-y-2">
                        <Label>Mode financier *</Label>
                        <Select
                          value={dsuProcessData.materielEvent.financialDecision.financialMode}
                          onValueChange={(value: MaterialFinancialMode) =>
                            setDsuProcessData((prev) => ({
                              ...prev,
                              materielEvent: {
                                ...prev.materielEvent,
                                financialDecision: {
                                  ...prev.materielEvent.financialDecision,
                                  financialMode: value,
                                  rentalAmountEuros: value === 'location_payante' ? prev.materielEvent.financialDecision.rentalAmountEuros : '',
                                  depositRequired: value === 'gratuite_avec_caution'
                                    ? true
                                    : prev.materielEvent.financialDecision.depositRequired,
                                },
                              },
                            }))
                          }
                        >
                          <SelectTrigger>
                            <SelectValue />
                          </SelectTrigger>
                          <SelectContent>
                            <SelectItem value="gratuite">Mise à disposition gratuite</SelectItem>
                            <SelectItem value="gratuite_avec_caution">Gratuite avec caution</SelectItem>
                            <SelectItem value="location_payante">Location payante</SelectItem>
                          </SelectContent>
                        </Select>
                      </div>

                      <div className="rounded-md border bg-white px-3 py-2 text-sm">
                        <span className="text-muted-foreground">Mode retenu :</span>{" "}
                        <span className="font-medium">{getFinancialModeLabel(dsuProcessData.materielEvent.financialDecision.financialMode)}</span>
                      </div>

                      <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:space-x-0">
                        <Switch
                          id="adminDepositRequired"
                          checked={dsuProcessData.materielEvent.financialDecision.depositRequired}
                          onCheckedChange={(checked) =>
                            setDsuProcessData((prev) => ({
                              ...prev,
                              materielEvent: {
                                ...prev.materielEvent,
                                financialDecision: {
                                  ...prev.materielEvent.financialDecision,
                                  depositRequired: Boolean(checked),
                                  depositAmountEuros: checked ? prev.materielEvent.financialDecision.depositAmountEuros : '',
                                },
                              },
                            }))
                          }
                        />
                        <Label htmlFor="adminDepositRequired">Caution / dépôt de garantie requis</Label>
                      </div>

                      <div className="grid gap-4 md:grid-cols-2">
                        <div className="space-y-2">
                          <Label htmlFor="adminDepositAmount">Montant de la caution (€)</Label>
                          <Input
                            id="adminDepositAmount"
                            type="number"
                            min={0}
                            step="0.01"
                            value={dsuProcessData.materielEvent.financialDecision.depositAmountEuros}
                            onChange={(e) =>
                              setDsuProcessData((prev) => ({
                                ...prev,
                                materielEvent: {
                                  ...prev.materielEvent,
                                  financialDecision: {
                                    ...prev.materielEvent.financialDecision,
                                    depositAmountEuros: e.target.value,
                                  },
                                },
                              }))
                            }
                            disabled={!dsuProcessData.materielEvent.financialDecision.depositRequired}
                            placeholder="0.00"
                          />
                        </div>

                        <div className="space-y-2">
                          <Label htmlFor="adminRentalAmount">
                            {selectedRequestIsSalle ? 'Montant de location calculé (€)' : 'Montant de location (€)'}
                          </Label>
                          <Input
                            id="adminRentalAmount"
                            type="number"
                            min={0}
                            step="0.01"
                            value={dsuProcessData.materielEvent.financialDecision.rentalAmountEuros}
                            onChange={(e) =>
                              setDsuProcessData((prev) => ({
                                ...prev,
                                materielEvent: {
                                  ...prev.materielEvent,
                                  financialDecision: {
                                    ...prev.materielEvent.financialDecision,
                                    rentalAmountEuros: e.target.value,
                                  },
                                },
                              }))
                            }
                            disabled={
                              dsuProcessData.materielEvent.financialDecision.financialMode !== 'location_payante'
                              || selectedRequestIsSalle
                            }
                            placeholder="0.00"
                          />
                        </div>
                      </div>

                      <div className="space-y-2">
                        <Label htmlFor="adminPricingNotes">Clauses spécifiques / réserves</Label>
                        <Textarea
                          id="adminPricingNotes"
                          value={dsuProcessData.materielEvent.financialDecision.pricingNotes}
                          onChange={(e) =>
                            setDsuProcessData((prev) => ({
                              ...prev,
                              materielEvent: {
                                ...prev.materielEvent,
                                financialDecision: {
                                  ...prev.materielEvent.financialDecision,
                                  pricingNotes: e.target.value,
                                },
                              },
                            }))
                          }
                          rows={3}
                          placeholder={selectedRequestIsSalle
                            ? "Ex. acompte demandé avant remise des clés, attestation RC obligatoire, consignes particulières de la Directrice..."
                            : "Ex. caution libérée après restitution conforme, remise exceptionnelle, clauses de location..."}
                        />
                      </div>
                    </div>
                  )}

                  <div className="space-y-2">
                    <Label htmlFor="adminDsuObservations">Observations DSU</Label>
                    <Textarea
                      id="adminDsuObservations"
                      value={dsuProcessData.observationsDSU}
                      onChange={(e) => setDsuProcessData((prev) => ({ ...prev, observationsDSU: e.target.value }))}
                      rows={3}
                      placeholder="Observations internes ou précisions sur la décision..."
                    />
                  </div>
                </div>
              </>
            )}

            <div className="space-y-2">
              <Label htmlFor="commentaire">Commentaire</Label>
              <Textarea
                id="commentaire"
                value={processData.commentaireAdmin}
                onChange={(e) => setProcessData(prev => ({ ...prev, commentaireAdmin: e.target.value }))}
                placeholder="Ajoutez un commentaire pour l'association..."
                rows={3}
              />
            </div>

            {selectedRequestIsMateriel && (
              <>
                <Separator />

                <div className="space-y-2">
                  <Label htmlFor="recapServiceLabel">Service terrain / récupération</Label>
                  <Input
                    id="recapServiceLabel"
                    value={processData.recapServiceLabel}
                    onChange={(e) => setProcessData(prev => ({ ...prev, recapServiceLabel: e.target.value }))}
                    placeholder="Ex: Service technique, régie logistique, agent récupérateur"
                  />
                </div>

                <div className="space-y-2">
                  <Label htmlFor="recapServiceDirectory">Répertoire du service terrain</Label>
                  <Select
                    value={processData.recapServiceId}
                    onValueChange={applyOperationalRecapServiceToProcessData}
                  >
                    <SelectTrigger id="recapServiceDirectory">
                      <SelectValue placeholder="Sélectionner un service enregistré" />
                    </SelectTrigger>
                    <SelectContent>
                      {terrainOperationalRecapServices
                        .filter((service) => service.actif)
                        .map((service) => (
                          <SelectItem key={service.id} value={String(service.id)}>
                            {service.label}
                          </SelectItem>
                        ))}
                    </SelectContent>
                  </Select>
                  <p className="text-xs text-muted-foreground">
                    Sélectionne un service pour préremplir automatiquement le terrain et ses destinataires.
                  </p>
                </div>

                <div className="space-y-2">
                  <Label htmlFor="recapRecipients">Destinataires terrain</Label>
                  <Textarea
                    id="recapRecipients"
                    value={processData.recapRecipients}
                    onChange={(e) => setProcessData(prev => ({ ...prev, recapRecipients: e.target.value }))}
                    placeholder="email1@service.fr, email2@service.fr"
                    rows={3}
                  />
                  <p className="text-xs text-muted-foreground">
                    Sépare les adresses par virgule, point-virgule ou retour à la ligne.
                  </p>
                </div>

                <div className="space-y-2">
                  <Label htmlFor="supervisionServiceLabel">Service interne de contrôle</Label>
                  <Input
                    id="supervisionServiceLabel"
                    value={processData.supervisionServiceLabel}
                    onChange={(e) => setProcessData(prev => ({ ...prev, supervisionServiceLabel: e.target.value }))}
                    placeholder="Ex: DSU, cellule de suivi, secrétariat général"
                  />
                </div>

                <div className="space-y-2">
                  <Label htmlFor="supervisionServiceDirectory">Répertoire du contrôle interne</Label>
                  <Select
                    value={processData.supervisionServiceId}
                    onValueChange={applyOperationalSupervisionServiceToProcessData}
                  >
                    <SelectTrigger id="supervisionServiceDirectory">
                      <SelectValue placeholder="Sélectionner un service enregistré" />
                    </SelectTrigger>
                    <SelectContent>
                      {controlOperationalRecapServices
                        .filter((service) => service.actif)
                        .map((service) => (
                          <SelectItem key={service.id} value={String(service.id)}>
                            {service.label}
                          </SelectItem>
                        ))}
                    </SelectContent>
                  </Select>
                  <p className="text-xs text-muted-foreground">
                    Ce service reçoit les confirmations, retards et alertes de restitution.
                  </p>
                </div>

                <div className="space-y-2">
                  <Label htmlFor="supervisionRecipients">Destinataires du contrôle interne</Label>
                  <Textarea
                    id="supervisionRecipients"
                    value={processData.supervisionRecipients}
                    onChange={(e) => setProcessData(prev => ({ ...prev, supervisionRecipients: e.target.value }))}
                    placeholder="suivi@collectivite.fr, direction@collectivite.fr"
                    rows={3}
                  />
                  <p className="text-xs text-muted-foreground">
                    Sépare les adresses par virgule, point-virgule ou retour à la ligne.
                  </p>
                </div>
              </>
            )}
          </div>
          <DialogFooter className={cn("border-t bg-background px-4 py-4 sm:px-6", isMobile && "sticky bottom-0 z-10 flex-col gap-2 bg-background/98 backdrop-blur supports-[backdrop-filter]:backdrop-blur")}>
            <Button variant="outline" onClick={() => setSelectedRequest(null)} className="w-full sm:w-auto">
              Annuler
            </Button>
            {selectedRequestNeedsFinancialDecision && processData.status === 'validee' && !selectedRequestIsSalle && (
              <Button variant="outline" onClick={() => handleProcess(true)} disabled={processMutation.isPending} className="w-full sm:w-auto">
                {processMutation.isPending ? (
                  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                ) : (
                  <FileText className="mr-2 h-4 w-4" />
                )}
                Valider et générer la convention
              </Button>
            )}
            <Button onClick={() => handleProcess(false)} disabled={processMutation.isPending} className="w-full sm:w-auto">
              {processMutation.isPending ? (
                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
              ) : processData.status === 'validee' ? (
                <CheckCircle className="mr-2 h-4 w-4" />
              ) : processData.status === 'refusee' ? (
                <XCircle className="mr-2 h-4 w-4" />
              ) : null}
              {selectedRequestIsSalle && processData.status === 'validee' ? 'Valider et envoyer le devis' : 'Confirmer'}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Association Documents Dialog */}
      <Dialog open={!!selectedAssociation} onOpenChange={() => setSelectedAssociation(null)}>
        <DialogContent className="max-w-2xl max-h-[80vh]">
          <DialogHeader>
            <DialogTitle>Documents de l'association</DialogTitle>
            <DialogDescription>
              {selectedAssociation?.nomAssociation}
            </DialogDescription>
          </DialogHeader>
          <ScrollArea className="h-[400px] pr-4">
            {documentsLoading ? (
              <div className="flex justify-center py-8">
                <Loader2 className="h-6 w-6 animate-spin" />
              </div>
            ) : associationDocuments && associationDocuments.length > 0 ? (
              <div className="space-y-3">
                {associationDocuments.map((doc: any) => (
                  <DocumentItem key={doc.id} document={doc} />
                ))}
              </div>
            ) : (
              <div className="text-center py-12 text-muted-foreground">
                <FileText className="h-12 w-12 mx-auto mb-4 opacity-50" />
                <p>Aucun document téléversé par cette association</p>
              </div>
            )}
          </ScrollArea>
          <DialogFooter>
            <Button variant="outline" onClick={() => setSelectedAssociation(null)}>
              Fermer
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog
        open={!!editingOperationalRecapService}
        onOpenChange={(open) => {
          if (!open) {
            setEditingOperationalRecapService(null);
          }
        }}
      >
        <DialogContent>
          <DialogHeader>
            <DialogTitle>
              {editingOperationalRecapService?.id ? "Modifier le service interne" : "Nouveau service interne"}
            </DialogTitle>
            <DialogDescription>
              Prépare une liste réutilisable de destinataires pour la récupération ou le contrôle du matériel CCDS.
            </DialogDescription>
          </DialogHeader>
          {editingOperationalRecapService && (
            <div className="space-y-4 py-2">
              <div className="space-y-2">
                <Label htmlFor="service-usage">Type de service</Label>
                <Select
                  value={editingOperationalRecapService.usage}
                  onValueChange={(value: "terrain" | "controle") =>
                    setEditingOperationalRecapService((prev) => prev ? { ...prev, usage: value } : prev)
                  }
                >
                  <SelectTrigger id="service-usage">
                    <SelectValue placeholder="Choisir le type de service" />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="terrain">Service terrain / récupération</SelectItem>
                    <SelectItem value="controle">Service interne de contrôle</SelectItem>
                  </SelectContent>
                </Select>
              </div>
              <div className="space-y-2">
                <Label htmlFor="service-label">Nom du service</Label>
                <Input
                  id="service-label"
                  value={editingOperationalRecapService.label}
                  onChange={(e) => setEditingOperationalRecapService((prev) => prev ? { ...prev, label: e.target.value } : prev)}
                  placeholder={
                    editingOperationalRecapService.usage === "controle"
                      ? "Ex. Cellule de contrôle matériel, secrétariat de suivi"
                      : "Ex. Régie logistique, service technique, agent récupérateur"
                  }
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="service-description">Description</Label>
                <Textarea
                  id="service-description"
                  value={editingOperationalRecapService.description}
                  onChange={(e) => setEditingOperationalRecapService((prev) => prev ? { ...prev, description: e.target.value } : prev)}
                  placeholder={
                    editingOperationalRecapService.usage === "controle"
                      ? "À utiliser pour recevoir les confirmations, retards et alertes de retour."
                      : "À utiliser pour envoyer la mission de récupération terrain."
                  }
                  rows={3}
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="service-recipients">Destinataires</Label>
                <Textarea
                  id="service-recipients"
                  value={editingOperationalRecapService.recipientEmails}
                  onChange={(e) => setEditingOperationalRecapService((prev) => prev ? { ...prev, recipientEmails: e.target.value } : prev)}
                  placeholder="email1@collectivite.fr, email2@collectivite.fr"
                  rows={4}
                />
                <p className="text-xs text-muted-foreground">
                  Sépare les adresses par virgule, point-virgule ou retour à la ligne.
                </p>
              </div>
              <div className="flex items-center justify-between rounded-lg border p-3">
                <div>
                  <p className="font-medium">Service actif</p>
                  <p className="text-sm text-muted-foreground">
                    Les services inactifs restent en archive mais ne sont plus proposés pendant le traitement.
                  </p>
                </div>
                <Switch
                  checked={editingOperationalRecapService.actif}
                  onCheckedChange={(checked) => setEditingOperationalRecapService((prev) => prev ? { ...prev, actif: checked } : prev)}
                />
              </div>
            </div>
          )}
          <DialogFooter>
            <Button variant="outline" onClick={() => setEditingOperationalRecapService(null)}>
              Annuler
            </Button>
            <Button
              onClick={handleSaveOperationalRecapService}
              disabled={createOperationalRecapServiceMutation.isPending || updateOperationalRecapServiceMutation.isPending}
            >
              {(createOperationalRecapServiceMutation.isPending || updateOperationalRecapServiceMutation.isPending) && (
                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
              )}
              Enregistrer
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog
        open={!!editingAdminUser}
        onOpenChange={(open) => {
          if (!open) {
            setEditingAdminUser(null);
          }
        }}
      >
        <DialogContent>
          <DialogHeader>
            <DialogTitle>
              {editingAdminUser?.id ? "Modifier le compte interne" : "Créer un compte interne"}
            </DialogTitle>
            <DialogDescription>
              {editingAdminUser?.id
                ? "Gère les accès internes du portail, y compris les rôles d’administration et de direction."
                : "Crée un compte interne sans saisie manuelle de mot de passe. Les accès techniques sont préparés automatiquement par le portail."}
            </DialogDescription>
          </DialogHeader>
          {editingAdminUser && (
            <div className="space-y-4 py-2">
              <div className="space-y-2">
                <Label htmlFor="admin-user-name">Nom affiché</Label>
                <Input
                  id="admin-user-name"
                  value={editingAdminUser.name}
                  onChange={(e) => setEditingAdminUser((prev) => prev ? { ...prev, name: e.target.value } : prev)}
                  placeholder="Ex. Service vie associative"
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="admin-user-email">Adresse email</Label>
                <Input
                  id="admin-user-email"
                  type="email"
                  value={editingAdminUser.email}
                  onChange={(e) => setEditingAdminUser((prev) => prev ? { ...prev, email: e.target.value } : prev)}
                  placeholder="admin@portail-association973.com"
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="admin-user-role">Rôle</Label>
                <Select
                  value={editingAdminUser.role}
                  onValueChange={(value: "user" | "accueil" | "service_terrain" | "logistique_controle" | "admin" | "directrice" | "super_admin") =>
                    setEditingAdminUser((prev) =>
                      prev
                        ? {
                            ...prev,
                            role: value,
                            canManageLogistics:
                              value === "super_admin" || value === "logistique_controle"
                                ? true
                                : value === "admin"
                                  ? prev.canManageLogistics
                                  : false,
                          }
                        : prev
                    )
                  }
                >
                  <SelectTrigger id="admin-user-role">
                    <SelectValue placeholder="Choisir un rôle" />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="user">{internalRoleLabels.user}</SelectItem>
                    <SelectItem value="accueil">{internalRoleLabels.accueil}</SelectItem>
                    <SelectItem value="service_terrain">{internalRoleLabels.service_terrain}</SelectItem>
                    <SelectItem value="logistique_controle">{internalRoleLabels.logistique_controle}</SelectItem>
                    <SelectItem value="admin">{internalRoleLabels.admin}</SelectItem>
                    <SelectItem value="directrice">{internalRoleLabels.directrice}</SelectItem>
                    <SelectItem value="super_admin">Super administrateur</SelectItem>
                  </SelectContent>
                </Select>
              </div>
              <div className="rounded-lg border bg-muted/20 p-3">
                <p className="font-medium">
                  {internalRoleLabels[editingAdminUser.role]}
                </p>
                <p className="text-sm text-muted-foreground">
                  {internalRoleDescriptions[editingAdminUser.role]}
                </p>
              </div>
              <div className="rounded-lg border p-3">
                <p className="font-medium">Droits logistique & contrôle</p>
                <p className="text-sm text-muted-foreground">
                  Ce droit limité permet de traiter les demandes de matériel CCDS, piloter les restitutions et contrôler les retours. Le Super Administrateur désigne les membres, mais n’est pas rattaché à ce groupe opérationnel.
                </p>
              </div>
              {editingAdminUser.id && (
                <div className="space-y-2">
                  <Label htmlFor="admin-user-password">Nouveau mot de passe (optionnel)</Label>
                  <Input
                    id="admin-user-password"
                    type="password"
                    value={editingAdminUser.password}
                    onChange={(e) => setEditingAdminUser((prev) => prev ? { ...prev, password: e.target.value } : prev)}
                    placeholder="Laisser vide pour conserver l’actuel"
                  />
                </div>
              )}
              {editingAdminUser.id && (
                <div className="flex items-center justify-between rounded-lg border p-3">
                  <div>
                    <p className="font-medium">Compte actif</p>
                    <p className="text-sm text-muted-foreground">
                      Un compte désactivé ne peut plus se connecter au portail.
                    </p>
                  </div>
                  <Switch
                    checked={editingAdminUser.isActive}
                    onCheckedChange={(checked) => setEditingAdminUser((prev) => prev ? { ...prev, isActive: checked } : prev)}
                  />
                </div>
              )}
            </div>
          )}
          <DialogFooter>
            <Button variant="outline" onClick={() => setEditingAdminUser(null)}>
              Annuler
            </Button>
            <Button
              onClick={handleSaveAdminUser}
              disabled={createAdminUserMutation.isPending || updateAdminUserMutation.isPending}
            >
              {(createAdminUserMutation.isPending || updateAdminUserMutation.isPending) && (
                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
              )}
              Enregistrer
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}

// Document item component for admin view
function DocumentItem({ document }: { document: any }) {
  const [isLoadingView, setIsLoadingView] = useState(false);
  const [isLoadingDownload, setIsLoadingDownload] = useState(false);
  const [showPreview, setShowPreview] = useState(false);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  
  const { refetch } = trpc.document.getDownloadUrl.useQuery(
    { id: document.id },
    { enabled: false }
  );

  const handleView = async () => {
    setIsLoadingView(true);
    try {
      const result = await refetch();
      if (result.data?.url) {
        setPreviewUrl(result.data.url);
        setShowPreview(true);
      }
    } finally {
      setIsLoadingView(false);
    }
  };

  const handleDownload = async () => {
    setIsLoadingDownload(true);
    try {
      const result = await refetch();
      if (result.data?.url) {
        // Create a temporary link to force download
        const link = window.document.createElement('a');
        link.href = result.data.url;
        link.download = document.nom || 'document';
        link.target = '_blank';
        window.document.body.appendChild(link);
        link.click();
        window.document.body.removeChild(link);
      }
    } finally {
      setIsLoadingDownload(false);
    }
  };

  const documentTypeLabels: Record<string, string> = {
    statuts: 'Statuts',
    recepisse_declaration: 'Récépissé de déclaration',
    rib: 'RIB',
    rapport_activite: 'Rapport d\'activité',
    rapport_financier: 'Rapport financier',
    pv_assemblee: 'PV d\'assemblée',
    liste_dirigeants: 'Liste des dirigeants',
    attestation_assurance: 'Attestation d\'assurance',
    autre: 'Autre',
  };

  const formatFileSize = (bytes: number | null) => {
    if (!bytes) return '';
    if (bytes < 1024) return `${bytes} o`;
    if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} Ko`;
    return `${(bytes / (1024 * 1024)).toFixed(1)} Mo`;
  };

  // Check if the file can be previewed in browser
  const canPreview = document.mimeType && (
    document.mimeType.startsWith('image/') ||
    document.mimeType === 'application/pdf'
  );

  return (
    <>
      <div className="flex items-center justify-between p-3 border rounded-lg hover:bg-muted/50 transition-colors">
        <div className="flex items-center gap-3">
          <div className="h-10 w-10 rounded-lg bg-primary/10 flex items-center justify-center">
            <FileText className="h-5 w-5 text-primary" />
          </div>
          <div>
            <p className="font-medium">{document.nom}</p>
            <div className="flex items-center gap-2 text-sm text-muted-foreground">
              <Badge variant="outline" className="text-xs">
                {documentTypeLabels[document.type] || document.type}
              </Badge>
              {document.fileSize && (
                <span>{formatFileSize(document.fileSize)}</span>
              )}
            </div>
          </div>
        </div>
        <div className="flex items-center gap-1">
          <Button
            size="sm"
            variant="ghost"
            onClick={handleView}
            disabled={isLoadingView}
            title="Afficher"
          >
            {isLoadingView ? (
              <Loader2 className="h-4 w-4 animate-spin" />
            ) : (
              <Eye className="h-4 w-4" />
            )}
          </Button>
          <Button
            size="sm"
            variant="ghost"
            onClick={handleDownload}
            disabled={isLoadingDownload}
            title="Télécharger"
          >
            {isLoadingDownload ? (
              <Loader2 className="h-4 w-4 animate-spin" />
            ) : (
              <Download className="h-4 w-4" />
            )}
          </Button>
        </div>
      </div>

      {/* Preview Dialog */}
      <Dialog open={showPreview} onOpenChange={setShowPreview}>
        <DialogContent className="max-w-4xl max-h-[90vh]">
          <DialogHeader>
            <DialogTitle>{document.nom}</DialogTitle>
            <DialogDescription>
              {documentTypeLabels[document.type] || document.type}
            </DialogDescription>
          </DialogHeader>
          <div className="flex-1 overflow-auto">
            {previewUrl && (
              canPreview ? (
                document.mimeType?.startsWith('image/') ? (
                  <img 
                    src={previewUrl} 
                    alt={document.nom} 
                    className="max-w-full h-auto mx-auto rounded-lg"
                  />
                ) : (
                  <iframe
                    src={previewUrl}
                    className="w-full h-[70vh] border-0 rounded-lg"
                    title={document.nom}
                  />
                )
              ) : (
                <div className="text-center py-12">
                  <FileText className="h-16 w-16 mx-auto text-muted-foreground mb-4" />
                  <p className="text-muted-foreground mb-4">
                    Ce type de fichier ne peut pas être affiché directement.
                  </p>
                  <Button onClick={handleDownload}>
                    <Download className="mr-2 h-4 w-4" />
                    Télécharger le fichier
                  </Button>
                </div>
              )
            )}
          </div>
          <DialogFooter>
            <Button variant="outline" onClick={() => setShowPreview(false)}>
              Fermer
            </Button>
            <Button onClick={handleDownload}>
              <Download className="mr-2 h-4 w-4" />
              Télécharger
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  );
}
