"use client";

import { useState, useTransition } from "react";
import {
  buildAcceptString,
  type FormField,
  type DocumentRequest,
} from "@/lib/forms/types";
import { submitFormResponse } from "@/app/(public)/_actions/submit-form";
import Icon from "@/components/Icon";

type Props = {
  serviceSlug: string;
  title: string;
  description?: string | null;
  fields: FormField[];
  documents: DocumentRequest[];
  /** Prix total du service (utilisé pour récap). */
  priceEur?: number | null;
  priceNote?: string | null;
  /** Acompte 50 % calculé côté server (lib/services.ts). Null si "sur devis". */
  depositEur?: number | null;
  /** Vrai si Stripe est configuré (sinon le bouton CB est désactivé côté UI). */
  stripeEnabled?: boolean;
};

type FieldValue = string | boolean | number | File[];
type FormValues = Record<string, FieldValue>;
type DocFiles = Record<string, File[]>;
type Step = 1 | 2 | 3;

type Meta = {
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  password: string;
  passwordConfirm: string;
};

type SuccessResult = {
  dossierReference: string;
  invoiceNumber: string;
  amountCents: number;
  email: string;
  paymentMode: "card" | "bank" | "quote";
};

const STEP_LABELS: Record<Step, string> = {
  1: "Vos informations",
  2: "Pièces à joindre",
  3: "Validation et paiement",
};

