import { toLocalIso } from '@/components/fr-date';
import { FrDateInput } from '@/components/fr-date-input';
import { Button } from '@/components/ui/button';
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 { Spinner } from '@/components/ui/spinner';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { useTranslation } from '@/hooks/use-translation';
import AppLayout from '@/layouts/app-layout';
import { cn } from '@/lib/utils';
import type { BreadcrumbItem } from '@/types';
import { Head, Link, router, useForm } from '@inertiajs/react';
import axios, { AxiosError } from 'axios';
import {
    ArrowLeft,
    BadgeCheck,
    CircleAlert,
    Copy,
    CreditCard,
    Download,
    FileInput,
    FileText,
    Info,
    MapPin,
    MoreHorizontal,
    PencilLine,
    Receipt,
    RotateCcw,
    Save,
    Send,
    Stamp,
    Trash2,
    Undo2,
} from 'lucide-react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'sonner';
import { ClientField } from './components/client-field';
import { CoordonneesDialog, type Coordonnees } from './components/coordonnees-dialog';
import { ConfirmDialog, EmitDialog, PaymentDialog, SendDialog, type Verification } from './components/editor-dialogs';
import { InvoicePreview } from './components/invoice-preview';
import { ID_AJOUTER_LIGNE, idDesignation, idPrix, LinesEditor } from './components/lines-editor';
import { StatusChip } from './components/status-chip';
import { fmtDate, fmtDateHeure, fmtMontant, TYPE_LIBELLE } from './format';
import type { Apercu, ContexteEditeur, FactureEditProps, FactureForm } from './types';

type PageProps = FactureEditProps & {
    flash?: { type?: string; message?: string } | null;
    errors: Record<string, string>;
};

/**
 * Éditeur de facture — création, brouillon et consultation d'une pièce émise.
 *
 * Le contenu est remonté à chaque changement de facture (clé = id) : dupliquer,
 * créer un avoir ou convertir une proforma ouvre un AUTRE document, et le
 * formulaire doit repartir de ses données, pas garder celles du précédent.
 */
export default function FactureEdit(props: PageProps) {
    // Un brouillon neuf se reconnaît à sa réservation, sa devise et son type :
    // une autre réservation ou un autre type recharge la même page avec
    // d'autres données, et le formulaire doit repartir d'elles.
    const f = props.formulaire;
    // Pièce émise : ses coordonnées (seule chose qui peut y changer) font partie
    // de la clé, pour que l'aperçu se recharge après « Modifier adresse, notes… ».
    const cle = props.facture
        ? props.facture.statut === 'brouillon'
            ? props.facture.id
            : `${props.facture.id}-${JSON.stringify([f.client_adresse, f.client_email, f.note_entete, f.note_pied, f.echeance_jours])}`
        : `nouvelle-${f.reservation_id ?? ''}-${f.devise}-${f.type}`;

    return <Editeur key={cle} {...props} />;
}

