import { useEffect, useMemo, useRef, useState } from "react";
import { createFileRoute, useNavigate, Link } from "@tanstack/react-router";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { z } from "zod";
import {
  ArrowLeft,
  ArrowRight,
  AlertTriangle,
  Sparkles,
  Check,
  Pencil,
} from "lucide-react";
import { toast } from "sonner";

import type { Client, Plan, PlanStep, RelanceTone } from "@rubis/shared";
import { api } from "@/lib/api";
import { queryKeys } from "@/lib/queryKeys";
import { TONE_LABELS, TEMPLATE_VARIABLES, PREVIEW_VARS } from "@/lib/plans";
import { cn } from "@/lib/utils";

import { Stepper } from "@/components/ui/Stepper";
import { Card } from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";
import { Field } from "@/components/ui/Field";
import { Input } from "@/components/ui/Input";
import { Textarea } from "@/components/ui/Textarea";
import { Eyebrow } from "@/components/ui/Eyebrow";
import { CadenceCalendar } from "@/components/plans/wizard/CadenceCalendar";
import { EmailPreview } from "@/components/plans/wizard/EmailPreview";
import { AiGenerateModal } from "@/components/plans/wizard/AiGenerateModal";

type WizardStep = 1 | 2 | 3 | 4;

type DraftStep = {
  offsetDays: number;
  tone: RelanceTone;
  subject: string;
  body: string;
  requiresManualValidation: boolean;
};

type Draft = {
  name: string;
  description: string;
  /** Tonalité globale choisie à l'étape 1 — sert de défaut pour les nouvelles étapes. */
  globalTone: RelanceTone;
  steps: DraftStep[];
};

const TONALITY_CARDS: Array<{
  tone: RelanceTone;
  title: string;
  helper: string;
  defaultCadence: { offsetDays: number; tone: RelanceTone }[];
}> = [
  {
    tone: "amical",
    title: "Doux",
    helper: "Pour les clients de longue date — on prend des gants.",
    defaultCadence: [
      { offsetDays: 5, tone: "amical" },
      { offsetDays: 20, tone: "amical" },
      { offsetDays: 45, tone: "courtois" },
    ],
  },
  {
    tone: "courtois",
    title: "Standard",
    helper: "Cadence sobre, ton qui monte progressivement. Le défaut B2B.",
    defaultCadence: [
      { offsetDays: 3, tone: "amical" },
      { offsetDays: 10, tone: "courtois" },
      { offsetDays: 25, tone: "ferme" },
    ],
  },
  {
    tone: "ferme",
    title: "Ferme",
    helper: "Cadence resserrée pour les retards récurrents.",
    defaultCadence: [
      { offsetDays: 1, tone: "courtois" },
      { offsetDays: 7, tone: "ferme" },
      { offsetDays: 15, tone: "ferme" },
    ],
  },
  {
    tone: "mise_en_demeure",
    title: "Strict",
    helper: "Pour les clients à risque, mise en demeure rapide.",
    defaultCadence: [
      { offsetDays: 1, tone: "courtois" },
      { offsetDays: 5, tone: "ferme" },
      { offsetDays: 10, tone: "mise_en_demeure" },
    ],
  },
];

function emptyStep(tone: RelanceTone, offsetDays: number): DraftStep {
  return {
    offsetDays,
    tone,
    subject: "",
    body: "",
    // Mise en demeure → validation manuelle obligatoire (cf. CLAUDE.md §4).
    requiresManualValidation: tone === "mise_en_demeure",
  };
}

const STEPS = [
  { id: "identity", label: "Identité" },
  { id: "cadence", label: "Cadence" },
  { id: "messages", label: "Messages" },
  { id: "recap", label: "Récap" },
] as const;

export const Route = createFileRoute("/_app/plans_/nouveau")({
  component: PlanCreateWizard,
  validateSearch: z.object({
    /** Slug d'un plan existant à dupliquer pour pré-remplir le wizard. */
    from: z.string().optional(),
  }),
});