export default function DynamicForm({
  serviceSlug,
  title,
  description,
  fields,
  documents,
  priceEur,
  priceNote,
  depositEur,
  stripeEnabled,
}: Props) {
  const [step, setStep] = useState<Step>(1);
  const [values, setValues] = useState<FormValues>(() => {
    const init: FormValues = {};
    for (const f of fields) {
      if (f.type === "checkbox") init[f.id] = false;
      else if (f.type === "file") init[f.id] = [];
      else init[f.id] = "";
    }
    return init;
  });
  const [docFiles, setDocFiles] = useState<DocFiles>(() => {
    const init: DocFiles = {};
    for (const d of documents) init[d.id] = [];
    return init;
  });
  const [meta, setMeta] = useState<Meta>({
    firstName: "",
    lastName: "",
    email: "",
    phone: "",
    password: "",
    passwordConfirm: "",
  });
  const [errors, setErrors] = useState<Record<string, string>>({});
  const [globalError, setGlobalError] = useState<string | null>(null);
  const [isPending, startTransition] = useTransition();
  const [successResult, setSuccessResult] = useState<SuccessResult | null>(null);

  function setMetaField<K extends keyof Meta>(k: K, v: string) {
    setMeta((prev) => ({ ...prev, [k]: v }));
    const errKey = `meta:${k}`;
    if (errors[errKey]) {
      setErrors((prev) => {
        const { [errKey]: _, ...rest } = prev;
        return rest;
      });
    }
  }

  function setValue(id: string, value: FieldValue) {
    setValues((prev) => ({ ...prev, [id]: value }));
    if (errors[id]) {
      setErrors((prev) => {
        const { [id]: _, ...rest } = prev;
        return rest;
      });
    }
  }

  function setDocFile(id: string, files: File[]) {
    setDocFiles((prev) => ({ ...prev, [id]: files }));
    if (errors[`doc:${id}`]) {
      setErrors((prev) => {
        const { [`doc:${id}`]: _, ...rest } = prev;
        return rest;
      });
    }
  }

  function validateStep1(): boolean {
    const errs: Record<string, string> = {};
    for (const f of fields) {
      const v = values[f.id];
      const strVal = typeof v === "string" ? v.trim() : "";

      // Required check
      if (f.required) {
        if (f.type === "checkbox") {
          if (v !== true) errs[f.id] = "Vous devez cocher ce champ";
        } else if (f.type === "file") {
          if (!Array.isArray(v) || v.length === 0)
            errs[f.id] = "Au moins un fichier requis";
        } else if (typeof v === "string") {
          if (!strVal) errs[f.id] = "Champ requis";
        } else if (v == null) {
          errs[f.id] = "Champ requis";
        }
      }

      // Format / contraintes (uniquement si rempli, sinon on laisse passer
      // les champs optionnels vides). On reproduit la validation serveur Zod
      // pour éviter les rebonds vers l'étape 1 lors du submit final.
      if (!errs[f.id] && strVal) {
        if (f.type === "email") {
          const ok = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(strVal);
          if (!ok) errs[f.id] = "Email invalide";
        } else if (f.type === "phone") {
          const ok = /^[\d\s+()-]{6,}$/.test(strVal);
          if (!ok) errs[f.id] = "Numéro de téléphone invalide";
        } else if (f.type === "text" || f.type === "textarea") {
          if (typeof f.minLength === "number" && strVal.length < f.minLength)
            errs[f.id] = `Au moins ${f.minLength} caractères requis (actuel : ${strVal.length}).`;
          else if (
            typeof f.maxLength === "number" &&
            strVal.length > f.maxLength
          )
            errs[f.id] = `Au plus ${f.maxLength} caractères autorisés.`;
        } else if (f.type === "number") {
          const n = Number(strVal);
          if (!Number.isFinite(n)) errs[f.id] = "Nombre invalide.";
          else if (typeof f.min === "number" && n < f.min)
            errs[f.id] = `Valeur minimale : ${f.min}.`;
          else if (typeof f.max === "number" && n > f.max)
            errs[f.id] = `Valeur maximale : ${f.max}.`;
        } else if (f.type === "select" || f.type === "radio") {
          if (
            f.options &&
            f.options.length > 0 &&
            !f.options.some((o) => o.value === strVal)
          ) {
            errs[f.id] = "Choisissez une option.";
          }
        } else if (f.type === "date") {
          if (Number.isNaN(new Date(strVal).getTime()))
            errs[f.id] = "Date invalide.";
        }
      }
    }
    setErrors(errs);
    if (Object.keys(errs).length > 0) {
      setGlobalError(
        "Certains champs ne sont pas valides. Corrigez-les pour continuer."
      );
      return false;
    }
    return true;
  }

  function validateStep2(): boolean {
    const errs: Record<string, string> = {};
    for (const d of documents) {
      if (!d.required) continue;
      const files = docFiles[d.id] ?? [];
      if (files.length === 0) errs[`doc:${d.id}`] = "Document requis";
    }
    setErrors(errs);
    if (Object.keys(errs).length > 0) {
      setGlobalError(
        "Veuillez fournir les documents requis pour continuer."
      );
      return false;
    }
    return true;
  }

  function buildFormData(): FormData {
    const fd = new FormData();
    fd.append("serviceSlug", serviceSlug);
    for (const f of fields) {
      const v = values[f.id];
      if (f.type === "file" && Array.isArray(v)) {
        for (const file of v) fd.append(`file:${f.id}`, file);
      } else if (typeof v === "boolean") {
        fd.append(`value:${f.id}`, v ? "true" : "false");
      } else if (v != null) {
        fd.append(`value:${f.id}`, String(v));
      }
    }
    for (const d of documents) {
      const files = docFiles[d.id] ?? [];
      for (const file of files) fd.append(`doc:${d.id}`, file);
    }
    return fd;
  }

  function validateStep3(requirePassword: boolean): boolean {
    const errs: Record<string, string> = {};
    if (!meta.firstName.trim()) errs["meta:firstName"] = "Prénom requis.";
    if (!meta.lastName.trim()) errs["meta:lastName"] = "Nom requis.";
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(meta.email.trim()))
      errs["meta:email"] = "Email invalide.";
    if (meta.phone.trim() && !/^[\d\s+()-]{6,}$/.test(meta.phone.trim()))
      errs["meta:phone"] = "Numéro de téléphone invalide.";
    if (requirePassword) {
      if (meta.password.length < 10)
        errs["meta:password"] =
          "Mot de passe trop court (10 caractères minimum).";
      if (meta.password !== meta.passwordConfirm)
        errs["meta:passwordConfirm"] =
          "Les deux mots de passe ne correspondent pas.";
    }
    setErrors(errs);
    if (Object.keys(errs).length > 0) {
      setGlobalError("Veuillez corriger vos informations pour continuer.");
      return false;
    }
    return true;
  }

  function goNext() {
    setGlobalError(null);
    if (step === 1) {
      if (!validateStep1()) return;
      setStep(2);
      if (typeof window !== "undefined")
        window.scrollTo({ top: 0, behavior: "smooth" });
    } else if (step === 2) {
      if (!validateStep2()) return;
      setStep(3);
      if (typeof window !== "undefined")
        window.scrollTo({ top: 0, behavior: "smooth" });
    }
  }

  function goBack() {
    if (step === 2) setStep(1);
    else if (step === 3) setStep(2);
  }

  function submitWithMode(mode: "card" | "bank" | "quote") {
    setGlobalError(null);
    const requirePwd = mode !== "quote";
    if (!validateStep3(requirePwd)) return;

    const fd = buildFormData();
    fd.append("meta:firstName", meta.firstName.trim());
    fd.append("meta:lastName", meta.lastName.trim());
    fd.append("meta:email", meta.email.trim().toLowerCase());
    fd.append("meta:phone", meta.phone.trim());
    if (requirePwd) fd.append("meta:password", meta.password);
    fd.append("meta:paymentMode", mode);

    startTransition(async () => {
      try {
        const result = await submitFormResponse(fd);
        if (!result.ok) {
          if (result.fieldErrors) setErrors(result.fieldErrors);
          if (result.globalError) setGlobalError(result.globalError);
          const stepOneIds = new Set(fields.map((f) => f.id));
          const hasStep1Error =
            !!result.fieldErrors &&
            Object.keys(result.fieldErrors).some((k) => stepOneIds.has(k));
          if (hasStep1Error) {
            setStep(1);
            if (typeof window !== "undefined")
              window.scrollTo({ top: 0, behavior: "smooth" });
          }
          return;
        }
        // Stripe : redirection immédiate vers la page Checkout
        if (result.checkoutUrl) {
          window.location.href = result.checkoutUrl;
          return;
        }
        // Modes virement / devis : page de confirmation
        setSuccessResult({
          dossierReference: result.dossierReference,
          invoiceNumber: result.invoiceNumber,
          amountCents: (depositEur ?? priceEur ?? 0) * 100,
          email: meta.email,
          paymentMode: result.paymentMode,
        });
        if (typeof window !== "undefined")
          window.scrollTo({ top: 0, behavior: "smooth" });
      } catch (err) {
        console.error("[submit] Server error:", err);
        setGlobalError(
          "Une erreur serveur est survenue. Réessayez ou contactez-nous par email à contact@socialex.pro."
        );
      }
    });
  }

  return (
    <div className="container-page py-16 md:py-24">
      <div className="max-w-3xl mx-auto">
        {/* Header */}
        <header className="mb-12 text-center">
          <span className="eyebrow">Démarrer un dossier</span>
          <h1 className="display-lg !text-3xl md:!text-4xl text-primary mt-3 mb-4">
            {title}
          </h1>
          {description && step !== 3 && (
            <p className="body-md text-on-surface-variant max-w-2xl mx-auto">
              {description}
            </p>
          )}
        </header>

        {/* Stepper */}
        {step !== 3 && <Stepper current={step} />}

        {/* Bandeau erreur global */}
        {globalError && step !== 3 && (
          <div
            role="alert"
            className="mb-6 bg-error-container border border-error/40 px-5 py-4 flex items-start gap-3"
            style={{
              backgroundColor: "rgba(186, 26, 26, 0.06)",
              borderColor: "rgba(186, 26, 26, 0.4)",
            }}
          >
            <Icon name="error" className="!text-base text-error mt-0.5" />
            <p className="body-md text-error flex-1">{globalError}</p>
          </div>
        )}

        {/* Content */}
        {step === 1 && (
          <StepFields
            fields={fields}
            values={values}
            onChange={setValue}
            errors={errors}
          />
        )}

        {step === 2 && (
          <StepDocuments
            serviceSlug={serviceSlug}
            documents={documents}
            docFiles={docFiles}
            onChange={setDocFile}
            errors={errors}
          />
        )}

        {step === 3 && !successResult && (
          <StepCheckout
            serviceTitle={title}
            priceEur={priceEur}
            priceNote={priceNote}
            depositEur={depositEur}
            stripeEnabled={stripeEnabled}
            meta={meta}
            onMetaChange={setMetaField}
            errors={errors}
            isPending={isPending}
            onSubmit={submitWithMode}
            onBack={goBack}
          />
        )}

        {step === 3 && successResult && (
          <StepConfirmation
            result={successResult}
            priceEur={priceEur}
            priceNote={priceNote}
            serviceTitle={title}
          />
        )}

        {/* Navigation classique uniquement aux étapes 1 et 2.
            La step 3 a ses propres boutons de paiement. */}
        {step !== 3 && (
          <NavBar
            step={step}
            isPending={isPending}
            onBack={goBack}
            onNext={goNext}
          />
        )}
      </div>
    </div>
  );
}

