import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { useTranslation } from '@/hooks/use-translation';
import { apiFetch } from '@/lib/loading/api-fetch';
import { router } from '@inertiajs/react';
import { AlertTriangle, ArrowDownToLine, Check, RefreshCcw } from 'lucide-react';
import * as React from 'react';
import { toast } from 'sonner';

/** Le rapport rendu par `BeneficiaireController::rapprochementTej()`. */
interface Rapport {
    ok: boolean;
    lecture_complete?: boolean;
    pages_lues?: number;
    arret?: string;
    lus_chez_tej?: number;
    beneficiaires_manager?: number;
    rapproches?: number;
    poses?: number;
    orphelins_tej?: { id_tej: number | null; identifiant: string; type: string; raison_sociale: string }[];
    orphelins_manager?: { beneficiaire_id: number; raison_sociale: string; matricule_fiscal: string; cin: string }[];
    divergences?: { beneficiaire_id: number; champ: string; manager: string; tej: string }[];
    ambiguites?: { identifiant: string; raison_sociale: string; motif: string }[];
    erreur?: string | null;
}

interface Props {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    /** `fournisseur.create` — sans lui, pas d'import. */
    peutImporter: boolean;
}

/**
 * RAPPROCHEMENT avec TEJ, depuis l'écran.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * TROIS ÉTATS, ET UN SEUL À LA FOIS
 * ═════════════════════════════════════════════════════════════════════════════
 *   au repos ...... on explique ce que le bouton va faire, avant de le faire ;
 *   en cours ...... la lecture parcourt la liste page par page, ça prend du temps ;
 *   le rapport .... quatre familles, chacune avec ce qu'elle appelle comme geste.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * CE QUE L'IMPORT N'ENVOIE PAS
 * ═════════════════════════════════════════════════════════════════════════════
 * Rien. Le bouton poste un corps VIDE : c'est le serveur qui relit la liste chez
 * TEJ et crée à partir d'elle. Renvoyer les orphelins affichés serait laisser le
 * navigateur dicter des identités fiscales — qui repartiraient ensuite chez la
 * DGI à la première modification.
 *
 * Après un import, la page est rechargée : la liste des fournisseurs a changé.
 */