function PlanCreateWizard() {
  const navigate = useNavigate();
  const { from } = Route.useSearch();
  const queryClient = useQueryClient();
  const [step, setStep] = useState<WizardStep>(1);

  // Si on duplique, on charge le plan source pour pré-remplir le draft.
  const { data: sourcePlan } = useQuery({
    queryKey: queryKeys.plans.detail(from ?? ""),
    queryFn: () => api.get<Plan>(`/api/v1/plans/${from}`),
    enabled: !!from,
  });

  // Liste des clients pour le warning "X clients sans prénom" si on utilise
  // {{client.contactFirstName}} dans un template.
  const { data: clients = [] } = useQuery({
    queryKey: queryKeys.clients.all(),
    queryFn: () => api.get<Client[]>("/api/v1/clients"),
  });

  const [draft, setDraft] = useState<Draft>(() => ({
    name: "",
    description: "",
    globalTone: "courtois",
    steps: [],
  }));

  // Quand le plan source est chargé (mode duplication), on injecte ses
  // étapes dans le draft. Une seule fois — l'utilisateur peut ensuite
  // modifier librement.
  const seededRef = useRef(false);
  useEffect(() => {
    if (!sourcePlan || seededRef.current) return;
    seededRef.current = true;
    setDraft({
      name: `${sourcePlan.name} (copie)`,
      description: sourcePlan.description,
      globalTone: lastTone(sourcePlan.steps) ?? "courtois",
      steps: sourcePlan.steps.map((s) => ({
        offsetDays: s.offsetDays,
        tone: s.tone,
        subject: s.subject,
        body: s.body,
        requiresManualValidation: s.requiresManualValidation,
      })),
    });
  }, [sourcePlan]);

  const createMutation = useMutation({
    mutationFn: (payload: { name: string; description: string; steps: DraftStep[] }) =>
      api.post<Plan>("/api/v1/plans", {
        name: payload.name,
        description: payload.description,
        steps: payload.steps.map((s, idx) => ({
          order: idx,
          offsetDays: s.offsetDays,
          tone: s.tone,
          subject: s.subject,
          body: s.body,
          requiresManualValidation: s.requiresManualValidation,
        })),
      }),
    onSuccess: (created) => {
      void queryClient.invalidateQueries({ queryKey: queryKeys.plans.all() });
      toast.success(`Plan « ${created.name} » créé.`);
      void navigate({
        to: "/plans/$slug",
        params: { slug: created.slug ?? "" },
      });
    },
    onError: () => {
      toast.error("Création impossible. Vérifiez les champs obligatoires.");
    },
  });

  const canProceed = stepCanProceed(step, draft);

  return (
    <div className="flex flex-col gap-5 pb-[env(safe-area-inset-bottom)]">
      {/* Header — sur mobile, on cache le label "Plans" : juste l'icône
          + compteur d'étape. Stepper centré, plus compact. */}
      <div className="flex items-center justify-between gap-3">
        <Button asChild variant="ghost" size="sm" className="px-2">
          <Link to="/plans" aria-label="Retour aux plans">
            <ArrowLeft size={16} />
            <span className="hidden sm:inline">Plans</span>
          </Link>
        </Button>
        <Stepper
          steps={[...STEPS]}
          currentIndex={step - 1}
          className="flex-1 max-w-md min-w-0"
        />
        <span className="text-[12px] text-ink-3 tabular-nums shrink-0">
          {step}/4
        </span>
      </div>

      <Card padding="none" className="p-5 sm:p-7 lg:p-9 overflow-visible">
        {step === 1 && <StepIdentity draft={draft} onChange={setDraft} />}
        {step === 2 && <StepCadence draft={draft} onChange={setDraft} />}
        {step === 3 && (
          <StepMessages draft={draft} onChange={setDraft} clients={clients} />
        )}
        {step === 4 && <StepRecap draft={draft} />}
      </Card>

      {/* Bottom nav — sticky en bas sur mobile pour rester accessible quand
          le formulaire est long, normal en flux sur desktop. */}
      <div
        className={cn(
          "sticky bottom-0 z-10 -mx-4 px-4 py-3 sm:mx-0 sm:px-0 sm:py-0 sm:static",
          "bg-cream/95 backdrop-blur-sm border-t border-line sm:bg-transparent sm:border-0 sm:backdrop-blur-none",
          "flex items-center justify-between gap-3",
        )}
        style={{ paddingBottom: "max(0.75rem, env(safe-area-inset-bottom))" }}
      >
        <Button
          variant="ghost"
          size="md"
          onClick={() => {
            if (step === 1) void navigate({ to: "/plans" });
            else setStep((s) => (s - 1) as WizardStep);
          }}
        >
          <ArrowLeft size={14} /> {step === 1 ? "Annuler" : "Retour"}
        </Button>
        {step < 4 ? (
          <Button
            size="md"
            disabled={!canProceed}
            onClick={() => setStep((s) => (s + 1) as WizardStep)}
          >
            Continuer <ArrowRight size={14} />
          </Button>
        ) : (
          <Button
            size="md"
            loading={createMutation.isPending}
            disabled={!canProceed}
            onClick={() =>
              createMutation.mutate({
                name: draft.name,
                description: draft.description,
                steps: draft.steps,
              })
            }
          >
            Créer le plan
          </Button>
        )}
      </div>
    </div>
  );
}