/* ─── Stepper ─────────────────────────────────────────────────────────── */

function Stepper({ current }: { current: Step }) {
  const steps: Step[] = [1, 2, 3];
  return (
    <nav aria-label="Progression" className="mb-12 md:mb-16">
      <ol className="flex items-center justify-between gap-4 max-w-xl mx-auto">
        {steps.map((s, i) => {
          const status =
            s < current ? "done" : s === current ? "active" : "future";
          return (
            <li key={s} className="flex items-center flex-1">
              <div className="flex flex-col items-center gap-3">
                <div
                  className={[
                    "w-11 h-11 flex items-center justify-center font-serif text-lg transition-all",
                    status === "done"
                      ? "bg-primary text-on-primary"
                      : status === "active"
                      ? "bg-primary text-on-primary ring-4 ring-secondary/30"
                      : "bg-white text-on-surface-variant border border-outline-variant",
                  ].join(" ")}
                >
                  {status === "done" ? <Icon name="check" /> : s}
                </div>
                <span
                  className={[
                    "text-xs uppercase tracking-widest font-serif text-center hidden md:block",
                    status === "future" ? "text-outline-variant" : "text-on-surface",
                  ].join(" ")}
                >
                  {STEP_LABELS[s]}
                </span>
              </div>
              {i < steps.length - 1 && (
                <div
                  className={[
                    "flex-1 h-px mx-2 md:mx-4 -translate-y-3",
                    s < current ? "bg-primary" : "bg-outline-variant",
                  ].join(" ")}
                />
              )}
            </li>
          );
        })}
      </ol>
    </nav>
  );
}

