import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { useTranslation } from '@/hooks/use-translation';
import { apiFetch } from '@/lib/loading/api-fetch';
import { useForm } from '@inertiajs/react';
import { AlertTriangle, Search } from 'lucide-react';
import * as React from 'react';
import { FicheCreeeStep } from './fiche-creee-step';
import { identifiantTransportable, PROFIL_REGISTRE_VIDE, TYPE_IDENTIFIER_MATRICULE, type ProfilRegistreTej, type ReponseRegistreTej } from './tej';

interface Props {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    /**
     * Raccourci de la dernière étape — ABSENT ⇒ pas de dernière étape.
     *
     * Ce dialogue est aussi monté DANS la saisie d'une ligne de retenue, où
     * l'utilisateur est au milieu d'autre chose : l'interrompre avec une étape
     * d'envoi y serait une nuisance, pas un service. Seul /fournisseurs le
     * passe.
     */
    onEnvoyerTej?: (ficheId: number) => void;
}

/**
 * Trois états, et l'écran n'en montre qu'un à la fois.
 *
 * - `saisie` : un seul champ, le matricule. Rien d'autre n'est demandé, parce
 *   que rien d'autre n'est nécessaire tant qu'on n'a pas interrogé le registre.
 * - `profil` : le registre a répondu. Ses valeurs s'affichent EN LECTURE SEULE,
 *   et seuls les champs qu'il n'a pas rendus deviennent saisissables.
 * - `manuel` : le registre ne connaît pas ce matricule, ou n'a pas pu être
 *   interrogé. Formulaire complet — on ne bloque pas l'utilisateur parce que
 *   l'État est muet.
 */
type Phase = 'saisie' | 'profil' | 'manuel';

/**
 * Étape 2a — création d'une fiche identifiée par un MATRICULE FISCAL.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * POURQUOI CET ÉCRAN NE DEMANDE QU'UN CHAMP
 * ═════════════════════════════════════════════════════════════════════════════
 * `GET /v0/beneficiary/verify/{type}/{identifiant}` interroge le REGISTRE
 * NATIONAL DES CONTRIBUABLES — pas la liste du déclarant. Tout matricule fiscal
 * valide y figure, avec le nom, l'activité, l'adresse, l'e-mail et le téléphone
 * que la DGI détient. Le portail TEJ les affiche d'ailleurs auto-remplis et
 * désactivés (capture du 2026-08-06). Réclamer ces champs à l'utilisateur
 * revenait à lui faire ressaisir ce que l'État sait déjà.
 *
 * ⚠️ CE N'EST PAS UNE DÉDUPLICATION, et le contresens inverse a coûté une
 * journée le 2026-08-10 : une réponse du registre ne dit PAS « ce tiers est déjà
 * déclaré ». Cet écran ne refuse donc jamais une création au motif que le
 * registre connaît le matricule — il s'en sert pour REMPLIR.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * CE QUE CET ÉCRAN N'A PAS LE DROIT DE FAIRE CROIRE
 * ═════════════════════════════════════════════════════════════════════════════
 * Les valeurs affichées ici partent dans le POST de création, mais le serveur ne
 * les croit PAS : il RAPPELLE `verify` et c'est sa réponse qui est enregistrée
 * (`BeneficiaireController::valeursDeLaFiche()`). Sans cela, un POST forgé
 * inscrirait n'importe quoi sous couvert de « TEJ l'a dit ». Ce que le drapeau
 * `registre_tej` transmet au serveur n'est donc pas une donnée : c'est une
 * DEMANDE d'aller vérifier.
 *
 * Corollaire à ne pas perdre en refactorisant : ce qui s'affiche ici est
 * indicatif, ce qui s'enregistre vient du serveur. Les deux coïncident dans le
 * cas nominal, et c'est le serveur qui a raison dans les autres.
 *
 * ⚠️ `onInteractOutside` neutralisé (errors.md, 2026-08-04) : perdre une saisie
 * — et une interrogation du registre déjà consommée sur le quota — sur un clic
 * mal placé n'est pas acceptable.
 */