export function TejRapprochementDialog({ open, onOpenChange, peutImporter }: Props) {
    const { t } = useTranslation();

    const [rapport, setRapport] = React.useState<Rapport | null>(null);
    const [enCours, setEnCours] = React.useState(false);
    const [importEnCours, setImportEnCours] = React.useState(false);

    // Rouvrir repart d'une page blanche : un rapport de la fois d'avant serait
    // lu comme celui d'aujourd'hui.
    React.useEffect(() => {
        if (open) return;
        setRapport(null);
        setEnCours(false);
        setImportEnCours(false);
    }, [open]);

    const lancer = async () => {
        setEnCours(true);
        setRapport(null);

        try {
            const reponse = await apiFetch(route('fournisseurs.tej.rapprochement'), {
                method: 'POST',
                headers: { Accept: 'application/json' },
            });
            // Un corps non-JSON (session expirée, page d'erreur) ne doit jamais
            // casser l'écran.
            const corps: unknown = await reponse.json().catch(() => null);
            setRapport((corps as Rapport | null) ?? { ok: false, erreur: t('fournisseurs', 'tej_sync_illisible', 'Réponse illisible.') });
        } catch {
            setRapport({ ok: false, erreur: t('fournisseurs', 'tej_sync_reseau', 'TEJ n’a pas pu être joint.') });
        } finally {
            setEnCours(false);
        }
    };

    const importer = async () => {
        setImportEnCours(true);

        try {
            const reponse = await apiFetch(route('fournisseurs.tej.importer'), {
                method: 'POST',
                headers: { Accept: 'application/json' },
            });
            const corps = (await reponse.json().catch(() => null)) as { ok?: boolean; crees?: number; raison?: string } | null;

            if (corps?.ok !== true) {
                toast.error(corps?.raison ?? t('fournisseurs', 'tej_import_echec', 'L’import n’a pas abouti.'));
                return;
            }

            toast.success(t('fournisseurs', 'tej_import_ok', ':n fiche(s) importée(s).').replace(':n', String(corps.crees ?? 0)));
            onOpenChange(false);
            router.reload();
        } catch {
            toast.error(t('fournisseurs', 'tej_import_echec', 'L’import n’a pas abouti.'));
        } finally {
            setImportEnCours(false);
        }
    };

    const orphelinsTej = rapport?.orphelins_tej ?? [];

    return (
        <Dialog open={open} onOpenChange={onOpenChange}>
            <DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-2xl" onInteractOutside={(e) => e.preventDefault()}>
                <DialogHeader>
                    <DialogTitle>{t('fournisseurs', 'tej_sync_title', 'Rapprochement avec TEJ')}</DialogTitle>
                    <DialogDescription>
                        {t(
                            'fournisseurs',
                            'tej_sync_desc',
                            'Lit la liste des bénéficiaires du déclarant chez la DGI et la confronte à la vôtre. Aucune identité n’est jamais écrasée : les écarts sont signalés, vous tranchez.',
                        )}
                    </DialogDescription>
                </DialogHeader>

                {rapport === null ? (
                    <p className="text-muted-foreground text-sm">
                        {enCours
                            ? t('fournisseurs', 'tej_sync_en_cours', 'Lecture de la liste, page par page…')
                            : t(
                                  'fournisseurs',
                                  'tej_sync_repos',
                                  'Rien n’a encore été lu. Le rapprochement pose le lien vers TEJ sur les fiches reconnues.',
                              )}
                    </p>
                ) : rapport.ok !== true ? (
                    <p className="flex items-start gap-2 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-900">
                        <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
                        {rapport.erreur ?? t('fournisseurs', 'tej_sync_echec', 'TEJ n’a pas répondu.')}
                    </p>
                ) : (
                    <div className="flex flex-col gap-3 text-sm">
                        {/* Une lecture partielle change la lecture de TOUT le
                            reste : ce qui « manque » peut être dans une page
                            jamais lue. On le dit avant les chiffres. */}
                        {rapport.lecture_complete !== true && (
                            <p className="flex items-start gap-2 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-900">
                                <AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" />
                                {t(
                                    'fournisseurs',
                                    'tej_sync_partielle',
                                    'Liste lue en partie seulement — ce qui suit ne porte que sur ce qui a été lu, et rien ne peut être importé.',
                                )}
                            </p>
                        )}

                        <div className="grid grid-cols-2 gap-x-4 gap-y-1 rounded-md border px-3 py-2 sm:grid-cols-4">
                            <Chiffre libelle={t('fournisseurs', 'tej_sync_lus', 'Chez TEJ')} valeur={rapport.lus_chez_tej ?? 0} />
                            <Chiffre libelle={t('fournisseurs', 'tej_sync_ici', 'Ici')} valeur={rapport.beneficiaires_manager ?? 0} />
                            <Chiffre libelle={t('fournisseurs', 'tej_sync_rapproches', 'Rapprochés')} valeur={rapport.rapproches ?? 0} />
                            <Chiffre libelle={t('fournisseurs', 'tej_sync_poses', 'Liens posés')} valeur={rapport.poses ?? 0} />
                        </div>

                        <Famille
                            titre={t('fournisseurs', 'tej_sync_orphelins_tej', 'Connus de TEJ, absents d’ici')}
                            aide={t('fournisseurs', 'tej_sync_orphelins_tej_aide', 'Importables en un clic ci-dessous.')}
                            entrees={orphelinsTej.map((o) => `${o.raison_sociale} — ${o.identifiant}`)}
                        />
                        <Famille
                            titre={t('fournisseurs', 'tej_sync_orphelins_manager', 'Ici, inconnus de TEJ')}
                            aide={t('fournisseurs', 'tej_sync_orphelins_manager_aide', 'À créer chez TEJ avec « Envoyer vers TEJ ».')}
                            entrees={(rapport.orphelins_manager ?? []).map((o) => `${o.raison_sociale} — ${o.matricule_fiscal || o.cin || '—'}`)}
                        />
                        <Famille
                            titre={t('fournisseurs', 'tej_sync_divergences', 'Identités qui diffèrent')}
                            aide={t('fournisseurs', 'tej_sync_divergences_aide', 'Signalées, jamais corrigées — c’est vous qui tranchez.')}
                            entrees={(rapport.divergences ?? []).map(
                                (d) => `#${d.beneficiaire_id} · ${d.champ} : « ${d.manager} » ici, « ${d.tej} » chez TEJ`,
                            )}
                        />
                        <Famille
                            titre={t('fournisseurs', 'tej_sync_ambiguites', 'Rapprochements ambigus')}
                            aide={t('fournisseurs', 'tej_sync_ambiguites_aide', 'Laissés non rapprochés : un doute vaut mieux qu’un lien inventé.')}
                            entrees={(rapport.ambiguites ?? []).map((a) => `${a.raison_sociale} — ${a.identifiant} (${a.motif})`)}
                        />
                    </div>
                )}

                <DialogFooter>
                    <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
                        {t('fournisseurs', 'cancel', 'Fermer')}
                    </Button>

                    {/* L'import n'est proposé QUE s'il y a quelque chose à
                        importer ET que la liste a été lue en entier : sur une
                        lecture partielle, un « orphelin » peut simplement se
                        trouver dans une page non lue, et l'importer créerait un
                        doublon d'identifiant — donc un upsert qui écrase la
                        fiche de la DGI. Le serveur refuse de toute façon ; le
                        bouton ne doit pas promettre le contraire. */}
                    {peutImporter && rapport?.ok === true && rapport.lecture_complete === true && orphelinsTej.length > 0 && (
                        <Button type="button" variant="secondary" onClick={importer} disabled={importEnCours}>
                            <ArrowDownToLine className="mr-2 h-4 w-4" />
                            {t('fournisseurs', 'tej_sync_importer', 'Importer les :n manquantes').replace(':n', String(orphelinsTej.length))}
                        </Button>
                    )}

                    <Button type="button" onClick={lancer} disabled={enCours || importEnCours}>
                        <RefreshCcw className={`mr-2 h-4 w-4 ${enCours ? 'animate-spin' : ''}`} />
                        {rapport === null ? t('fournisseurs', 'tej_sync_lancer', 'Rapprocher') : t('fournisseurs', 'tej_sync_relancer', 'Relancer')}
                    </Button>
                </DialogFooter>
            </DialogContent>
        </Dialog>
    );
}