/* ─── Step 1 : Fields ─────────────────────────────────────────────────── */

function StepFields({
  fields,
  values,
  onChange,
  errors,
}: {
  fields: FormField[];
  values: FormValues;
  onChange: (id: string, v: FieldValue) => void;
  errors: Record<string, string>;
}) {
  return (
    <section className="bg-white border border-outline-variant p-8 lg:p-12 space-y-10">
      <header className="border-b border-outline-variant/60 pb-6">
        <span className="eyebrow">Étape 1 sur 3</span>
        <h2 className="font-serif text-2xl md:text-3xl text-primary mt-2">
          Vos informations
        </h2>
      </header>
      <div className="space-y-8">
        {fields.map((f) => (
          <FieldRenderer
            key={f.id}
            field={f}
            value={values[f.id]}
            onChange={(v) => onChange(f.id, v)}
            error={errors[f.id]}
          />
        ))}
      </div>
    </section>
  );
}

/* ─── Step 2 : Documents ──────────────────────────────────────────────── */

function StepDocuments({
  serviceSlug,
  documents,
  docFiles,
  onChange,
  errors,
}: {
  serviceSlug: string;
  documents: DocumentRequest[];
  docFiles: DocFiles;
  onChange: (id: string, files: File[]) => void;
  errors: Record<string, string>;
}) {
  if (documents.length === 0) {
    return (
      <section className="bg-white border border-outline-variant p-12 text-center space-y-4">
        <Icon name="task_alt" className="!text-4xl text-secondary" filled />
        <h2 className="font-serif text-2xl text-primary">
          Aucun document requis
        </h2>
        <p className="body-md text-on-surface-variant max-w-md mx-auto">
          Vous pouvez passer directement à la validation de votre dossier.
        </p>
      </section>
    );
  }

  return (
    <section className="bg-white border border-outline-variant p-8 lg:p-12 space-y-10">
      <header className="border-b border-outline-variant/60 pb-6">
        <span className="eyebrow">Étape 2 sur 3</span>
        <h2 className="font-serif text-2xl md:text-3xl text-primary mt-2">
          Pièces à joindre
        </h2>
        <p className="body-md text-on-surface-variant mt-3">
          Téléversez les documents demandés. Vous pourrez en ajouter plus tard
          depuis votre espace client si besoin.
        </p>
      </header>

      <div className="space-y-10">
        {documents.map((d) => (
          <DocumentUpload
            key={d.id}
            serviceSlug={serviceSlug}
            doc={d}
            files={docFiles[d.id] ?? []}
            onChange={(files) => onChange(d.id, files)}
            error={errors[`doc:${d.id}`]}
          />
        ))}
      </div>
    </section>
  );
}

function DocumentUpload({
  serviceSlug,
  doc,
  files,
  onChange,
  error,
}: {
  serviceSlug: string;
  doc: DocumentRequest;
  files: File[];
  onChange: (files: File[]) => void;
  error?: string;
}) {
  const accept = buildAcceptString(doc.acceptCategories);
  const maxSizeMb = doc.maxSizeMb ?? 25;

  return (
    <div className="border-l-2 border-outline-variant pl-6">
      <div className="flex items-start justify-between gap-4 flex-wrap mb-4">
        <div>
          <p className="eyebrow !text-on-surface-variant">
            {doc.label}
            {doc.required && <span className="ml-1 text-secondary">*</span>}
          </p>
          {doc.description && (
            <p className="text-sm text-on-surface-variant mt-1 max-w-md">
              {doc.description}
            </p>
          )}
        </div>
        {doc.sampleFile && (
          <a
            href={`/api/samples/${serviceSlug}/${doc.id}`}
            className="inline-flex items-center gap-2 text-xs uppercase tracking-widest font-serif text-secondary hover:text-primary transition-colors"
          >
            <Icon name="download" className="!text-base" />
            Télécharger le modèle
          </a>
        )}
      </div>

      <input
        id={`doc-${doc.id}`}
        type="file"
        accept={accept || undefined}
        multiple={doc.multiple}
        onChange={(e) => {
          const list = Array.from(e.target.files ?? []);
          const oversized = list.find((f) => f.size > maxSizeMb * 1024 * 1024);
          if (oversized) {
            alert(
              `Le fichier "${oversized.name}" dépasse la taille maximale (${maxSizeMb} Mo).`
            );
            e.target.value = "";
            return;
          }
          onChange(list);
        }}
        className="block w-full text-sm text-on-surface file:mr-4 file:py-3 file:px-5 file:border file:border-outline-variant file:bg-white file:text-xs file:uppercase file:tracking-widest file:font-serif file:cursor-pointer file:hover:border-primary file:hover:bg-surface-container-low file:transition-colors py-2"
      />

      {files.length > 0 && (
        <ul className="mt-3 space-y-1">
          {files.map((f, i) => (
            <li
              key={i}
              className="flex items-center gap-2 text-xs font-mono text-on-surface-variant truncate"
            >
              <Icon name="attach_file" className="!text-sm text-secondary" />
              {f.name}
              <span className="opacity-50">
                ({(f.size / 1024 / 1024).toFixed(2)} Mo)
              </span>
            </li>
          ))}
        </ul>
      )}

      <p className="text-xs text-on-surface-variant/70 mt-2">
        {doc.multiple ? "Un ou plusieurs fichiers" : "Un fichier"} · max{" "}
        {maxSizeMb} Mo
      </p>

      {error && <p className="text-error text-sm mt-2">{error}</p>}
    </div>
  );
}