export function SupplierRegistreDialog({ open, onOpenChange, onEnvoyerTej }: Props) {
    const { t } = useTranslation();

    type RegistreForm = {
        matricule_fiscal: string;
        raison_sociale: string;
        adresse: string;
        ville: string;
        code_postal: string;
        email: string;
        telephone: string;
        note: string;
    };

    const form = useForm<RegistreForm>({
        matricule_fiscal: '',
        raison_sociale: '',
        adresse: '',
        ville: '',
        code_postal: '',
        email: '',
        telephone: '',
        note: '',
    });
    const { data, setData, processing, clearErrors } = form;
    const errors = form.errors as Record<string, string | undefined>;

    const [phase, setPhase] = React.useState<Phase>('saisie');
    const [profil, setProfil] = React.useState<ProfilRegistreTej>(PROFIL_REGISTRE_VIDE);
    const [identifiantNormalise, setIdentifiantNormalise] = React.useState<string | null>(null);
    const [avis, setAvis] = React.useState<string | null>(null);
    const [interrogation, setInterrogation] = React.useState(false);
    /** Fiche NEUVE et pas encore chez TEJ ⇒ dernière étape (« Envoyer à TEJ ? »). */
    const [creee, setCreee] = React.useState<{ id: number } | null>(null);

    // Dialogue monté en permanence : tout repart de zéro à chaque ouverture.
    // Laisser traîner un profil ferait afficher l'identité d'un contribuable
    // sous le matricule d'un autre — la faute la plus coûteuse de cet écran.
    React.useEffect(() => {
        if (!open) return;
        clearErrors();
        setPhase('saisie');
        setProfil(PROFIL_REGISTRE_VIDE);
        setIdentifiantNormalise(null);
        setAvis(null);
        setInterrogation(false);
        setCreee(null);
        setData({
            matricule_fiscal: '',
            raison_sociale: '',
            adresse: '',
            ville: '',
            code_postal: '',
            email: '',
            telephone: '',
            note: '',
        });
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [open]);

    /**
     * Bascule en saisie manuelle avec la raison, plutôt que de bloquer.
     *
     * « Le registre ne répond pas » n'est pas « vous ne pouvez pas créer cette
     * fiche » : le manager n'a pas besoin de la DGI pour tenir sa propre liste,
     * et l'envoi pourra toujours se rattraper depuis « Envoyer vers TEJ ».
     */
    const basculerEnManuel = (raison: string) => {
        setPhase('manuel');
        setProfil(PROFIL_REGISTRE_VIDE);
        setAvis(raison);
    };

    const interroger = async () => {
        const transportable = identifiantTransportable(data.matricule_fiscal);

        if (transportable === '') {
            setAvis(t('fournisseurs', 'registre_matricule_vide', 'Saisissez un matricule fiscal avant d’interroger TEJ.'));
            return;
        }

        setInterrogation(true);
        setAvis(null);

        try {
            const reponse = await apiFetch(
                route('fournisseurs.registre-tej', { typeIdentifier: TYPE_IDENTIFIER_MATRICULE, identifiant: transportable }),
                { headers: { Accept: 'application/json' } },
            );

            // Le corps peut n'être pas du JSON (page d'erreur, session expirée) :
            // on ne laisse jamais un `json()` casser l'écran.
            const corps: unknown = await reponse.json().catch(() => null);
            const message = typeof (corps as { message?: unknown } | null)?.message === 'string' ? (corps as { message: string }).message : null;

            if (!reponse.ok) {
                // 403 (hors compte principal ou droit manquant), 429 (débit),
                // 419, 5xx : tous mènent au même endroit, avec leur phrase.
                basculerEnManuel(
                    message ?? t('fournisseurs', 'registre_indisponible', 'TEJ n’a pas pu être interrogé. Saisissez la fiche à la main.'),
                );
                return;
            }

            const donnees = corps as ReponseRegistreTej | null;

            if (!donnees || donnees.ok !== true || donnees.connu !== true || !donnees.profil) {
                basculerEnManuel(
                    donnees?.message ??
                        t(
                            'fournisseurs',
                            'registre_inconnu',
                            'TEJ ne rend rien pour ce matricule. Vérifiez-le, ou saisissez la fiche à la main : elle sera enregistrée ici quoi qu’il arrive.',
                        ),
                );
                return;
            }

            setProfil(donnees.profil);
            setIdentifiantNormalise(donnees.identifiant);
            setPhase('profil');
            setAvis(null);
            // Pré-remplissage des seuls champs que le registre n'a PAS rendus :
            // ceux qu'il rend restent en lecture seule et ne transitent pas par
            // le formulaire.
            setData((current) => ({
                ...current,
                raison_sociale: donnees.profil?.raison_sociale ?? current.raison_sociale,
                adresse: donnees.profil?.adresse ?? current.adresse,
                email: donnees.profil?.email ?? current.email,
                telephone: donnees.profil?.telephone ?? current.telephone,
            }));
        } catch {
            basculerEnManuel(
                t(
                    'fournisseurs',
                    'registre_reseau',
                    'TEJ n’a pas répondu. Saisissez la fiche à la main : elle sera enregistrée ici quoi qu’il arrive.',
                ),
            );
        } finally {
            setInterrogation(false);
        }
    };

    const optionnel = (valeur: string) => (valeur.trim() === '' ? null : valeur.trim());

    const submit = (event: React.FormEvent) => {
        event.preventDefault();
        if (phase === 'saisie') return;

        form.transform((current) => ({
            raison_sociale: current.raison_sociale.trim(),
            // Le parcours a été choisi à l'étape 1 : cette fiche est identifiée
            // par un matricule fiscal, donc `typeIdentifier` 1 chez TEJ. Aucun
            // CIN ne part d'ici, aucune date de naissance n'est attendue.
            type_personne: 'morale',
            matricule_fiscal: optionnel(current.matricule_fiscal),
            adresse: optionnel(current.adresse),
            ville: optionnel(current.ville),
            code_postal: optionnel(current.code_postal),
            email: optionnel(current.email),
            telephone: optionnel(current.telephone),
            actif: true,
            note: optionnel(current.note),

            /*
             * ⚠️ CE DRAPEAU NE TRANSMET PAS UNE DONNÉE, IL DEMANDE UNE
             * VÉRIFICATION.
             *
             * À `true`, le serveur RAPPELLE `verify` et enregistre SA réponse à
             * la place des valeurs ci-dessus. Le poser depuis un client forgé
             * n'apporte donc rien : ce qui entre en base vient du registre, ou
             * n'a l'autorité de personne.
             *
             * À `false` (saisie manuelle), les valeurs sont celles de
             * l'utilisateur et l'assument — c'est une saisie, elle ne prétend
             * pas venir de la DGI.
             */
            registre_tej: phase === 'profil',
        }));

        form.post(route('fournisseurs.store'), {
            preserveScroll: true,
            onSuccess: (page) => {
                const flash = page.props.flash as { fiche_id?: number; tej_envoye?: boolean } | undefined;

                // Pas de raccourci demandé, pas d'id, ou fiche déjà inscrite
                // chez TEJ : on ferme, exactement comme avant.
                if (!onEnvoyerTej || typeof flash?.fiche_id !== 'number' || flash.tej_envoye !== false) {
                    onOpenChange(false);

                    return;
                }

                setCreee({ id: flash.fiche_id });
            },
        });
    };

    /** Un champ que le registre n'a pas rendu doit être saisi à la main. */
    const manque = (cle: keyof ProfilRegistreTej) => phase === 'manuel' || profil[cle] === null;

    return (
        <Dialog open={open} onOpenChange={onOpenChange}>
            <DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-xl" onInteractOutside={(e) => e.preventDefault()}>
                <DialogHeader>
                    <DialogTitle>{t('fournisseurs', 'creation_societe', 'Société — matricule fiscal')}</DialogTitle>
                    <DialogDescription>
                        {phase === 'saisie'
                            ? t(
                                  'fournisseurs',
                                  'registre_desc',
                                  'Saisissez le matricule fiscal : TEJ renvoie le nom, l’activité, l’adresse, l’e-mail et le téléphone.',
                              )
                            : t('fournisseurs', 'registre_desc_suite', 'Vérifiez ce qui sera enregistré, puis validez.')}
                    </DialogDescription>
                </DialogHeader>

                {creee !== null ? (
                    <FicheCreeeStep
                        onFermer={() => onOpenChange(false)}
                        onEnvoyer={() => {
                            const id = creee.id;
                            onOpenChange(false);
                            onEnvoyerTej?.(id);
                        }}
                    />
                ) : (
                    <form onSubmit={submit} className="flex flex-col gap-4">
                        {/* Le seul champ de l'étape 1 du parcours matricule. Il reste
                        modifiable après une interrogation : corriger une faute de
                        frappe et redemander est le geste normal.

                        ⚠️ Et c'est PRÉCISÉMENT pourquoi le changer doit tout
                        réinitialiser. Sans ça, on interrogeait le matricule A,
                        on corrigeait en B, et on validait : l'écran montrait
                        l'identité de A pendant que le serveur enregistrait B.
                        Le profil affiché n'aurait alors garanti RIEN de ce qui
                        partait chez la DGI. */}
                        <div className="flex flex-col gap-1.5">
                            <Label htmlFor="registre-mf">{t('fournisseurs', 'matricule_fiscal')}</Label>
                            <div className="flex items-start gap-2">
                                <div className="flex flex-1 flex-col gap-1.5">
                                    <Input
                                        id="registre-mf"
                                        aria-invalid={!!errors.matricule_fiscal}
                                        value={data.matricule_fiscal}
                                        onChange={(e) => {
                                            setData('matricule_fiscal', e.target.value);

                                            // Le profil affiché ne vaut que pour le
                                            // matricule qui l'a produit : il tombe
                                            // avec lui.
                                            if (phase !== 'saisie') {
                                                setPhase('saisie');
                                                setProfil(PROFIL_REGISTRE_VIDE);
                                                setIdentifiantNormalise(null);
                                                setAvis(null);
                                            }
                                        }}
                                        maxLength={40}
                                        placeholder="1631526V"
                                        className="tabular-nums"
                                    />
                                    {/* La forme NORMALISÉE, celle qui partira dans le
                                    corps — jamais la saisie. Doctrine du module :
                                    on confirme la chaîne qui voyage. */}
                                    {identifiantNormalise && (
                                        <p className="text-muted-foreground text-xs">
                                            {t('fournisseurs', 'tej_identifiant', 'Identifiant normalisé')} :{' '}
                                            <span className="font-mono tracking-wide">{identifiantNormalise}</span>
                                        </p>
                                    )}
                                    <FieldError message={errors.matricule_fiscal} />
                                </div>
                                <Button type="button" variant="secondary" onClick={interroger} disabled={interrogation || processing}>
                                    <Search className="mr-2 h-4 w-4" />
                                    {t('fournisseurs', 'registre_interroger', 'Interroger TEJ')}
                                </Button>
                            </div>
                        </div>

                        {/* Pourquoi on est passé en saisie manuelle. Ce n'est pas une
                        erreur de l'utilisateur : le ton reste informatif. */}
                        {avis !== null && (
                            <div className="flex items-start gap-2 rounded-md border border-amber-300 bg-amber-50 p-3 text-sm text-amber-900">
                                <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
                                <span>{avis}</span>
                            </div>
                        )}

                        {/* CE QUE LE REGISTRE REND — lecture seule, comme sur le
                        portail TEJ. Ces lignes ne sont pas des champs : leur
                        valeur ne transite pas par le formulaire, et le serveur la
                        redemande de toute façon. */}
                        {phase === 'profil' && (
                            <dl className="bg-muted/40 flex flex-col gap-2 rounded-md border p-3 text-sm">
                                <p className="text-muted-foreground text-xs">
                                    {t('fournisseurs', 'registre_source', 'Renseigné par le registre national des contribuables.')}
                                </p>
                                <ProfilLigne libelle={t('fournisseurs', 'raison_sociale')} valeur={profil.raison_sociale} />
                                <ProfilLigne libelle={t('fournisseurs', 'registre_activite', 'Activité')} valeur={profil.activite} />
                                <ProfilLigne libelle={t('fournisseurs', 'adresse')} valeur={profil.adresse} />
                                <ProfilLigne libelle={t('fournisseurs', 'email')} valeur={profil.email} />
                                <ProfilLigne libelle={t('fournisseurs', 'telephone')} valeur={profil.telephone} />
                                <ProfilLigne
                                    libelle={t('fournisseurs', 'registre_resident', 'Résident')}
                                    valeur={
                                        profil.resident === null
                                            ? null
                                            : profil.resident
                                              ? t('fournisseurs', 'oui', 'Oui')
                                              : t('fournisseurs', 'non', 'Non')
                                    }
                                />
                            </dl>
                        )}

                        {/* Ce que le registre n'a pas donné se saisit — préremplis de
                        ce qui existe. Bloquer sur un registre partiel enfermerait
                        l'utilisateur pour une donnée que lui connaît. */}
                        {phase !== 'saisie' && (
                            <>
                                {manque('raison_sociale') && (
                                    <div className="flex flex-col gap-1.5">
                                        <Label htmlFor="registre-raison">
                                            {t('fournisseurs', 'raison_sociale')}
                                            <span className="text-destructive ml-1">*</span>
                                        </Label>
                                        <Input
                                            id="registre-raison"
                                            aria-invalid={!!errors.raison_sociale}
                                            value={data.raison_sociale}
                                            onChange={(e) => setData('raison_sociale', e.target.value)}
                                            maxLength={255}
                                        />
                                        <FieldError message={errors.raison_sociale} />
                                    </div>
                                )}

                                <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
                                    {manque('email') && (
                                        <div className="flex flex-col gap-1.5">
                                            <Label htmlFor="registre-email">{t('fournisseurs', 'email')}</Label>
                                            <Input
                                                id="registre-email"
                                                aria-invalid={!!errors.email}
                                                type="email"
                                                value={data.email}
                                                onChange={(e) => setData('email', e.target.value)}
                                                maxLength={255}
                                            />
                                            <FieldError message={errors.email} />
                                        </div>
                                    )}
                                    {manque('telephone') && (
                                        <div className="flex flex-col gap-1.5">
                                            <Label htmlFor="registre-tel">{t('fournisseurs', 'telephone')}</Label>
                                            <Input
                                                id="registre-tel"
                                                aria-invalid={!!errors.telephone}
                                                value={data.telephone}
                                                onChange={(e) => setData('telephone', e.target.value)}
                                                maxLength={40}
                                            />
                                            <FieldError message={errors.telephone} />
                                        </div>
                                    )}
                                </div>

                                {manque('adresse') && (
                                    <div className="grid grid-cols-1 gap-3 sm:grid-cols-4">
                                        <div className="flex flex-col gap-1.5 sm:col-span-2">
                                            <Label htmlFor="registre-adresse">{t('fournisseurs', 'adresse')}</Label>
                                            <Input
                                                id="registre-adresse"
                                                aria-invalid={!!errors.adresse}
                                                value={data.adresse}
                                                onChange={(e) => setData('adresse', e.target.value)}
                                                maxLength={255}
                                            />
                                            <FieldError message={errors.adresse} />
                                        </div>
                                        <div className="flex flex-col gap-1.5">
                                            <Label htmlFor="registre-ville">{t('fournisseurs', 'ville')}</Label>
                                            <Input
                                                id="registre-ville"
                                                aria-invalid={!!errors.ville}
                                                value={data.ville}
                                                onChange={(e) => setData('ville', e.target.value)}
                                                maxLength={100}
                                            />
                                            <FieldError message={errors.ville} />
                                        </div>
                                        <div className="flex flex-col gap-1.5">
                                            <Label htmlFor="registre-cp">{t('fournisseurs', 'code_postal')}</Label>
                                            <Input
                                                id="registre-cp"
                                                aria-invalid={!!errors.code_postal}
                                                value={data.code_postal}
                                                onChange={(e) => setData('code_postal', e.target.value)}
                                                maxLength={20}
                                                className="tabular-nums"
                                            />
                                            <FieldError message={errors.code_postal} />
                                        </div>
                                    </div>
                                )}

                                {/* La note est à NOUS, pas au registre : elle ne
                                s'affiche que là où l'utilisateur saisit. */}
                                {phase === 'manuel' && (
                                    <div className="flex flex-col gap-1.5">
                                        <Label htmlFor="registre-note">{t('fournisseurs', 'note')}</Label>
                                        <Textarea
                                            id="registre-note"
                                            value={data.note}
                                            onChange={(e) => setData('note', e.target.value)}
                                            rows={2}
                                            maxLength={1000}
                                        />
                                        <FieldError message={errors.note} />
                                    </div>
                                )}
                            </>
                        )}

                        <DialogFooter>
                            <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
                                {t('fournisseurs', 'cancel')}
                            </Button>
                            <Button type="submit" disabled={processing || interrogation || phase === 'saisie'}>
                                {t('fournisseurs', 'registre_valider', 'Valider et créer')}
                            </Button>
                        </DialogFooter>
                    </form>
                )}
            </DialogContent>
        </Dialog>
    );
}

/** Une ligne du profil rendu par le registre. Jamais un champ. */
function ProfilLigne({ libelle, valeur }: { libelle: string; valeur: string | null }) {
    if (valeur === null) return null;

    return (
        <div className="flex items-baseline justify-between gap-3">
            <dt className="text-muted-foreground shrink-0">{libelle}</dt>
            <dd className="text-right font-medium break-words">{valeur}</dd>
        </div>
    );
}

function FieldError({ message }: { message?: string }) {
    if (!message) return null;
    return <p className="text-destructive text-xs">{message}</p>;
}