// ============================================================================
// Step 1 — Identité (nom + tonalité globale)
// ============================================================================
function StepIdentity({ draft, onChange }: { draft: Draft; onChange: (d: Draft) => void }) {
  return (
    <div className="flex flex-col gap-7">
      <div>
        <Eyebrow>Étape 1 sur 4</Eyebrow>
        <h2 className="font-display text-[22px] font-bold tracking-[-0.02em] text-ink mt-2">
          Donnez une identité à votre plan
        </h2>
        <p className="mt-1.5 text-[14px] text-ink-3 leading-relaxed max-w-xl">
          Choisissez un nom clair et une tonalité globale. La tonalité oriente la
          cadence par défaut — vous pourrez tout ajuster ensuite.
        </p>
      </div>

      <Field
        label="Nom du plan"
        htmlFor="plan-name"
        hint="Visible uniquement dans votre bibliothèque, jamais envoyé."
      >
        <Input
          id="plan-name"
          autoFocus
          maxLength={80}
          placeholder="Ex. Clients fidèles, Nouveaux clients, Sociétés à risque…"
          value={draft.name}
          onChange={(e) => onChange({ ...draft, name: e.target.value })}
        />
      </Field>

      <Field
        label="Description"
        htmlFor="plan-description"
        hint="Optionnel. Un rappel à vous-même de quand utiliser ce plan."
      >
        <Textarea
          id="plan-description"
          rows={2}
          maxLength={500}
          placeholder="Ex. Pour les nouveaux clients sans historique de paiement."
          value={draft.description}
          onChange={(e) => onChange({ ...draft, description: e.target.value })}
        />
      </Field>

      <div>
        <p className="font-sans text-[13px] font-semibold text-ink mb-2">
          Tonalité globale
        </p>
        <p className="text-[12.5px] text-ink-3 mb-4 leading-snug">
          On s'en sert pour pré-remplir la cadence à l'étape suivante. Vous
          pourrez retoucher chaque étape individuellement.
        </p>
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
          {TONALITY_CARDS.map((card) => {
            const selected = draft.globalTone === card.tone;
            return (
              <button
                key={card.tone}
                type="button"
                onClick={() => onChange({ ...draft, globalTone: card.tone })}
                className={cn(
                  "text-left rounded-card border p-4 transition-all",
                  "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-rubis-glow",
                  selected
                    ? "border-rubis bg-rubis-glow/40 shadow-rubis"
                    : "border-line bg-white hover:border-ink-3",
                )}
              >
                <div className="flex items-start justify-between gap-3">
                  <div>
                    <p className="font-display text-[15px] font-semibold text-ink">
                      {card.title}
                    </p>
                    <p className="text-[12.5px] text-ink-3 mt-1 leading-snug">
                      {card.helper}
                    </p>
                  </div>
                  <span
                    aria-hidden="true"
                    className={cn(
                      "shrink-0 size-5 rounded-full border-2 flex items-center justify-center",
                      selected ? "bg-rubis border-rubis text-white" : "border-line bg-white",
                    )}
                  >
                    {selected && <Check size={11} strokeWidth={3} />}
                  </span>
                </div>
                <div className="mt-3 flex items-center gap-1.5 text-[11px] tabular-nums text-ink-3">
                  {card.defaultCadence.map((s, i) => (
                    <span key={i} className="inline-flex items-center gap-1">
                      <span
                        className={cn(
                          "size-2 rotate-45",
                          s.tone === "amical" && "bg-rubis-glow",
                          s.tone === "courtois" && "bg-cream-2",
                          s.tone === "ferme" && "bg-ink",
                          s.tone === "mise_en_demeure" && "bg-rubis-deep",
                        )}
                      />
                      <span>
                        J{s.offsetDays >= 0 ? "+" : ""}
                        {s.offsetDays}
                      </span>
                      {i < card.defaultCadence.length - 1 && (
                        <span className="text-ink-3">→</span>
                      )}
                    </span>
                  ))}
                </div>
              </button>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ============================================================================
// Step 2 — Cadence (timeline avec ajout/retrait/édition)
// ============================================================================
function StepCadence({ draft, onChange }: { draft: Draft; onChange: (d: Draft) => void }) {
  // Si pas encore d'étapes, on injecte la cadence par défaut de la tonalité.
  useEffect(() => {
    if (draft.steps.length > 0) return;
    const defaults = TONALITY_CARDS.find((c) => c.tone === draft.globalTone)
      ?.defaultCadence;
    if (!defaults) return;
    onChange({
      ...draft,
      steps: defaults.map((s) => emptyStep(s.tone, s.offsetDays)),
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [draft.globalTone]);

  // Pas de présélection : l'utilisateur choisit en touchant une case.
  // Évite la confusion "Étape 1 affichée par défaut alors que je n'ai
  // rien cliqué".
  const [selectedIdx, setSelectedIdx] = useState(-1);

  const updateStep = (idx: number, patch: Partial<DraftStep>) => {
    onChange({
      ...draft,
      steps: draft.steps.map((s, i) => (i === idx ? { ...s, ...patch } : s)),
    });
  };

  const addStep = () => {
    const last = draft.steps[draft.steps.length - 1];
    const offsetDays = (last?.offsetDays ?? 0) + 7;
    onChange({
      ...draft,
      steps: [...draft.steps, emptyStep(draft.globalTone, offsetDays)],
    });
    setSelectedIdx(draft.steps.length);
  };

  // Création directe par clic sur une case vide du calendrier.
  // L'étape est insérée triée par offset croissant pour que l'ordre
  // visuel suive l'ordre temporel.
  const addStepAtOffset = (offsetDays: number) => {
    const newStep = emptyStep(draft.globalTone, offsetDays);
    const merged = [...draft.steps, newStep].sort(
      (a, b) => a.offsetDays - b.offsetDays,
    );
    const newIdx = merged.findIndex(
      (s) => s.offsetDays === offsetDays && s === newStep,
    );
    onChange({ ...draft, steps: merged });
    setSelectedIdx(newIdx);
  };

  const removeStep = (idx: number) => {
    if (draft.steps.length <= 1) return;
    onChange({ ...draft, steps: draft.steps.filter((_, i) => i !== idx) });
    setSelectedIdx(-1);
  };

  return (
    <div className="flex flex-col gap-6">
      <div>
        <Eyebrow>Étape 2 sur 4</Eyebrow>
        <h2 className="font-display text-[22px] font-bold tracking-[-0.02em] text-ink mt-2">
          Réglez la cadence
        </h2>
        <p className="mt-1.5 text-[14px] text-ink-3 leading-relaxed max-w-xl">
          Touchez une <strong>case colorée</strong> pour modifier une relance,
          ou un <strong>jour vide</strong> pour en ajouter une. Le ◆ rouge =
          échéance fictive de la facture (juste pour visualiser le timing).
        </p>
      </div>

      <CadenceCalendar
        steps={draft.steps}
        selectedIndex={selectedIdx}
        // Toggle : re-cliquer une étape déjà sélectionnée la désélectionne
        // pour revenir à la vue d'ensemble.
        onSelectStep={(idx) =>
          setSelectedIdx((cur) => (cur === idx ? -1 : idx))
        }
        onUpdateStep={updateStep}
        onAddStep={addStep}
        onAddStepAtOffset={addStepAtOffset}
        onRemoveStep={removeStep}
      />
    </div>
  );
}

// ============================================================================
// Step 3 — Messages (édition par étape, manuel ou IA)
// ============================================================================
function StepMessages({
  draft,
  onChange,
  clients,
}: {
  draft: Draft;
  onChange: (d: Draft) => void;
  clients: Client[];
}) {
  const [selectedIdx, setSelectedIdx] = useState(0);
  const [aiOpen, setAiOpen] = useState(false);
  const subjectRef = useRef<HTMLInputElement | null>(null);
  const bodyRef = useRef<HTMLTextAreaElement | null>(null);
  // Quel champ a été focus en dernier — détermine où on insère une variable
  // quand l'utilisateur clique sur un chip. Capturé sur focus ; on capture
  // aussi la position du curseur sur blur pour pouvoir insérer même après
  // que le clic sur le chip ait blur le champ.
  const [focusedField, setFocusedField] = useState<"subject" | "body">("body");
  const subjectSelRef = useRef<{ start: number; end: number }>({ start: 0, end: 0 });
  const bodySelRef = useRef<{ start: number; end: number }>({ start: 0, end: 0 });
  const selected = draft.steps[selectedIdx];

  const updateSelected = (patch: Partial<DraftStep>) => {
    onChange({
      ...draft,
      steps: draft.steps.map((s, i) => (i === selectedIdx ? { ...s, ...patch } : s)),
    });
  };

  // Détection des variables utilisées qui exigent un champ client → warning
  // si certains clients n'ont pas le champ rempli.
  const warnings = useMemo(
    () => computeClientWarnings(draft.steps, clients),
    [draft.steps, clients],
  );

  const captureSubjectSelection = () => {
    const el = subjectRef.current;
    if (el) subjectSelRef.current = { start: el.selectionStart ?? el.value.length, end: el.selectionEnd ?? el.value.length };
  };
  const captureBodySelection = () => {
    const el = bodyRef.current;
    if (el) bodySelRef.current = { start: el.selectionStart, end: el.selectionEnd };
  };

  const insertVariable = (token: string) => {
    if (!selected) return;
    if (focusedField === "subject") {
      const el = subjectRef.current;
      const current = selected.subject;
      const { start, end } = subjectSelRef.current;
      const insertAt = Math.min(Math.max(start, 0), current.length);
      const insertEnd = Math.min(Math.max(end, insertAt), current.length);
      const next = current.slice(0, insertAt) + token + current.slice(insertEnd);
      updateSelected({ subject: next.slice(0, 200) });
      requestAnimationFrame(() => {
        if (!el) return;
        el.focus();
        const caret = insertAt + token.length;
        el.setSelectionRange(caret, caret);
        subjectSelRef.current = { start: caret, end: caret };
      });
      return;
    }
    // body (défaut)
    const el = bodyRef.current;
    const current = selected.body;
    const { start, end } = bodySelRef.current;
    const insertAt = Math.min(Math.max(start, 0), current.length);
    const insertEnd = Math.min(Math.max(end, insertAt), current.length);
    const next = current.slice(0, insertAt) + token + current.slice(insertEnd);
    updateSelected({ body: next.slice(0, 5000) });
    requestAnimationFrame(() => {
      if (!el) return;
      el.focus();
      const caret = insertAt + token.length;
      el.setSelectionRange(caret, caret);
      bodySelRef.current = { start: caret, end: caret };
    });
  };

  return (
    <div className="flex flex-col gap-7">
      <div>
        <Eyebrow>Étape 3 sur 4</Eyebrow>
        <h2 className="font-display text-[22px] font-bold tracking-[-0.02em] text-ink mt-2">
          Rédigez vos messages
        </h2>
        <p className="mt-1.5 text-[14px] text-ink-3 leading-relaxed max-w-xl">
          Pour chaque étape, écrivez un sujet et un corps. Vous pouvez aussi
          demander à l'IA de proposer un brouillon.
        </p>
      </div>

      {/* Sélecteur d'étape — scroll horizontal sur mobile, wrap au-delà.
          Le -mx + px compense pour pouvoir scroller jusqu'au bord de la card. */}
      <div className="-mx-1 px-1 flex gap-2 overflow-x-auto sm:flex-wrap sm:overflow-visible scrollbar-thin">
        {draft.steps.map((s, idx) => {
          const filled = s.subject.trim() !== "" && s.body.trim() !== "";
          return (
            <button
              key={idx}
              type="button"
              onClick={() => setSelectedIdx(idx)}
              className={cn(
                "shrink-0 inline-flex items-center gap-2 rounded-default border px-3 py-1.5",
                "text-[12.5px] font-medium transition-all",
                idx === selectedIdx
                  ? "border-rubis bg-rubis-glow text-rubis-deep"
                  : "border-line bg-white text-ink-2 hover:border-ink-3",
              )}
            >
              <span className="tabular-nums">
                J{s.offsetDays >= 0 ? "+" : ""}
                {s.offsetDays}
              </span>
              <span className="text-ink-3">·</span>
              <span>{TONE_LABELS[s.tone]}</span>
              {filled ? (
                <Check size={11} className="text-rubis-deep" />
              ) : (
                <Pencil size={11} className="text-ink-3" />
              )}
            </button>
          );
        })}
      </div>

      {selected && (
        <div className="grid grid-cols-1 lg:grid-cols-[1fr,1fr] gap-6">
          <div className="flex flex-col gap-4">
            <div className="flex items-center justify-between gap-2">
              <p className="text-[11px] uppercase tracking-[0.1em] text-ink-3 font-semibold">
                Étape {selectedIdx + 1} · J{selected.offsetDays >= 0 ? "+" : ""}
                {selected.offsetDays}
              </p>
              <Button size="sm" variant="secondary" onClick={() => setAiOpen(true)}>
                <Sparkles size={13} className="text-rubis" /> Générer avec l'IA
              </Button>
            </div>

            <Field label="Sujet de l'email" htmlFor="step-subject">
              <Input
                id="step-subject"
                ref={subjectRef}
                placeholder="Ex. Petit rappel — facture {{numero}}"
                value={selected.subject}
                onChange={(e) => updateSelected({ subject: e.target.value })}
                onFocus={() => {
                  setFocusedField("subject");
                  captureSubjectSelection();
                }}
                onSelect={captureSubjectSelection}
                onBlur={captureSubjectSelection}
                onKeyUp={captureSubjectSelection}
                onClick={captureSubjectSelection}
                maxLength={200}
              />
            </Field>

            <Field label="Corps de l'email" htmlFor="step-body">
              <Textarea
                id="step-body"
                ref={bodyRef}
                rows={8}
                className="min-h-[180px] sm:min-h-[260px]"
                value={selected.body}
                onChange={(e) => updateSelected({ body: e.target.value })}
                onFocus={() => {
                  setFocusedField("body");
                  captureBodySelection();
                }}
                onSelect={captureBodySelection}
                onBlur={captureBodySelection}
                onKeyUp={captureBodySelection}
                onClick={captureBodySelection}
                placeholder="Bonjour {{client.contactFirstName}}, ..."
                maxLength={5000}
              />
            </Field>

            <div>
              <p className="text-[11.5px] text-ink-3 mb-2">
                Cliquez pour insérer une variable dans{" "}
                <strong className="text-ink-2">
                  {focusedField === "subject" ? "le sujet" : "le corps"}
                </strong>{" "}
                au curseur :
              </p>
              <div className="flex flex-wrap gap-1.5">
                {TEMPLATE_VARIABLES.map((v) => (
                  <button
                    key={v.token}
                    type="button"
                    // onMouseDown au lieu de onClick : le mousedown se déclenche
                    // AVANT le blur de l'input, donc subjectSelRef/bodySelRef
                    // contiennent encore la position correcte. preventDefault
                    // empêche le focus de quitter le champ visé.
                    onMouseDown={(e) => {
                      e.preventDefault();
                      insertVariable(v.token);
                    }}
                    className="inline-flex items-center rounded-full border border-line bg-cream-2/50 px-2.5 py-1 text-[11.5px] text-ink-2 hover:border-rubis hover:text-rubis-deep transition-colors"
                  >
                    {v.label}
                  </button>
                ))}
              </div>
            </div>

            {warnings.length > 0 && (
              <div className="rounded-default border border-rubis-glow bg-rubis-glow/30 p-3 flex gap-2 text-[12.5px] leading-snug text-ink-2">
                <AlertTriangle size={14} className="text-rubis-deep shrink-0 mt-0.5" />
                <div>
                  <p className="font-semibold text-rubis-deep mb-0.5">
                    Variables sensibles détectées
                  </p>
                  <ul className="list-disc pl-4 space-y-0.5">
                    {warnings.map((w) => (
                      <li key={w.field}>
                        <strong>{w.label}</strong> est utilisé mais{" "}
                        <strong>{w.missingCount}</strong> client
                        {w.missingCount > 1 ? "s n'ont" : " n'a"} pas ce champ
                        rempli. La variable sera remplacée par une chaîne vide
                        pour ces clients.
                      </li>
                    ))}
                  </ul>
                </div>
              </div>
            )}
          </div>

          {/* Preview live */}
          <div className="lg:sticky lg:top-6 lg:self-start">
            <EmailPreview subject={selected.subject} body={selected.body} />
          </div>
        </div>
      )}

      {selected && (
        <AiGenerateModal
          open={aiOpen}
          onOpenChange={setAiOpen}
          tone={selected.tone}
          offsetDays={selected.offsetDays}
          planName={draft.name}
          planDescription={draft.description}
          onAccept={({ subject, body }) => updateSelected({ subject, body })}
        />
      )}
    </div>
  );
}

// ============================================================================
// Step 4 — Récap (preview de chaque email + submit)
// ============================================================================
function StepRecap({ draft }: { draft: Draft }) {
  return (
    <div className="flex flex-col gap-7">
      <div>
        <Eyebrow>Étape 4 sur 4</Eyebrow>
        <h2 className="font-display text-[22px] font-bold tracking-[-0.02em] text-ink mt-2">
          Vérifiez et créez
        </h2>
        <p className="mt-1.5 text-[14px] text-ink-3 leading-relaxed max-w-xl">
          Aperçu de chaque email tel qu'il sera reçu par <em>{PREVIEW_VARS.client.contactFirstName} {PREVIEW_VARS.client.contactLastName}</em> ({PREVIEW_VARS.client.name}). Vos vrais clients verront leurs propres données.
        </p>
      </div>

      <div className="rounded-card border border-line bg-cream-2/30 p-4 flex flex-wrap items-center gap-x-6 gap-y-2 text-[13px]">
        <div>
          <span className="text-ink-3">Nom : </span>
          <strong className="text-ink">{draft.name || "(sans nom)"}</strong>
        </div>
        <div>
          <span className="text-ink-3">Étapes : </span>
          <strong className="text-ink tabular-nums">{draft.steps.length}</strong>
        </div>
        <div>
          <span className="text-ink-3">Cadence : </span>
          <strong className="text-ink tabular-nums">
            {draft.steps
              .map((s) => `J${s.offsetDays >= 0 ? "+" : ""}${s.offsetDays}`)
              .join(" · ")}
          </strong>
        </div>
      </div>

      <div className="flex flex-col gap-5">
        {draft.steps.map((s, idx) => (
          <div key={idx}>
            <div className="flex items-center gap-3 mb-2">
              <span className="size-2 rotate-45 bg-rubis" aria-hidden="true" />
              <p className="font-display text-[14px] font-semibold text-ink">
                Étape {idx + 1} · J{s.offsetDays >= 0 ? "+" : ""}
                {s.offsetDays} · {TONE_LABELS[s.tone]}
              </p>
              {s.requiresManualValidation && (
                <span className="inline-flex items-center gap-1 rounded-full bg-rubis-glow px-2 py-0.5 text-[10.5px] font-semibold text-rubis-deep">
                  <AlertTriangle size={10} /> Validation manuelle
                </span>
              )}
            </div>
            <EmailPreview subject={s.subject} body={s.body} />
          </div>
        ))}
      </div>
    </div>
  );
}

// ============================================================================
// Helpers
// ============================================================================

function lastTone(steps: PlanStep[]): RelanceTone | null {
  return steps[steps.length - 1]?.tone ?? null;
}

function stepCanProceed(step: WizardStep, draft: Draft): boolean {
  switch (step) {
    case 1:
      return draft.name.trim().length > 0;
    case 2:
      return draft.steps.length >= 1 && draft.steps.length <= 8;
    case 3:
      return draft.steps.every(
        (s) => s.subject.trim() !== "" && s.body.trim() !== "",
      );
    case 4:
      return (
        draft.name.trim().length > 0 &&
        draft.steps.length >= 1 &&
        draft.steps.every((s) => s.subject.trim() !== "" && s.body.trim() !== "")
      );
  }
}

/**
 * Détecte les variables sensibles utilisées dans les templates et compte
 * combien de clients existants n'ont pas le champ correspondant rempli.
 */
function computeClientWarnings(
  steps: DraftStep[],
  clients: Client[],
): Array<{ field: string; label: string; missingCount: number }> {
  const warnings: Array<{ field: string; label: string; missingCount: number }> = [];
  const allText = steps.map((s) => `${s.subject}\n${s.body}`).join("\n");

  for (const v of TEMPLATE_VARIABLES) {
    if (!v.requiresClientField) continue;
    if (!allText.includes(v.token)) continue;
    const missingCount = clients.filter((c) => {
      const value = c[v.requiresClientField as keyof Client];
      return !value || (typeof value === "string" && value.trim() === "");
    }).length;
    if (missingCount > 0) {
      warnings.push({
        field: v.requiresClientField,
        label: v.label,
        missingCount,
      });
    }
  }
  return warnings;
}