/* ─── Step 3 : Confirmation + paiement ────────────────────────────────── */

function StepConfirmation({
  result,
  priceEur,
  priceNote,
  serviceTitle,
}: {
  result: SuccessResult;
  priceEur?: number | null;
  priceNote?: string | null;
  serviceTitle: string;
}) {
  const amount = result.amountCents / 100;
  const formattedAmount = new Intl.NumberFormat("fr-FR", {
    style: "currency",
    currency: "EUR",
    maximumFractionDigits: 0,
  }).format(amount);

  const isQuote = result.paymentMode === "quote";
  const isBank = result.paymentMode === "bank";

  return (
    <div className="space-y-8">
      {/* Confirmation hero */}
      <section className="bg-white border border-outline-variant p-12 lg:p-16 text-center space-y-6">
        <div className="inline-flex items-center justify-center w-16 h-16 bg-secondary/10">
          <Icon name="check" className="!text-3xl text-secondary" />
        </div>
        <span className="eyebrow !text-secondary block">
          {isQuote ? "Demande enregistrée" : "Dossier créé"}
        </span>
        <h2 className="font-serif text-3xl md:text-4xl text-primary">
          {isQuote ? (
            <>
              Votre demande a bien été reçue.
              <br />
              <em className="font-normal">
                Notre équipe vous recontacte sous 24 h.
              </em>
            </>
          ) : (
            <>
              Votre dossier est enregistré.
              <br />
              <em className="font-normal">
                Il ne reste plus qu'à régler l'acompte.
              </em>
            </>
          )}
        </h2>
        <p className="body-md text-on-surface-variant max-w-xl mx-auto">
          Un email récapitulatif a été envoyé à{" "}
          <span className="text-primary font-medium">{result.email}</span>
          {isBank
            ? " avec les coordonnées de virement et l'accès à votre espace client."
            : isQuote
            ? "."
            : " avec votre facture détaillée."}
        </p>
      </section>

      {/* Récap facture (sauf quote) */}
      {!isQuote && (
        <section className="bg-primary text-on-primary p-10 lg:p-12">
          <span className="eyebrow !text-secondary">
            {isBank ? "Acompte à régler par virement" : "Acompte"}
          </span>
          <h3 className="font-serif text-xl mt-2 mb-8 opacity-90">
            {serviceTitle}
          </h3>

          <dl className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-10 border-y border-on-primary/20 py-8">
            <div>
              <dt className="text-xs uppercase tracking-widest opacity-60 font-serif mb-2">
                Référence dossier
              </dt>
              <dd className="font-mono text-lg">{result.dossierReference}</dd>
            </div>
            <div>
              <dt className="text-xs uppercase tracking-widest opacity-60 font-serif mb-2">
                Facture acompte
              </dt>
              <dd className="font-mono text-lg">{result.invoiceNumber}</dd>
            </div>
            <div>
              <dt className="text-xs uppercase tracking-widest opacity-60 font-serif mb-2">
                Montant à régler
              </dt>
              <dd className="font-serif text-2xl text-secondary">
                {formattedAmount}
              </dd>
            </div>
          </dl>

          {priceNote && (
            <p className="text-sm opacity-70 max-w-2xl">{priceNote}</p>
          )}
        </section>
      )}

      {/* Accès espace client */}
      {!isQuote && (
        <section className="bg-white border border-outline-variant p-8 lg:p-10 text-center">
          <p className="body-md text-on-surface-variant mb-6">
            Vous pouvez dès maintenant accéder à votre espace client pour
            suivre l'avancement de votre dossier et déposer des documents
            complémentaires.
          </p>
          <a href="/espace-client" className="btn btn-primary inline-flex">
            Accéder à mon espace client
            <Icon name="arrow_forward" className="!text-base ml-2" />
          </a>
        </section>
      )}

      <p className="text-center text-xs text-on-surface-variant uppercase tracking-widest font-serif">
        Pour toute question :{" "}
        <a
          href="mailto:contact@socialex.pro"
          className="underline hover:text-primary transition-colors"
        >
          contact@socialex.pro
        </a>
      </p>
    </div>
  );
}