function Editeur({ facture, formulaire, contexte, options, prochains, can, flash, errors }: PageProps) {
    const { t } = useTranslation();
    const form = useForm<FactureForm>(formulaire);
    const { data, setData } = form;
    const lectureSeule = facture !== null && facture.statut !== 'brouillon';
    const modifiable = !lectureSeule && can.create;
    // Valeurs ENREGISTRÉES (pas la saisie) pour « Modifier adresse, e-mail, notes, échéance ».
    const coordonnees = useMemo<Coordonnees>(
        () => ({
            client_adresse: formulaire.client_adresse,
            client_email: formulaire.client_email,
            note_entete: formulaire.note_entete,
            note_pied: formulaire.note_pied,
            echeance_jours: formulaire.echeance_jours,
        }),
        [formulaire],
    );

    const [apercu, setApercu] = useState<Apercu | null>(null);
    const [chargement, setChargement] = useState(false);
    const [pause, setPause] = useState<string | null>(null);
    const [vue, setVue] = useState<'saisie' | 'apercu'>('saisie');
    const [dialogue, setDialogue] = useState<
        null | 'emettre' | 'envoyer' | 'payer' | 'avoir' | 'supprimer' | 'impayer' | 'corriger' | 'coordonnees'
    >(null);
    const [emission, setEmission] = useState(false);
    const enregistrement = useRef(false);
    const requete = useRef(0);

    const erreurs: Record<string, string> = { ...errors, ...form.errors };

    // ── Messages du serveur ────────────────────────────────────────────────
    useEffect(() => {
        if (flash?.type === 'success' && flash.message) toast.success(flash.message);
        else if (flash?.type === 'error' && flash.message) toast.error(flash.message);
    }, [flash]);

    // Refus d'émission : le premier motif en toast ; ceux qui ont un champ s'affichent aussi dessous.
    useEffect(() => {
        const message = ['facture', 'reservation', 'date_facture', 'total_ttc', 'client_adresse', 'client_matricule_fiscal', 'mention_exoneration']
            .map((cle) => errors[cle])
            .find(Boolean);
        if (message) toast.error(message);
    }, [errors]);

    // ── Aperçu : le serveur calcule, rend le document et les totaux ────────
    // `t` est recréée à chaque rendu : dans les dépendances de `charger`, elle
    // relançait l'effet après chaque réponse — une requête toutes les 350 ms,
    // sans fin. On la lit donc à travers une ref.
    const tRef = useRef(t);
    tRef.current = t;
    const charger = useCallback(
        (donnees: FactureForm) => {
            const numero = ++requete.current;
            setChargement(true);
            axios
                .post<Apercu>(route('factures.apercu'), { ...donnees, facture_id: facture?.id ?? null })
                .then((r) => {
                    if (numero !== requete.current) return;
                    setApercu(r.data);
                    setPause(null);
                })
                .catch((e: AxiosError<{ errors?: Record<string, string[]> }>) => {
                    if (numero !== requete.current) return;
                    const premiere = Object.values(e.response?.data?.errors ?? {})[0]?.[0];
                    const tr = tRef.current;
                    setPause(
                        premiere
                            ? `${tr('factures', 'editeur.apercu_pause', 'Aperçu en pause')} : ${premiere}`
                            : tr('factures', 'editeur.apercu_erreur', 'Aperçu indisponible'),
                    );
                })
                .finally(() => {
                    if (numero === requete.current) setChargement(false);
                });
        },
        [facture?.id],
    );

    const empreinte = JSON.stringify(data);
    useEffect(() => {
        const minuteur = window.setTimeout(() => charger(JSON.parse(empreinte) as FactureForm), lectureSeule ? 0 : 350);
        return () => window.clearTimeout(minuteur);
    }, [empreinte, charger, lectureSeule]);

    // ── Ne pas perdre une saisie en quittant la page ───────────────────────
    useEffect(() => {
        if (lectureSeule) return;
        const avantDeQuitter = (e: BeforeUnloadEvent) => {
            if (form.isDirty && !enregistrement.current) e.preventDefault();
        };
        window.addEventListener('beforeunload', avantDeQuitter);
        const retirer = router.on('before', (event) => {
            if (form.isDirty && !enregistrement.current && event.detail.visit.method === 'get') {
                if (
                    !window.confirm(tRef.current('factures', 'editeur.quitter', 'Des modifications ne sont pas enregistrées. Quitter quand même ?'))
                ) {
                    event.preventDefault();
                }
            }
        });
        return () => {
            window.removeEventListener('beforeunload', avantDeQuitter);
            retirer();
        };
    }, [form.isDirty, lectureSeule]);

    // ── Enregistrer, puis éventuellement émettre ───────────────────────────
    const enregistrer = (ensuite?: (id: number) => void) => {
        const rappels = {
            preserveScroll: true,
            onStart: () => {
                enregistrement.current = true;
            },
            onFinish: () => {
                enregistrement.current = false;
            },
            onSuccess: (page: { props: unknown }) => {
                form.setDefaults();
                const id = (page.props as PageProps).facture?.id;
                if (ensuite && id) ensuite(id);
            },
        };
        if (facture) form.put(route('factures.update', facture.id), rappels);
        else form.post(route('factures.store'), rappels);
    };

    const emettre = () => {
        const lancer = (id: number) =>
            router.post(
                route('factures.emettre', id),
                {},
                {
                    preserveScroll: true,
                    onStart: () => setEmission(true),
                    onFinish: () => {
                        setEmission(false);
                        setDialogue(null);
                    },
                },
            );
        if (!facture || form.isDirty) enregistrer(lancer);
        else lancer(facture.id);
    };

    const action = (nom: 'avoir' | 'convertir' | 'dupliquer') => {
        if (facture) router.post(route(`factures.${nom}`, facture.id));
    };

    // ── Vérifications affichées avant l'émission ───────────────────────────
    const totaux = apercu?.totaux ?? null;
    // Chaque point rouge sait où se corrige ce qu'il signale (`cible`) : un clic
    // ferme la fenêtre et place le curseur dans le champ.
    const verifications = useMemo<Verification[]>(() => {
        const sansDesignation = data.lignes.findIndex((l) => l.designation.trim() === '');
        const prixNul = data.lignes.findIndex((l) => l.prix_unitaire_ht <= 0);
        // Mentions obligatoires d'une pièce fiscale (FactureService::verifierEmettable) — une proforma n'y est pas soumise.
        const fiscale = data.type !== 'proforma';
        const identifiantManquant = data.client_identifiant_type !== 'aucun' && !(data.client_matricule_fiscal ?? '').trim();
        const exonerationManquante = data.lignes.some((l) => Number(l.taux_tva) === 0) && !(data.mention_exoneration ?? '').trim();
        const sejourDejaFacture =
            contexte?.type === 'reservation' ? contexte.deja_facturee.find((f) => f.numero !== null && f.statut !== 'annulee') : undefined;

        const mentions: Verification[] = fiscale
            ? [
                  {
                      ok: (data.client_adresse ?? '').trim() !== '',
                      texte: (data.client_adresse ?? '').trim()
                          ? t('factures', 'editeur.verif_adresse_ok', 'Adresse du client renseignée')
                          : t('factures', 'editeur.verif_adresse', "Renseignez l'adresse du client (mention obligatoire)"),
                      cible: 'client_adresse',
                  },
                  {
                      ok: !identifiantManquant,
                      texte: identifiantManquant
                          ? t('factures', 'editeur.verif_identifiant', 'Renseignez le MF / la CIN du client, ou choisissez « Particulier »')
                          : data.client_identifiant_type === 'aucun'
                            ? t('factures', 'editeur.verif_particulier', 'Client particulier, sans identifiant')
                            : t('factures', 'editeur.verif_identifiant_ok', 'Identifiant du client renseigné'),
                      cible: 'client_mf',
                  },
                  ...(data.lignes.some((l) => Number(l.taux_tva) === 0)
                      ? [
                            {
                                ok: !exonerationManquante,
                                texte: exonerationManquante
                                    ? t('factures', 'editeur.verif_exoneration', "Une ligne est à 0 % : indiquez le fondement de l'exonération")
                                    : t('factures', 'editeur.verif_exoneration_ok', "Fondement de l'exonération indiqué"),
                                cible: 'mention_exoneration',
                            },
                        ]
                      : []),
              ]
            : [];
        const sejour: Verification[] =
            contexte?.type === 'reservation' && data.type === 'facture' && (contexte.statut_reservation || sejourDejaFacture)
                ? [
                      {
                          ok: false,
                          texte: contexte.statut_reservation
                              ? t('factures', 'editeur.verif_reservation_annulee', 'La réservation est annulée ou non confirmée : elle ne se facture pas')
                              : t('factures', 'editeur.verif_deja_facturee', 'Ce séjour est déjà facturé sur {numero}').replace('{numero}', sejourDejaFacture?.numero ?? ''),
                      },
                  ]
                : [];

        return [
            ...sejour,
            {
                ok: data.client_nom.trim() !== '',
                texte: data.client_nom.trim() ? `Client : ${data.client_nom}` : 'Choisissez le client',
                cible: 'client_nom',
            },
            {
                ok: data.lignes.length > 0 && sansDesignation === -1,
                texte:
                    data.lignes.length === 0
                        ? 'Ajoutez au moins une ligne'
                        : sansDesignation === -1
                          ? `${data.lignes.length} ligne${data.lignes.length > 1 ? 's' : ''}`
                          : `La ligne ${sansDesignation + 1} n'a pas de désignation`,
                cible: data.lignes.length === 0 ? ID_AJOUTER_LIGNE : idDesignation(Math.max(0, sansDesignation)),
            },
            {
                // Un aperçu en échec ne doit pas bloquer sur « Calcul… » sans dire
                // pourquoi : la raison du serveur s'affiche à la place.
                ok: pause === null && (totaux?.total_ttc ?? 0) > 0,
                texte: pause ?? (totaux ? `Net à payer : ${fmtMontant(totaux.total_ttc, data.devise)}` : 'Calcul du montant…'),
                cible: totaux && totaux.total_ttc <= 0 && data.lignes.length > 0 ? idPrix(Math.max(0, prixNul)) : undefined,
            },
            {
                ok: data.date_facture <= new Date().toISOString().slice(0, 10),
                texte:
                    data.date_facture <= new Date().toISOString().slice(0, 10)
                        ? `Date : ${fmtDate(data.date_facture)}`
                        : `Date dans le futur : ${fmtDate(data.date_facture)}`,
                cible: 'date_facture',
            },
            ...mentions,
        ];
        // `t` change à chaque rendu (voir `charger`) : ne pas l'y ajouter.
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [data, totaux, pause, contexte]);

    const allerAuChamp = (cible: string) => {
        setDialogue(null);
        setVue('saisie');
        // Après la fermeture de la fenêtre, qui rend le focus à son déclencheur.
        window.setTimeout(() => {
            const champ = document.getElementById(cible);
            champ?.scrollIntoView({ behavior: 'smooth', block: 'center' });
            champ?.focus({ preventScroll: true });
        }, 180);
    };

    const titre = facture?.numero
        ? facture.numero
        : facture
          ? `Brouillon · ${TYPE_LIBELLE[data.type]}`
          : data.type === 'proforma'
            ? t('factures', 'editeur.nouvelle_proforma', 'Nouvelle proforma')
            : data.type === 'avoir'
              ? t('factures', 'editeur.nouvel_avoir', 'Nouvel avoir')
              : t('factures', 'editeur.nouvelle_facture', 'Nouvelle facture');

    const breadcrumbs: BreadcrumbItem[] = [
        { title: t('factures', 'liste.titre', 'Factures'), href: route('factures.index') },
        { title: titre, href: facture ? route('factures.edit', facture.id) : route('factures.create') },
    ];

    return (
        <AppLayout breadcrumbs={breadcrumbs}>
            <Head title={titre} />
            <div className="flex h-full min-w-0 flex-1 flex-col gap-4 p-3 sm:p-4 lg:p-6">
                {/* ── En-tête ─────────────────────────────────────────────── */}
                <div className="flex w-full min-w-0 flex-wrap items-start justify-between gap-3">
                    <div className="min-w-0">
                        <Link
                            href={route('factures.index')}
                            className="text-muted-foreground hover:text-foreground mb-1 inline-flex items-center gap-1 text-sm"
                        >
                            <ArrowLeft className="h-4 w-4" />
                            {t('factures', 'liste.titre', 'Factures')}
                        </Link>
                        <div className="flex flex-wrap items-center gap-2">
                            <h1 className={cn('text-xl font-semibold', facture?.numero && 'font-mono')}>{titre}</h1>
                            {facture && <StatusChip statut={facture.statut} enRetard={facture.en_retard} type={facture.type} />}
                            {facture?.numero && (
                                <span className="text-muted-foreground rounded border px-1.5 py-0.5 text-xs">{TYPE_LIBELLE[facture.type]}</span>
                            )}
                        </div>
                        {(facture?.origine_libelle ?? data.origine_libelle) && (
                            <p className="text-muted-foreground mt-0.5 text-sm">{facture?.origine_libelle ?? data.origine_libelle}</p>
                        )}
                    </div>

                    <div className="flex flex-wrap items-center gap-2" data-tour="factures-edit-actions">
                        {modifiable && (
                            <>
                                <Button variant="outline" onClick={() => enregistrer()} disabled={form.processing}>
                                    {form.processing ? <Spinner size="sm" className="mr-2" /> : <Save className="mr-2 h-4 w-4" />}
                                    {form.isDirty || !facture
                                        ? t('factures', 'editeur.enregistrer', 'Enregistrer le brouillon')
                                        : t('factures', 'editeur.enregistre', 'Enregistré')}
                                </Button>
                                {can.emettre && (
                                    <Button
                                        className="bg-secondary hover:bg-secondary/90"
                                        onClick={() => setDialogue('emettre')}
                                        data-tour="factures-edit-emit"
                                    >
                                        <Stamp className="mr-2 h-4 w-4" />
                                        {t('factures', 'editeur.emettre', 'Émettre')}
                                    </Button>
                                )}
                            </>
                        )}

                        {lectureSeule && facture && (
                            <>
                                <Button variant="outline" asChild>
                                    <a href={route('factures.pdf', { id: facture.id, telecharger: 1 })}>
                                        <Download className="mr-2 h-4 w-4" />
                                        {t('factures', 'editeur.telecharger', 'Télécharger le PDF')}
                                    </a>
                                </Button>
                                {can.emettre && facture.statut !== 'annulee' && facture.statut !== 'convertie' && (
                                    <Button variant="outline" onClick={() => setDialogue('envoyer')}>
                                        <Send className="mr-2 h-4 w-4" />
                                        {t('factures', 'editeur.envoyer', 'Envoyer')}
                                    </Button>
                                )}
                                {can.emettre && facture.type === 'facture' && facture.statut === 'emise' && (
                                    <Button className="bg-secondary hover:bg-secondary/90" onClick={() => setDialogue('payer')}>
                                        <CreditCard className="mr-2 h-4 w-4" />
                                        {t('factures', 'editeur.marquer_payee', 'Marquer payée')}
                                    </Button>
                                )}
                                {can.create && facture.type === 'proforma' && facture.statut === 'emise' && (
                                    <Button className="bg-secondary hover:bg-secondary/90" onClick={() => action('convertir')}>
                                        <Receipt className="mr-2 h-4 w-4" />
                                        {t('factures', 'editeur.convertir', 'Convertir en facture')}
                                    </Button>
                                )}
                                {/* Montants ou lignes à changer sur une facture émise non payée :
                                    avoir total émis + brouillon de remplacement, en un geste. */}
                                {can.emettre && can.create && facture.type === 'facture' && facture.statut === 'emise' && (
                                    <Button variant="outline" onClick={() => setDialogue('corriger')} data-tour="factures-edit-corriger">
                                        <PencilLine className="mr-2 h-4 w-4" />
                                        {t('factures', 'editeur.corriger', 'Corriger')}
                                    </Button>
                                )}
                            </>
                        )}

                        {facture && (
                            <DropdownMenu modal={false}>
                                <DropdownMenuTrigger asChild>
                                    <Button variant="ghost" size="icon" aria-label={t('factures', 'editeur.plus', "Plus d'actions")}>
                                        <MoreHorizontal className="h-4 w-4" />
                                    </Button>
                                </DropdownMenuTrigger>
                                <DropdownMenuContent align="end" className="w-60">
                                    {!lectureSeule && (
                                        <DropdownMenuItem asChild>
                                            <a href={route('factures.pdf', facture.id)} target="_blank" rel="noreferrer">
                                                <FileText className="mr-2 h-4 w-4" />
                                                {t('factures', 'editeur.pdf_brouillon', 'Voir le PDF (brouillon)')}
                                            </a>
                                        </DropdownMenuItem>
                                    )}
                                    {can.create && lectureSeule && ['emise', 'payee'].includes(facture.statut) && (
                                        <DropdownMenuItem onSelect={() => setDialogue('coordonnees')}>
                                            <MapPin className="mr-2 h-4 w-4" />
                                            {t('factures', 'editeur.modifier_coordonnees', 'Modifier adresse, e-mail, notes, échéance')}
                                        </DropdownMenuItem>
                                    )}
                                    {can.create && (
                                        <DropdownMenuItem onSelect={() => action('dupliquer')}>
                                            <Copy className="mr-2 h-4 w-4" />
                                            {t('factures', 'editeur.dupliquer', 'Dupliquer')}
                                        </DropdownMenuItem>
                                    )}
                                    {can.create && facture.type === 'facture' && ['emise', 'payee'].includes(facture.statut) && (
                                        <DropdownMenuItem onSelect={() => setDialogue('avoir')}>
                                            <RotateCcw className="mr-2 h-4 w-4" />
                                            {t('factures', 'editeur.creer_avoir', 'Créer un avoir')}
                                        </DropdownMenuItem>
                                    )}
                                    {can.emettre && facture.statut === 'payee' && (
                                        <DropdownMenuItem onSelect={() => setDialogue('impayer')}>
                                            <Undo2 className="mr-2 h-4 w-4" />
                                            {t('factures', 'editeur.annuler_paiement', 'Annuler le paiement')}
                                        </DropdownMenuItem>
                                    )}
                                    {facture.document_source && (
                                        <DropdownMenuItem asChild>
                                            <a href={route('factures.source', facture.id)} target="_blank" rel="noreferrer">
                                                <FileInput className="mr-2 h-4 w-4" />
                                                {t('factures', 'editeur.voir_source', "Voir le PDF d'origine")}
                                            </a>
                                        </DropdownMenuItem>
                                    )}
                                    {!lectureSeule && can.delete && (
                                        <>
                                            <DropdownMenuSeparator />
                                            <DropdownMenuItem className="text-rose-600 focus:text-rose-700" onSelect={() => setDialogue('supprimer')}>
                                                <Trash2 className="mr-2 h-4 w-4" />
                                                {t('factures', 'editeur.supprimer', 'Supprimer le brouillon')}
                                            </DropdownMenuItem>
                                        </>
                                    )}
                                </DropdownMenuContent>
                            </DropdownMenu>
                        )}
                    </div>
                </div>

                {/* ── Bandeaux d'information ──────────────────────────────── */}
                {facture && lectureSeule && <BandeauEmise facture={facture} />}
                {contexte && !lectureSeule && (
                    <BandeauContexte
                        contexte={contexte}
                        devise={data.devise}
                        factureId={facture?.id ?? null}
                        netCalcule={totaux?.total_ttc ?? null}
                    />
                )}

                {/* Bascule saisie / aperçu, sous 1280 px seulement */}
                {!lectureSeule && (
                    <ToggleGroup
                        type="single"
                        value={vue}
                        onValueChange={(v) => v && setVue(v as 'saisie' | 'apercu')}
                        variant="outline"
                        className="justify-start xl:hidden"
                    >
                        <ToggleGroupItem value="saisie" className="px-4">
                            {t('factures', 'editeur.vue_saisie', 'Saisie')}
                        </ToggleGroupItem>
                        <ToggleGroupItem value="apercu" className="px-4">
                            {t('factures', 'editeur.vue_apercu', 'Aperçu')}
                        </ToggleGroupItem>
                    </ToggleGroup>
                )}

                <div
                    className={cn(
                        'grid min-w-0 gap-4',
                        lectureSeule ? 'lg:grid-cols-[minmax(0,340px)_minmax(0,1fr)]' : 'xl:grid-cols-[minmax(0,1.15fr)_minmax(0,0.85fr)]',
                    )}
                >
                    {/* ── Colonne de saisie ─────────────────────────────── */}
                    {lectureSeule && facture ? (
                        <Resume data={data} totaux={totaux} facture={facture} />
                    ) : (
                        <div className={cn('flex min-w-0 flex-col gap-4', vue === 'apercu' && 'hidden xl:flex')}>
                            <Carte titre={t('factures', 'editeur.carte_document', 'Document')} tour="factures-edit-document">
                                <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
                                    <div className="grid gap-1.5 sm:col-span-2">
                                        <Label>{t('factures', 'editeur.type', 'Type')}</Label>
                                        {data.type === 'avoir' ? (
                                            <p className="flex h-9 items-center text-sm font-medium">
                                                {t('factures', 'editeur.type_avoir', "Facture d'avoir")}
                                            </p>
                                        ) : (
                                            <ToggleGroup
                                                type="single"
                                                value={data.type}
                                                onValueChange={(v) => v && setData('type', v as FactureForm['type'])}
                                                variant="outline"
                                                disabled={!modifiable}
                                                className="justify-start"
                                            >
                                                <ToggleGroupItem
                                                    value="facture"
                                                    className="data-[state=on]:bg-secondary px-4 data-[state=on]:text-white"
                                                >
                                                    {t('factures', 'editeur.type_facture', 'Facture')}
                                                </ToggleGroupItem>
                                                <ToggleGroupItem
                                                    value="proforma"
                                                    className="data-[state=on]:bg-secondary px-4 data-[state=on]:text-white"
                                                >
                                                    {t('factures', 'editeur.type_proforma', 'Proforma')}
                                                </ToggleGroupItem>
                                            </ToggleGroup>
                                        )}
                                        {data.type === 'proforma' && (
                                            <p className="text-muted-foreground text-xs">
                                                {t(
                                                    'factures',
                                                    'editeur.proforma_aide',
                                                    'Sans valeur fiscale, série PRO-. Convertible en facture une fois acceptée.',
                                                )}
                                            </p>
                                        )}
                                    </div>
                                    <div className="grid gap-1.5">
                                        <Label>{t('factures', 'editeur.date', 'Date')}</Label>
                                        {/* Une facture ne se date pas dans le futur : le calendrier ne propose pas ces jours (l'émission le refuse aussi). */}
                                        <FrDateInput
                                            id="date_facture"
                                            value={data.date_facture}
                                            onChange={(iso) => setData('date_facture', iso)}
                                            max={toLocalIso(new Date())}
                                            disabled={!modifiable}
                                        />
                                        {erreurs.date_facture && <p className="text-xs text-rose-600">{erreurs.date_facture}</p>}
                                    </div>
                                    <div className="grid gap-1.5">
                                        <Label>{t('factures', 'editeur.devise', 'Devise')}</Label>
                                        <Select
                                            value={data.devise}
                                            disabled={!modifiable}
                                            onValueChange={(v) => {
                                                const devise = v as FactureForm['devise'];
                                                form.setData((d) => ({ ...d, devise, timbre_fiscal: devise === 'TND' ? d.timbre_fiscal : false }));
                                            }}
                                        >
                                            <SelectTrigger>
                                                <SelectValue />
                                            </SelectTrigger>
                                            <SelectContent>
                                                {Object.entries(options.devises).map(([code, conf]) => (
                                                    <SelectItem key={code} value={code}>
                                                        {code} · {conf.symbole}
                                                    </SelectItem>
                                                ))}
                                            </SelectContent>
                                        </Select>
                                    </div>
                                    {data.type === 'facture' && (
                                        <div className="grid gap-1.5 sm:col-span-2">
                                            <Label>{t('factures', 'editeur.echeance', 'Échéance')}</Label>
                                            <Select
                                                value={String(data.echeance_jours)}
                                                disabled={!modifiable}
                                                onValueChange={(v) => setData('echeance_jours', Number(v))}
                                            >
                                                <SelectTrigger>
                                                    <SelectValue />
                                                </SelectTrigger>
                                                <SelectContent>
                                                    {options.echeances.map((j) => (
                                                        <SelectItem key={j} value={String(j)}>
                                                            {j === 0 ? t('factures', 'editeur.a_reception', 'À réception') : `${j} jours`}
                                                        </SelectItem>
                                                    ))}
                                                </SelectContent>
                                            </Select>
                                        </div>
                                    )}
                                    <div className="flex items-end gap-2 pb-2 sm:col-span-2">
                                        <Switch
                                            id="timbre"
                                            checked={data.timbre_fiscal}
                                            disabled={!modifiable || data.devise !== 'TND'}
                                            onCheckedChange={(v) => setData('timbre_fiscal', v)}
                                        />
                                        <Label htmlFor="timbre" className="text-sm font-normal">
                                            {t('factures', 'editeur.timbre', 'Timbre fiscal')} ({fmtMontant(options.timbre, 'TND')})
                                            {data.devise !== 'TND' && (
                                                <span className="text-muted-foreground ml-1 text-xs">
                                                    {t('factures', 'editeur.timbre_tnd', '— factures en dinars seulement')}
                                                </span>
                                            )}
                                        </Label>
                                    </div>
                                </div>
                            </Carte>

                            <Carte titre={t('factures', 'editeur.carte_client', 'Client')} tour="factures-edit-client">
                                <ClientField
                                    valeur={data}
                                    disabled={!modifiable}
                                    erreurs={{
                                        client_nom: erreurs.client_nom,
                                        client_email: erreurs.client_email,
                                        client_adresse: erreurs.client_adresse,
                                        client_matricule_fiscal: erreurs.client_matricule_fiscal,
                                    }}
                                    onChange={(champs) => form.setData((d) => ({ ...d, ...champs }))}
                                />
                            </Carte>

                            <Carte titre={t('factures', 'editeur.carte_lignes', 'Lignes')} tour="factures-edit-lines">
                                <LinesEditor
                                    lignes={data.lignes}
                                    onChange={(lignes) => setData('lignes', lignes)}
                                    totaux={totaux?.lignes ?? null}
                                    devise={data.devise}
                                    options={options}
                                    erreurs={erreurs}
                                    disabled={!modifiable}
                                />
                            </Carte>

                            <div className="grid gap-4 lg:grid-cols-2">
                                <Carte titre={t('factures', 'editeur.carte_notes', 'Notes')}>
                                    <div className="grid gap-1.5">
                                        <Label htmlFor="note_entete">{t('factures', 'editeur.note_entete', 'Sous la date')}</Label>
                                        <Textarea
                                            id="note_entete"
                                            rows={2}
                                            value={data.note_entete ?? ''}
                                            disabled={!modifiable}
                                            onChange={(e) => setData('note_entete', e.target.value || null)}
                                            placeholder={'Montant Airbnb : 5 134,78 $US\nnotre commission'}
                                        />
                                    </div>
                                    <div className="grid gap-1.5">
                                        <Label htmlFor="note_pied">{t('factures', 'editeur.note_pied', 'Bas de facture')}</Label>
                                        <Textarea
                                            id="note_pied"
                                            rows={2}
                                            value={data.note_pied ?? ''}
                                            disabled={!modifiable}
                                            onChange={(e) => setData('note_pied', e.target.value || null)}
                                            placeholder={t('factures', 'editeur.note_pied_exemple', 'Conditions de paiement, référence de commande…')}
                                        />
                                    </div>
                                    {/* Fondement légal d'une ligne à 0 % de TVA : n'apparaît que lorsqu'il est exigé (ou déjà rempli). */}
                                    {(data.lignes.some((l) => Number(l.taux_tva) === 0) || data.mention_exoneration) && (
                                        <div className="grid gap-1.5" data-tour="factures-edit-exoneration">
                                            <Label htmlFor="mention_exoneration">
                                                {t('factures', 'editeur.mention_exoneration', 'Exonération de TVA — fondement légal')}
                                            </Label>
                                            <Input
                                                id="mention_exoneration"
                                                value={data.mention_exoneration ?? ''}
                                                disabled={!modifiable}
                                                maxLength={255}
                                                onChange={(e) => setData('mention_exoneration', e.target.value || null)}
                                                placeholder={t(
                                                    'factures',
                                                    'editeur.mention_exoneration_exemple',
                                                    'Ex. : Exportation de services — attestation n° … du …',
                                                )}
                                            />
                                            {erreurs.mention_exoneration && <p className="text-xs text-rose-600">{erreurs.mention_exoneration}</p>}
                                        </div>
                                    )}
                                </Carte>
                                <Totaux totaux={totaux} devise={data.devise} chargement={chargement} />
                            </div>
                        </div>
                    )}

                    {/* ── Aperçu ─────────────────────────────────────────── */}
                    <div
                        className={cn('min-w-0', !lectureSeule && vue === 'saisie' && 'hidden xl:block', 'xl:sticky xl:top-4 xl:self-start')}
                        data-tour="factures-edit-preview"
                    >
                        <InvoicePreview html={apercu?.html ?? null} chargement={chargement} pause={pause} />
                    </div>
                </div>
            </div>

            <EmitDialog
                open={dialogue === 'emettre'}
                onOpenChange={(o) => setDialogue(o ? 'emettre' : null)}
                type={data.type}
                numero={prochains[data.type]}
                verifications={verifications}
                enCours={emission || form.processing}
                onConfirmer={emettre}
                onAller={allerAuChamp}
            />
            {facture && facture.numero && (
                <>
                    <SendDialog
                        open={dialogue === 'envoyer'}
                        onOpenChange={(o) => setDialogue(o ? 'envoyer' : null)}
                        factureId={facture.id}
                        numero={facture.numero}
                        emailParDefaut={facture.client_email}
                    />
                    <PaymentDialog
                        open={dialogue === 'payer'}
                        onOpenChange={(o) => setDialogue(o ? 'payer' : null)}
                        factureId={facture.id}
                        numero={facture.numero}
                        montant={facture.total_ttc}
                        devise={facture.devise}
                    />
                    <ConfirmDialog
                        open={dialogue === 'avoir'}
                        onOpenChange={(o) => setDialogue(o ? 'avoir' : null)}
                        titre={t('factures', 'editeur.avoir_titre', 'Créer un avoir sur {numero} ?').replace('{numero}', facture.numero)}
                        texte={t(
                            'factures',
                            'editeur.avoir_texte',
                            "Un brouillon d'avoir reprend toutes les lignes. Retirez celles à ne pas rembourser, puis émettez-le. Un avoir du montant total annule la facture ; un avoir partiel la laisse en place.",
                        )}
                        action={t('factures', 'editeur.creer_avoir', 'Créer un avoir')}
                        onConfirmer={() => action('avoir')}
                    />
                    <ConfirmDialog
                        open={dialogue === 'impayer'}
                        onOpenChange={(o) => setDialogue(o ? 'impayer' : null)}
                        titre={t('factures', 'editeur.impayer_titre', 'Annuler le paiement de {numero} ?').replace('{numero}', facture.numero)}
                        texte={t(
                            'factures',
                            'editeur.impayer_texte',
                            'La facture repasse « non payée ». À utiliser pour corriger un paiement enregistré par erreur.',
                        )}
                        action={t('factures', 'editeur.annuler_paiement', 'Annuler le paiement')}
                        danger
                        onConfirmer={() => router.post(route('factures.paiement', facture.id), { payee_le: null }, { preserveScroll: true })}
                    />
                    <ConfirmDialog
                        open={dialogue === 'corriger'}
                        onOpenChange={(o) => setDialogue(o ? 'corriger' : null)}
                        titre={t('factures', 'editeur.corriger_titre', 'Corriger {numero} ?').replace('{numero}', facture.numero)}
                        texte={t(
                            'factures',
                            'editeur.corriger_texte',
                            "Un avoir du montant total est émis tout de suite ({avoir}) : {numero} est annulée et reste dans la série. Un brouillon identique s'ouvre : corrigez-le, puis émettez-le — il recevra un nouveau numéro. L'avoir émis ne s'efface pas.",
                        )
                            .replace('{avoir}', prochains.avoir)
                            .replace('{numero}', facture.numero)}
                        action={t('factures', 'editeur.corriger_action', "Émettre l'avoir et corriger")}
                        danger
                        onConfirmer={() => router.post(route('factures.corriger', facture.id))}
                    />
                    <CoordonneesDialog
                        open={dialogue === 'coordonnees'}
                        onOpenChange={(o) => setDialogue(o ? 'coordonnees' : null)}
                        factureId={facture.id}
                        numero={facture.numero}
                        avecEcheance={facture.type === 'facture'}
                        valeurs={coordonnees}
                    />
                </>
            )}
            {facture && !lectureSeule && (
                <ConfirmDialog
                    open={dialogue === 'supprimer'}
                    onOpenChange={(o) => setDialogue(o ? 'supprimer' : null)}
                    titre={t('factures', 'editeur.supprimer_titre', 'Supprimer ce brouillon ?')}
                    texte={t('factures', 'editeur.supprimer_texte', "Il n'a pas de numéro : le supprimer ne laisse aucun trou dans la série.")}
                    action={t('factures', 'editeur.supprimer', 'Supprimer le brouillon')}
                    danger
                    onConfirmer={() => {
                        enregistrement.current = true;
                        router.delete(route('factures.destroy', facture.id));
                    }}
                />
            )}
        </AppLayout>
    );
}

// ─────────────────────────────────────────────────────────────────────────────

function Carte({ titre, tour, children }: { titre: string; tour?: string; children: React.ReactNode }) {
    return (
        <section className="bg-card flex min-w-0 flex-col gap-3 rounded-xl border p-4 shadow-sm" data-tour={tour}>
            <h2 className="text-muted-foreground text-xs font-semibold tracking-wider uppercase">{titre}</h2>
            {children}
        </section>
    );
}

function Totaux({ totaux, devise, chargement }: { totaux: Apercu['totaux'] | null; devise: FactureForm['devise']; chargement: boolean }) {
    const { t } = useTranslation();

    return (
        <section className="bg-card flex flex-col gap-2 rounded-xl border p-4 shadow-sm" data-tour="factures-edit-totals">
            <h2 className="text-muted-foreground flex items-center justify-between text-xs font-semibold tracking-wider uppercase">
                {t('factures', 'editeur.carte_totaux', 'Totaux')}
                {chargement && <Spinner size="sm" />}
            </h2>
            {totaux ? (
                <div className="grid gap-1.5 text-sm tabular-nums">
                    <Ligne libelle={t('factures', 'editeur.total_ht', 'Total HT')} valeur={fmtMontant(totaux.total_ht, devise)} />
                    {totaux.total_remise > 0 && (
                        <Ligne
                            libelle={t('factures', 'editeur.dont_remise', 'dont remise')}
                            valeur={fmtMontant(totaux.total_remise, devise)}
                            discret
                        />
                    )}
                    {totaux.tva.map((g) => (
                        <Ligne key={g.taux} libelle={`TVA ${g.taux} % sur ${fmtMontant(g.base, devise)}`} valeur={fmtMontant(g.montant, devise)} />
                    ))}
                    {totaux.montant_timbre > 0 && (
                        <>
                            {/* Sans timbre, le TTC est le net : la ligne ferait doublon. */}
                            <Ligne
                                libelle={t('factures', 'editeur.total_ttc', 'Total TTC')}
                                valeur={fmtMontant(totaux.total_ttc - totaux.montant_timbre, devise)}
                            />
                            <Ligne libelle={t('factures', 'editeur.timbre', 'Timbre fiscal')} valeur={fmtMontant(totaux.montant_timbre, devise)} />
                        </>
                    )}
                    <div className="mt-1 flex items-baseline justify-between gap-3 border-t pt-2">
                        <span className="font-medium">{t('factures', 'editeur.net', 'Net à payer')}</span>
                        <span className="text-primary text-xl font-semibold">{fmtMontant(totaux.total_ttc, devise)}</span>
                    </div>
                    <p className="text-muted-foreground text-xs italic">{totaux.lettres}</p>
                </div>
            ) : (
                <p className="text-muted-foreground text-sm">{t('factures', 'editeur.calcul', 'Calcul…')}</p>
            )}
        </section>
    );
}

function Ligne({ libelle, valeur, discret }: { libelle: string; valeur: string; discret?: boolean }) {
    return (
        <div className={cn('flex justify-between gap-3', discret && 'text-muted-foreground')}>
            <span>{libelle}</span>
            <span>{valeur}</span>
        </div>
    );
}

/** Colonne de gauche d'une pièce émise : l'essentiel, sans champs de saisie. */
function Resume({
    data,
    totaux,
    facture,
}: {
    data: FactureForm;
    totaux: Apercu['totaux'] | null;
    facture: NonNullable<FactureEditProps['facture']>;
}) {
    const { t } = useTranslation();

    return (
        <div className="flex min-w-0 flex-col gap-4">
            <Carte titre={t('factures', 'editeur.carte_client', 'Client')}>
                <div className="text-sm">
                    <p className="font-medium">{data.client_nom}</p>
                    {data.client_adresse && <p className="text-muted-foreground whitespace-pre-line">{data.client_adresse}</p>}
                    {data.client_matricule_fiscal && <p className="text-muted-foreground">MF {data.client_matricule_fiscal}</p>}
                    {data.client_email && <p className="text-muted-foreground">{data.client_email}</p>}
                </div>
            </Carte>
            <Carte titre={t('factures', 'editeur.carte_suivi', 'Suivi')}>
                <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5 text-sm">
                    <dt className="text-muted-foreground">{t('factures', 'editeur.date', 'Date')}</dt>
                    <dd className="tabular-nums">{fmtDate(data.date_facture)}</dd>
                    {facture.type === 'facture' && (
                        <>
                            <dt className="text-muted-foreground">{t('factures', 'editeur.echeance', 'Échéance')}</dt>
                            <dd className={cn('tabular-nums', facture.en_retard && 'font-medium text-rose-700')}>
                                {data.echeance_jours === 0 ? t('factures', 'editeur.a_reception', 'À réception') : fmtDate(echeanceDe(data))}
                            </dd>
                        </>
                    )}
                    <dt className="text-muted-foreground">{t('factures', 'editeur.emise', 'Émise')}</dt>
                    <dd className="tabular-nums">{fmtDateHeure(facture.emise_le)}</dd>
                    <dt className="text-muted-foreground">{t('factures', 'editeur.envoyee', 'Envoyée')}</dt>
                    <dd className="tabular-nums">{facture.envoyee_le ? `${fmtDateHeure(facture.envoyee_le)} · ${facture.envoyee_a}` : '—'}</dd>
                    {facture.type === 'facture' && (
                        <>
                            <dt className="text-muted-foreground">{t('factures', 'editeur.payee', 'Payée')}</dt>
                            <dd className="tabular-nums">{facture.payee_le ? fmtDate(facture.payee_le) : '—'}</dd>
                        </>
                    )}
                </dl>
            </Carte>
            <Totaux totaux={totaux} devise={data.devise} chargement={false} />
        </div>
    );
}

function echeanceDe(data: FactureForm): string {
    const d = new Date(`${data.date_facture}T00:00:00`);
    d.setDate(d.getDate() + data.echeance_jours);
    const p = (n: number) => String(n).padStart(2, '0');
    return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
}

function BandeauEmise({ facture }: { facture: NonNullable<FactureEditProps['facture']> }) {
    const { t } = useTranslation();

    return (
        <div className="flex flex-wrap items-start gap-2 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2.5 text-sm text-emerald-900">
            <BadgeCheck className="mt-0.5 h-4 w-4 shrink-0" />
            <div className="min-w-0 flex-1">
                <p>
                    {facture.statut === 'annulee'
                        ? t('factures', 'editeur.bandeau_annulee', "Annulée par un avoir — elle reste dans la série, barrée, comme l'exige la loi.")
                        : facture.statut === 'convertie'
                          ? t('factures', 'editeur.bandeau_convertie', 'Proforma convertie en facture.')
                          : t(
                                'factures',
                                'editeur.bandeau_emise',
                                'Émise le {date} — le contenu est figé. Une correction passe par un avoir.',
                            ).replace('{date}', fmtDateHeure(facture.emise_le))}
                </p>
                {(facture.facture_origine || facture.derivees.length > 0) && (
                    <p className="mt-1 flex flex-wrap gap-x-3 gap-y-1">
                        {facture.facture_origine && (
                            <Link href={route('factures.edit', facture.facture_origine.id)} className="underline underline-offset-2">
                                {facture.type === 'avoir' ? 'Sur la facture' : 'Depuis la proforma'} {facture.facture_origine.numero}
                            </Link>
                        )}
                        {facture.derivees.map((d) => (
                            <Link key={d.id} href={route('factures.edit', d.id)} className="underline underline-offset-2">
                                {TYPE_LIBELLE[d.type]} {d.numero ?? '(brouillon)'}
                            </Link>
                        ))}
                    </p>
                )}
            </div>
        </div>
    );
}

function BandeauContexte({
    contexte,
    devise,
    factureId,
    netCalcule,
}: {
    contexte: ContexteEditeur;
    devise: FactureForm['devise'];
    factureId: number | null;
    netCalcule: number | null;
}) {
    const { t } = useTranslation();

    if (contexte.type === 'reservation') {
        return (
            <div className="grid gap-2">
                {contexte.statut_reservation && (
                    <div className="flex items-start gap-2 rounded-lg border border-rose-200 bg-rose-50 px-3 py-2.5 text-sm text-rose-900">
                        <CircleAlert className="mt-0.5 h-4 w-4 shrink-0" />
                        <p>
                            {(contexte.statut_reservation === 'annulee'
                                ? t('factures', 'editeur.reservation_annulee', 'La réservation {code} est annulée : elle ne se facture pas.')
                                : t(
                                      'factures',
                                      'editeur.reservation_demande',
                                      "La réservation {code} n'est encore qu'une demande : elle se facturera une fois confirmée.",
                                  )
                            ).replace('{code}', contexte.reservation)}
                        </p>
                    </div>
                )}
                <div className="bg-muted/40 flex items-start gap-2 rounded-lg border px-3 py-2.5 text-sm">
                    <Info className="text-muted-foreground mt-0.5 h-4 w-4 shrink-0" />
                    <div className="min-w-0 flex-1">
                        <p>
                            {t('factures', 'editeur.depuis_reservation', 'Pré-remplie depuis la réservation {code}.').replace(
                                '{code}',
                                contexte.reservation,
                            )}{' '}
                            {/* Le total de la réservation, tel quel, dans sa devise : aucun taux de change.
                                Tout reste modifiable à la main — devise comprise, sans conversion. */}
                            {t(
                                'factures',
                                'editeur.total_reservation',
                                'Net à payer = le montant total de la réservation, {montant}, sans taux de change ; le HT en est déduit.',
                            ).replace('{montant}', fmtMontant(contexte.montant_sejour, contexte.devise))}
                        </p>
                        {contexte.devise_origine && (
                            <p className="mt-1 text-amber-800">
                                {t(
                                    'factures',
                                    'editeur.devise_hors_liste',
                                    'Réservation en {devise}, devise que les factures ne gèrent pas : son total est repris converti en dinars.',
                                ).replace('{devise}', contexte.devise_origine)}
                            </p>
                        )}
                        {contexte.prolongations_tnd !== null && (
                            <p className="mt-1 text-amber-800">
                                {t(
                                    'factures',
                                    'editeur.prolongation_non_incluse',
                                    'Prolongation de {montant} non incluse : elle est chiffrée en dinars, et la reprendre dans une facture en {devise} demanderait un taux de change. Ajoutez une ligne à la main si besoin.',
                                )
                                    .replace('{montant}', fmtMontant(contexte.prolongations_tnd, 'TND'))
                                    .replace('{devise}', contexte.devise)}
                            </p>
                        )}
                        {netCalcule !== null && devise === contexte.devise && Math.abs(netCalcule - contexte.montant_sejour) >= 0.0005 && (
                            <p className="mt-1 text-amber-800">
                                {t(
                                    'factures',
                                    'editeur.ecart_arrondi',
                                    "Le séjour vaut {sejour} ; la TVA arrondie au centime donne {net}. Aucun prix HT au centime ne retombe exactement dessus — ajustez le prix si vous préférez l'autre centime.",
                                )
                                    .replace('{sejour}', fmtMontant(contexte.montant_sejour, devise))
                                    .replace('{net}', fmtMontant(netCalcule, devise))}
                            </p>
                        )}
                    </div>
                </div>
                {contexte.deja_facturee.length > 0 && (
                    <div className="flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2.5 text-sm text-amber-900">
                        <CircleAlert className="mt-0.5 h-4 w-4 shrink-0" />
                        <p>
                            {t('factures', 'editeur.deja_facturee', 'Ce séjour a déjà une facture :')}{' '}
                            {contexte.deja_facturee.map((f, i) => (
                                <span key={f.id}>
                                    {i > 0 && ', '}
                                    <Link href={route('factures.edit', f.id)} className="font-medium underline underline-offset-2">
                                        {f.numero ?? 'brouillon'}
                                    </Link>
                                </span>
                            ))}
                            .
                            {contexte.deja_facturee.some((f) => f.numero !== null) && (
                                <>
                                    {' '}
                                    {t(
                                        'factures',
                                        'editeur.deja_facturee_bloque',
                                        'Une seconde facture ne pourra pas être émise : pour modifier le séjour facturé, corrigez la facture existante (avoir puis nouvelle facture).',
                                    )}
                                </>
                            )}
                        </p>
                    </div>
                )}
            </div>
        );
    }

    const ecarts = contexte.controles.filter((c) => c.ok === false);

    return (
        <div
            className={cn(
                'rounded-lg border px-3 py-2.5 text-sm',
                ecarts.length ? 'border-amber-200 bg-amber-50 text-amber-900' : 'border-emerald-200 bg-emerald-50 text-emerald-900',
            )}
        >
            <p className="flex flex-wrap items-center gap-x-2">
                <FileInput className="h-4 w-4" />
                <span>
                    {t('factures', 'editeur.lu_dans', 'Lu dans « {fichier} »').replace('{fichier}', contexte.fichier)}
                    {contexte.numero_source && ` (${contexte.numero_source}${contexte.date_source ? ` du ${fmtDate(contexte.date_source)}` : ''})`}.
                </span>
                {factureId && (
                    <a href={route('factures.source', factureId)} target="_blank" rel="noreferrer" className="underline underline-offset-2">
                        {t('factures', 'editeur.voir_source', "Voir le PDF d'origine")}
                    </a>
                )}
            </p>
            <ul className="mt-1.5 flex flex-wrap gap-x-4 gap-y-1">
                {contexte.controles.map((c) => (
                    <li key={c.libelle} className="flex items-center gap-1 tabular-nums">
                        {c.ok === false ? <CircleAlert className="h-3.5 w-3.5" /> : <BadgeCheck className="h-3.5 w-3.5" />}
                        {c.libelle === 'Lignes lues'
                            ? `${c.calcule} lignes lues`
                            : c.document === null
                              ? `${c.libelle} ${fmtMontant(c.calcule, devise)} (absent du document)`
                              : c.ok
                                ? `${c.libelle} ${fmtMontant(c.calcule, devise)} = document`
                                : `${c.libelle} ${fmtMontant(c.calcule, devise)} ≠ ${fmtMontant(c.document, devise)} sur le document`}
                    </li>
                ))}
            </ul>
            {ecarts.length > 0 && (
                <p className="mt-1.5">
                    {t('factures', 'editeur.ecart', "Un total ne retombe pas juste : comparez les lignes avec le PDF d'origine avant d'émettre.")}
                </p>
            )}
        </div>
    );
}