function Chiffre({ libelle, valeur }: { libelle: string; valeur: number }) {
    return (
        <span className="flex flex-col">
            <span className="text-muted-foreground text-xs">{libelle}</span>
            <span className="font-semibold tabular-nums">{valeur}</span>
        </span>
    );
}

/**
 * Une famille du rapport. Zéro entrée s'affiche AUSSI, en vert : « rien à
 * signaler » est une information, et une famille qui disparaît laisse croire
 * qu'elle n'a pas été regardée.
 */
function Famille({ titre, aide, entrees }: { titre: string; aide: string; entrees: string[] }) {
    if (entrees.length === 0) {
        return (
            <p className="flex items-center gap-2 text-xs text-emerald-700">
                <Check className="h-3.5 w-3.5 shrink-0" />
                {titre} — 0
            </p>
        );
    }

    return (
        <div className="flex flex-col gap-1 rounded-md border px-3 py-2">
            <span className="flex items-baseline justify-between gap-2">
                <span className="font-medium">
                    {titre} — {entrees.length}
                </span>
                <span className="text-muted-foreground text-xs">{aide}</span>
            </span>
            <ul className="text-muted-foreground max-h-40 overflow-y-auto text-xs">
                {entrees.map((entree, index) => (
                    <li key={index} className="truncate py-0.5">
                        {entree}
                    </li>
                ))}
            </ul>
        </div>
    );
}