/* ─── Step 3 : Compte + Paiement ──────────────────────────────────────── */

function StepCheckout({
  serviceTitle,
  priceEur,
  priceNote,
  depositEur,
  stripeEnabled,
  meta,
  onMetaChange,
  errors,
  isPending,
  onSubmit,
  onBack,
}: {
  serviceTitle: string;
  priceEur?: number | null;
  priceNote?: string | null;
  depositEur?: number | null;
  stripeEnabled?: boolean;
  meta: Meta;
  onMetaChange: <K extends keyof Meta>(k: K, v: string) => void;
  errors: Record<string, string>;
  isPending: boolean;
  onSubmit: (mode: "card" | "bank" | "quote") => void;
  onBack: () => void;
}) {
  const noPrice = depositEur == null || depositEur <= 0;
  const total = priceEur ?? (depositEur != null ? depositEur * 2 : null);

  const fmt = (eur: number) =>
    new Intl.NumberFormat("fr-FR", {
      style: "currency",
      currency: "EUR",
      maximumFractionDigits: 0,
    }).format(eur);

  return (
    <section className="bg-white border border-outline-variant p-8 lg:p-12 space-y-12">
      <header className="border-b border-outline-variant/60 pb-6">
        <span className="eyebrow">Étape 3 sur 3</span>
        <h2 className="font-serif text-2xl md:text-3xl text-primary mt-2">
          Validation et paiement
        </h2>
        <p className="body-md text-on-surface-variant mt-3 max-w-2xl">
          Créez votre espace client en deux clics, puis réglez l'acompte pour
          que nous lancions le traitement de votre dossier.
        </p>
      </header>

      {/* Informations de contact */}
      <div className="space-y-6">
        <h3 className="font-serif text-xl text-primary">Vos informations</h3>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
          <CheckoutField
            label="Prénom"
            required
            value={meta.firstName}
            onChange={(v) => onMetaChange("firstName", v)}
            error={errors["meta:firstName"]}
            autoComplete="given-name"
          />
          <CheckoutField
            label="Nom"
            required
            value={meta.lastName}
            onChange={(v) => onMetaChange("lastName", v)}
            error={errors["meta:lastName"]}
            autoComplete="family-name"
          />
        </div>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
          <CheckoutField
            label="Email"
            type="email"
            required
            value={meta.email}
            onChange={(v) => onMetaChange("email", v)}
            error={errors["meta:email"]}
            autoComplete="email"
            help="Sert d'identifiant pour votre espace client."
          />
          <CheckoutField
            label="Téléphone"
            type="tel"
            value={meta.phone}
            onChange={(v) => onMetaChange("phone", v)}
            error={errors["meta:phone"]}
            autoComplete="tel"
          />
        </div>
      </div>

      {/* Création de compte (sauf devis) */}
      {!noPrice && (
        <div className="space-y-6">
          <h3 className="font-serif text-xl text-primary">
            Créer votre espace client
          </h3>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
            <CheckoutField
              label="Mot de passe (10 caractères min)"
              type="password"
              required
              value={meta.password}
              onChange={(v) => onMetaChange("password", v)}
              error={errors["meta:password"]}
              autoComplete="new-password"
            />
            <CheckoutField
              label="Confirmation"
              type="password"
              required
              value={meta.passwordConfirm}
              onChange={(v) => onMetaChange("passwordConfirm", v)}
              error={errors["meta:passwordConfirm"]}
              autoComplete="new-password"
            />
          </div>
        </div>
      )}

      {/* Récapitulatif facturation */}
      <div className="bg-surface-container-low border border-outline-variant p-6 lg:p-8 space-y-4">
        <span className="eyebrow !text-secondary">Récapitulatif</span>
        <p className="font-serif text-xl text-primary">{serviceTitle}</p>
        {noPrice ? (
          <p className="body-md text-on-surface-variant">
            Tarif établi sur devis selon les pays choisis. Notre équipe vous
            recontactera sous 24 heures ouvrées avec une proposition détaillée.
          </p>
        ) : (
          <>
            <dl className="grid grid-cols-1 md:grid-cols-2 gap-6 pt-2">
              <div>
                <dt className="text-xs uppercase tracking-widest text-on-surface-variant font-serif mb-1">
                  Total prestation
                </dt>
                <dd className="font-serif text-2xl text-on-surface">
                  {total != null ? fmt(total) : "—"}
                </dd>
              </div>
              <div>
                <dt className="text-xs uppercase tracking-widest text-on-surface-variant font-serif mb-1">
                  Acompte à régler maintenant (50 %)
                </dt>
                <dd className="font-serif text-3xl text-secondary tracking-tight">
                  {fmt(depositEur!)}
                </dd>
              </div>
            </dl>
            {priceNote && (
              <p className="text-xs text-on-surface-variant pt-2">
                {priceNote}
              </p>
            )}
            <p className="text-xs text-on-surface-variant">
              Le solde est facturé séparément, à régler une fois la prestation
              terminée.
            </p>
          </>
        )}
      </div>

      {/* Boutons de paiement */}
      <div className="space-y-3">
        {noPrice ? (
          <button
            type="button"
            onClick={() => onSubmit("quote")}
            disabled={isPending}
            className="btn btn-primary w-full justify-center disabled:opacity-50 disabled:cursor-not-allowed"
          >
            {isPending ? "Envoi…" : "Envoyer ma demande"}
            <Icon name="arrow_forward" className="!text-base ml-2" />
          </button>
        ) : (
          <>
            <button
              type="button"
              onClick={() => onSubmit("card")}
              disabled={isPending || !stripeEnabled}
              className="btn btn-primary w-full justify-center disabled:opacity-50 disabled:cursor-not-allowed"
              title={
                !stripeEnabled
                  ? "Paiement CB momentanément indisponible. Utilisez le virement."
                  : undefined
              }
            >
              <Icon name="credit_card" className="!text-base mr-2" />
              {isPending
                ? "Redirection…"
                : `Payer l'acompte par carte (${fmt(depositEur!)})`}
            </button>
            <button
              type="button"
              onClick={() => onSubmit("bank")}
              disabled={isPending}
              className="btn btn-secondary w-full justify-center disabled:opacity-50 disabled:cursor-not-allowed"
            >
              <Icon name="account_balance" className="!text-base mr-2" />
              {isPending ? "Envoi…" : "Régler par virement"}
            </button>
            {!stripeEnabled && (
              <p className="text-xs text-on-surface-variant text-center pt-2">
                Le paiement par carte sera bientôt disponible. Pour l'instant,
                réglez par virement (coordonnées envoyées par email).
              </p>
            )}
          </>
        )}

        <button
          type="button"
          onClick={onBack}
          disabled={isPending}
          className="inline-flex items-center gap-2 text-xs uppercase tracking-widest font-serif text-on-surface-variant hover:text-primary transition-colors mt-4 disabled:opacity-30"
        >
          <Icon name="arrow_back" className="!text-base" />
          Retour aux pièces
        </button>
      </div>
    </section>
  );
}

function CheckoutField({
  label,
  type = "text",
  value,
  onChange,
  required,
  error,
  autoComplete,
  help,
}: {
  label: string;
  type?: "text" | "email" | "tel" | "password";
  value: string;
  onChange: (v: string) => void;
  required?: boolean;
  error?: string;
  autoComplete?: string;
  help?: string;
}) {
  return (
    <div>
      <label className="eyebrow !text-on-surface-variant block mb-2">
        {label}
        {required && <span className="ml-1 text-secondary">*</span>}
      </label>
      <input
        type={type}
        value={value}
        onChange={(e) => onChange(e.target.value)}
        required={required}
        autoComplete={autoComplete}
        className="w-full bg-transparent border-0 border-b border-outline-variant focus:border-secondary py-3 outline-none body-md transition-colors"
      />
      {help && !error && (
        <p className="text-xs text-on-surface-variant mt-1">{help}</p>
      )}
      {error && <p className="text-error text-sm mt-1">{error}</p>}
    </div>
  );
}

/* ─── Nav buttons ─────────────────────────────────────────────────────── */

function NavBar({
  step,
  isPending,
  onBack,
  onNext,
}: {
  step: Step;
  isPending: boolean;
  onBack: () => void;
  onNext: () => void;
}) {
  const isLast = step === 2;
  return (
    <div className="mt-10">
      <div className="flex items-center justify-between gap-4 flex-wrap">
        {step > 1 ? (
          <button
            type="button"
            onClick={onBack}
            disabled={isPending}
            className="inline-flex items-center gap-2 text-xs uppercase tracking-widest font-serif text-on-surface-variant hover:text-primary transition-colors disabled:opacity-30"
          >
            <Icon name="arrow_back" className="!text-base" />
            Retour
          </button>
        ) : (
          <span aria-hidden />
        )}

        <button
          type="button"
          onClick={onNext}
          disabled={isPending}
          className="btn btn-primary disabled:opacity-50 disabled:cursor-not-allowed"
        >
          {isLast ? "Payer l'acompte" : "Continuer"}
          <Icon name="arrow_forward" className="!text-base ml-2" />
        </button>
      </div>
    </div>
  );
}

/* ─── Field renderer ─────────────────────────────────────────────────── */

function FieldRenderer({
  field,
  value,
  onChange,
  error,
}: {
  field: FormField;
  value: FieldValue | undefined;
  onChange: (v: FieldValue) => void;
  error?: string;
}) {
  const labelEl = (
    <label
      htmlFor={field.id}
      className="eyebrow !text-on-surface-variant block mb-2"
    >
      {field.label}
      {field.required && <span className="ml-1 text-secondary">*</span>}
    </label>
  );
  const helpEl = field.description && (
    <p className="text-xs text-on-surface-variant mt-1">{field.description}</p>
  );
  const errorEl = error && <p className="text-error text-sm mt-1">{error}</p>;

  const inputCls =
    "w-full bg-transparent border-0 border-b border-outline-variant focus:border-secondary py-3 outline-none body-md transition-colors";

  if (field.type === "textarea") {
    return (
      <div>
        {labelEl}
        <textarea
          id={field.id}
          value={String(value ?? "")}
          onChange={(e) => onChange(e.target.value)}
          placeholder={field.placeholder}
          required={field.required}
          minLength={field.minLength}
          maxLength={field.maxLength}
          rows={5}
          className={inputCls + " resize-y"}
        />
        {helpEl}
        {errorEl}
      </div>
    );
  }

  if (field.type === "select") {
    return (
      <div>
        {labelEl}
        <select
          id={field.id}
          value={String(value ?? "")}
          onChange={(e) => onChange(e.target.value)}
          required={field.required}
          className={inputCls + " appearance-none cursor-pointer"}
        >
          <option value="" disabled>
            Sélectionnez…
          </option>
          {field.options?.map((o) => (
            <option key={o.value} value={o.value}>
              {o.label}
            </option>
          ))}
        </select>
        {helpEl}
        {errorEl}
      </div>
    );
  }

  if (field.type === "radio") {
    return (
      <div>
        {labelEl}
        <div className="space-y-2 mt-2">
          {field.options?.map((o) => (
            <label
              key={o.value}
              className="flex items-center gap-3 body-md cursor-pointer"
            >
              <input
                type="radio"
                name={field.id}
                value={o.value}
                checked={value === o.value}
                onChange={(e) => onChange(e.target.value)}
                required={field.required}
                className="w-4 h-4 accent-primary"
              />
              {o.label}
            </label>
          ))}
        </div>
        {helpEl}
        {errorEl}
      </div>
    );
  }

  if (field.type === "file") {
    const files = Array.isArray(value) ? value : [];
    const accept = buildAcceptString(field.acceptCategories);
    const maxSizeMb = field.maxSizeMb ?? 25;
    return (
      <div>
        {labelEl}
        <input
          id={field.id}
          type="file"
          accept={accept || undefined}
          multiple={field.multiple}
          required={field.required && files.length === 0}
          onChange={(e) => {
            const list = Array.from(e.target.files ?? []);
            const oversized = list.find(
              (f) => f.size > maxSizeMb * 1024 * 1024
            );
            if (oversized) {
              alert(
                `Le fichier "${oversized.name}" dépasse la taille maximale (${maxSizeMb} Mo).`
              );
              e.target.value = "";
              return;
            }
            onChange(list);
          }}
          className="block w-full text-sm text-on-surface file:mr-4 file:py-2 file:px-4 file:border file:border-outline-variant file:bg-white file:text-xs file:uppercase file:tracking-widest file:font-serif file:cursor-pointer file:hover:border-primary file:transition-colors py-3"
        />
        {files.length > 0 && (
          <ul className="mt-2 space-y-1 text-xs text-on-surface-variant">
            {files.map((f, i) => (
              <li key={i} className="font-mono truncate">
                {f.name} ({(f.size / 1024 / 1024).toFixed(2)} Mo)
              </li>
            ))}
          </ul>
        )}
        <p className="text-xs text-on-surface-variant mt-1">
          {field.description ??
            `${field.multiple ? "Un ou plusieurs fichiers" : "Un fichier"} · max ${maxSizeMb} Mo`}
        </p>
        {errorEl}
      </div>
    );
  }

  if (field.type === "checkbox") {
    return (
      <div>
        <label className="flex items-start gap-3 cursor-pointer">
          <input
            type="checkbox"
            checked={Boolean(value)}
            onChange={(e) => onChange(e.target.checked)}
            required={field.required}
            className="mt-1 w-4 h-4 accent-primary shrink-0"
          />
          <span className="body-md text-on-surface">
            {field.label}
            {field.required && <span className="ml-1 text-secondary">*</span>}
          </span>
        </label>
        {helpEl}
        {errorEl}
      </div>
    );
  }

  // text, email, phone, number, date
  const inputType =
    field.type === "phone"
      ? "tel"
      : field.type === "number"
      ? "number"
      : field.type;

  return (
    <div>
      {labelEl}
      <input
        id={field.id}
        type={inputType}
        value={String(value ?? "")}
        onChange={(e) => onChange(e.target.value)}
        placeholder={field.placeholder}
        required={field.required}
        minLength={field.minLength}
        maxLength={field.maxLength}
        min={field.min}
        max={field.max}
        className={inputCls}
      />
      {helpEl}
      {errorEl}
    </div>
  );
}
