import { DataTable } from '@/components/data-table';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { useTranslation } from '@/hooks/use-translation';
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, router, usePage } from '@inertiajs/react';
import { Plus, RefreshCcw, X } from 'lucide-react';
import * as React from 'react';
import { toast } from 'sonner';
import { getColumns, type SupplierRow } from './columns';
import { OwnerFicheDialog, type OwnerOption } from './owner-fiche-dialog';
import { SupplierChoiceDialog, type ParcoursCreation } from './supplier-choice-dialog';
import { SupplierDeleteDialog } from './supplier-delete-dialog';
import { SupplierDialog } from './supplier-dialog';
import { SupplierRegistreDialog } from './supplier-registre-dialog';
import type { TejEcritureProp } from './tej';
import { TejRapprochementDialog } from './tej-rapprochement-dialog';
import { TejSendDialog } from './tej-send-dialog';

interface PageProps {
    suppliers: SupplierRow[];
    filters: { search: string; statut: string; perimetre: string };
    /**
     * Les propriétaires du parc, chacun avec l'état de sa fiche. Vide si le
     * compte n'a pas le droit de créer : la liste n'a alors aucun usage, et la
     * publier exposerait l'identité fiscale du parc à un compte en lecture.
     */
    proprietaires?: OwnerOption[];
    /** `tej` = permission `fournisseur.tej`, celle de la route POST /fournisseurs/{beneficiaire}/tej. */
    can: { create: boolean; update: boolean; delete: boolean; tej?: boolean };
    /**
     * Verrou serveur d'écriture vers TEJ — prop attendue sous le nom `tej`.
     * Contrat complet dans `./tej.ts`. Absente, l'écran se comporte comme si
     * l'écriture était coupée : c'est le défaut sûr.
     */
    tej?: TejEcritureProp;
    flash?: { type: 'success' | 'error'; message: string };
    [key: string]: unknown;
}

/** Filtre avec son « × » de retrait — même patron que fond-de-caisse. */
function FilterLabel({ children, active, onClear }: { children: React.ReactNode; active: boolean; onClear: () => void }) {
    return (
        <div className="flex items-center gap-1">
            <Label className="text-xs">{children}</Label>
            {active && (
                <button
                    type="button"
                    onClick={onClear}
                    title="Retirer ce filtre"
                    className="text-muted-foreground flex h-3.5 w-3.5 items-center justify-center rounded-full transition-colors hover:bg-red-100 hover:text-red-600"
                >
                    <X className="h-2.5 w-2.5" />
                </button>
            )}
        </div>
    );
}

export default function Index() {
    const { t } = useTranslation();
    const { suppliers, filters, proprietaires, can, tej, flash } = usePage<PageProps>().props;

    // Défaut FERMÉ. Une prop absente (déploiement partiel, contrôleur pas
    // encore à jour) doit éteindre le bouton, jamais l'ouvrir.
    const ecriture: TejEcritureProp = React.useMemo(
        () => ({
            ecriture_active: tej?.ecriture_active === true,
            raison_indisponible: tej?.raison_indisponible ?? null,
        }),
        [tej?.ecriture_active, tej?.raison_indisponible],
    );

    const breadcrumbs: BreadcrumbItem[] = [{ title: t('fournisseurs', 'title'), href: '/fournisseurs' }];

    // Filtres initialisés depuis la prop `filters` : ils survivent au retour
    // navigateur (Inertia restaure l'URL + les props).
    const [search, setSearch] = React.useState(filters.search ?? '');
    const [statut, setStatut] = React.useState(filters.statut ?? 'actifs');
    // Défaut « tous » — le serveur le renvoie déjà dans `filters`, ce repli ne
    // sert qu'à un rendu sans props. Il était à « tiers » : l'écran s'ouvrait
    // alors sur une liste amputée des propriétaires, sans le dire.
    const [perimetre, setPerimetre] = React.useState(filters.perimetre ?? 'tous');

    // Garde de premier rendu : sans elle, l'effet rejouerait la requête au
    // montage et écraserait les filtres restaurés.
    const hasMounted = React.useRef(false);
    React.useEffect(() => {
        if (!hasMounted.current) {
            hasMounted.current = true;
            return;
        }
        const timer = setTimeout(() => {
            router.get(
                route('fournisseurs.index'),
                {
                    search: search || undefined,
                    statut: statut !== 'actifs' ? statut : undefined,
                    perimetre: perimetre !== 'tous' ? perimetre : undefined,
                },
                { preserveState: true, preserveScroll: true, replace: true },
            );
        }, 350);
        return () => clearTimeout(timer);
    }, [search, statut, perimetre]);

    /*
     * L'AJOUT SE FAIT EN DEUX TEMPS : le choix, puis le vrai formulaire.
     *
     * `choixOuvert` porte l'étape 1 (quelle pièce d'identité ?). Une fois le
     * choix fait, ce dialogue se FERME et `creation` ouvre celui qui correspond.
     * Les deux ne sont jamais visibles ensemble : mélanger le choix et la saisie
     * était précisément ce qu'il fallait défaire.
     *
     * `editSupplier` reste la modification, et n'a pas bougé — elle n'a pas de
     * choix préalable, une fiche existante porte déjà son identité.
     */
    const [choixOuvert, setChoixOuvert] = React.useState(false);
    const [creation, setCreation] = React.useState<ParcoursCreation | null>(null);
    const [editSupplier, setEditSupplier] = React.useState<SupplierRow | null>(null);
    const [deleteSupplier, setDeleteSupplier] = React.useState<SupplierRow | null>(null);
    const [tejSupplier, setTejSupplier] = React.useState<SupplierRow | null>(null);

    /**
     * Raccourci « Envoyer à TEJ » de la dernière étape des formulaires.
     *
     * ⚠️ ON RECHARGE LA LISTE SANS FILTRE plutôt que de chercher la fiche dans
     * `suppliers`. Une première version faisait
     * `suppliers.find((s) => s.id === ficheId)` en supposant la fiche présente
     * « puisque le serveur a répondu par un `back()` ». Or `suppliers` est
     * FILTRÉ en SQL par `search`, `statut` et `perimetre` : chercher « Sté
     * Alpha », ne rien trouver, puis créer la fiche par son matricule — que le
     * registre national renvoie sous « SOCIETE ALPHA SARL » — la laissait hors
     * de la liste. Le raccourci ne faisait alors RIEN, en silence.
     *
     * L'aller-retour par l'URL est le seul mécanisme qui marche dans tous les
     * cas : il repart d'une liste non filtrée, et il survit au `preserveState:
     * false` dont le parcours propriétaire a besoin.
     */
    const ouvrirEnvoiApresCreation = (ficheId: number) => {
        router.get(route('fournisseurs.index'), { envoyer: ficheId }, { preserveScroll: true, preserveState: false });
    };

    /**
     * L'autre bout du raccourci : la visite est arrivée, la fiche est là.
     *
     * Le passage par l'URL n'est pas un détour. Une fiche fraîchement
     * enregistrée peut être masquée par une recherche ou un filtre en cours :
     * les deux parcours rechargent donc la liste, avec `preserveState: false`.
     * Tout état React posé avant la visite serait perdu au remontage — l'URL,
     * elle, survit.
     */
    const url = usePage().url;
    React.useEffect(() => {
        const demande = Number(new URLSearchParams(url.split('?')[1] ?? '').get('envoyer'));

        if (!Number.isInteger(demande) || demande <= 0) return;

        const fiche = suppliers.find((supplier) => supplier.id === demande);

        if (fiche) setTejSupplier(fiche);

        // Consommé une fois. Le laisser dans l'URL ferait rouvrir la
        // confirmation d'envoi à chaque rafraîchissement, sur une fiche déjà
        // traitée — et ce dialogue écrit chez la DGI.
        if (typeof window !== 'undefined') {
            const propre = new URL(window.location.href);
            propre.searchParams.delete('envoyer');
            window.history.replaceState(window.history.state, '', propre.toString());
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [url, suppliers]);
    const [rapprochementOuvert, setRapprochementOuvert] = React.useState(false);

    /*
     * QUEL formulaire de modification ouvrir — et c'est la FICHE qui décide,
     * jamais l'endroit d'où l'on a cliqué. Une fiche rattachée à un propriétaire
     * ne se corrige pas dans le formulaire des tiers : son identité y serait
     * éditable alors que `beneficiairePourProprietaire()` la réécrit depuis
     * `users` au premier rafraîchissement.
     */
    const ficheProprietaire = editSupplier !== null && editSupplier.proprietaire != null;

    // Défensif : nettoyer un éventuel body-lock Radix resté en place.
    const anyOpen =
        choixOuvert || creation !== null || editSupplier !== null || deleteSupplier !== null || tejSupplier !== null || rapprochementOuvert;
    React.useEffect(() => {
        if (anyOpen) return;
        const timer = setTimeout(() => {
            if (typeof document !== 'undefined') {
                document.body.style.pointerEvents = '';
                document.body.style.removeProperty('pointer-events');
            }
        }, 300);
        return () => clearTimeout(timer);
    }, [anyOpen]);

    React.useEffect(() => {
        if (flash?.type === 'success') toast.success(flash.message);
        else if (flash?.type === 'error') toast.error(flash.message);
    }, [flash]);

    // La fiche ouverte dans la confirmation TEJ, re-lue dans les props à chaque
    // rendu : après un envoi réussi, `tej.synced_at` change côté serveur et le
    // dialogue doit montrer le nouvel état, pas l'instantané du clic.
    const tejSupplierFrais = React.useMemo(
        () => (tejSupplier ? (suppliers.find((supplier) => supplier.id === tejSupplier.id) ?? tejSupplier) : null),
        [suppliers, tejSupplier],
    );

    // Même relecture pour la confirmation de suppression, et pour une raison
    // plus dure : elle ANNONCE une conséquence (« effacée définitivement » /
    // « conservée, N lignes la citent »). Si une ligne de retenue est ajoutée
    // ailleurs pendant que cet écran vit, l'instantané du clic promettrait un
    // effacement là où le serveur conservera la fiche. Le compte affiché doit
    // être celui des props courantes.
    const deleteSupplierFrais = React.useMemo(
        () => (deleteSupplier ? (suppliers.find((supplier) => supplier.id === deleteSupplier.id) ?? deleteSupplier) : null),
        [suppliers, deleteSupplier],
    );

    const columns = React.useMemo(
        () =>
            getColumns({
                t,
                onEdit: (supplier) => setEditSupplier(supplier),
                onDelete: (supplier) => setDeleteSupplier(supplier),
                onSendTej: (supplier) => setTejSupplier(supplier),
                can: { ...can, tej: can.tej === true },
                ecriture,
            }),
        [t, can, ecriture],
    );

    return (
        <AppLayout breadcrumbs={breadcrumbs}>
            <Head title={t('fournisseurs', 'title')} />

            <div className="flex h-full flex-1 flex-col gap-4 p-3 sm:p-4 lg:p-6">
                <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
                    <div>
                        <h1 className="text-2xl font-semibold tracking-tight">{t('fournisseurs', 'title')}</h1>
                        <p className="text-muted-foreground text-sm">{t('fournisseurs', 'subtitle')}</p>
                    </div>
                    <div className="flex flex-wrap items-center gap-2">
                        {/* Le rapprochement est un geste de RÉFÉRENTIEL, pas de
                            saisie : il vit à côté de « Ajouter », pas dedans.
                            Réservé à qui peut modifier — il pose le lien vers la
                            DGI sur les fiches reconnues. */}
                        {can.update && (
                            <Button variant="outline" onClick={() => setRapprochementOuvert(true)}>
                                <RefreshCcw className="mr-2 h-4 w-4" />
                                {t('fournisseurs', 'tej_sync', 'Rapprocher avec TEJ')}
                            </Button>
                        )}
                        {can.create && (
                            <Button onClick={() => setChoixOuvert(true)}>
                                <Plus className="mr-2 h-4 w-4" />
                                {t('fournisseurs', 'add')}
                            </Button>
                        )}
                    </div>
                </div>

                <Card>
                    <CardContent className="flex flex-col flex-wrap gap-3 px-3 py-3 sm:flex-row sm:items-end">
                        <div className="flex flex-1 flex-col gap-1 sm:min-w-[220px]">
                            <FilterLabel active={search !== ''} onClear={() => setSearch('')}>
                                {t('fournisseurs', 'search')}
                            </FilterLabel>
                            <Input
                                type="search"
                                value={search}
                                onChange={(e) => setSearch(e.target.value)}
                                placeholder={t('fournisseurs', 'search')}
                                className="h-9"
                            />
                        </div>

                        <div className="flex flex-col gap-1">
                            <FilterLabel active={statut !== 'actifs'} onClear={() => setStatut('actifs')}>
                                {t('fournisseurs', 'filter_statut')}
                            </FilterLabel>
                            <Select value={statut} onValueChange={setStatut}>
                                <SelectTrigger className="h-9 w-[160px]">
                                    <SelectValue />
                                </SelectTrigger>
                                <SelectContent>
                                    <SelectItem value="actifs">{t('fournisseurs', 'statut_actifs')}</SelectItem>
                                    <SelectItem value="inactifs">{t('fournisseurs', 'statut_inactifs')}</SelectItem>
                                    <SelectItem value="tous">{t('fournisseurs', 'statut_tous')}</SelectItem>
                                </SelectContent>
                            </Select>
                        </div>

                        {/* QUELLE MOITIÉ DE LA TABLE ON REGARDE — et par défaut
                            les deux. Le défaut d'avant (« Tiers ») ouvrait
                            l'écran sur une liste amputée des propriétaires sans
                            que rien ne le dise : une fiche créée depuis le
                            formulaire propriétaire disparaissait à l'instant de
                            sa création. Le mot « Tiers » a disparu aussi —
                            c'était celui de la table, pas celui de la personne
                            qui s'en sert. */}
                        <div className="flex flex-col gap-1">
                            <FilterLabel active={perimetre !== 'tous'} onClear={() => setPerimetre('tous')}>
                                {t('fournisseurs', 'filter_perimetre', 'Afficher')}
                            </FilterLabel>
                            <Select value={perimetre} onValueChange={setPerimetre}>
                                <SelectTrigger className="h-9 w-[210px]">
                                    <SelectValue />
                                </SelectTrigger>
                                <SelectContent>
                                    <SelectItem value="tous">{t('fournisseurs', 'perimetre_tous', 'Toutes les fiches')}</SelectItem>
                                    <SelectItem value="fournisseurs">{t('fournisseurs', 'perimetre_tiers', 'Fournisseurs seulement')}</SelectItem>
                                    <SelectItem value="proprietaires">
                                        {t('fournisseurs', 'perimetre_proprietaires', 'Propriétaires seulement')}
                                    </SelectItem>
                                </SelectContent>
                            </Select>
                        </div>

                        <Button
                            variant="ghost"
                            size="sm"
                            onClick={() => {
                                setSearch('');
                                setStatut('actifs');
                                setPerimetre('tous');
                            }}
                            className="self-end"
                        >
                            <RefreshCcw className="mr-2 h-4 w-4" />
                            {t('fournisseurs', 'reset_filters')}
                        </Button>

                        {/* Ce que veut dire chaque moitié. La distinction décide
                            de l'écran où l'identité se corrige — et rien, avant,
                            ne la disait nulle part. */}
                        <p className="text-muted-foreground w-full text-xs">
                            {t(
                                'fournisseurs',
                                'perimetre_hint',
                                'Une fiche « propriétaire » appartient à un compte du parc : son identité se corrige dans Propriétaires. Une fiche « fournisseur » n’appartient à personne : elle se saisit ici.',
                            )}
                        </p>
                    </CardContent>
                </Card>

                <Card className="flex-1">
                    <CardContent className="px-3 py-0">
                        {suppliers.length === 0 ? (
                            <p className="text-muted-foreground py-10 text-center text-sm">{t('fournisseurs', 'none')}</p>
                        ) : (
                            <DataTable columns={columns} data={suppliers} />
                        )}
                    </CardContent>
                </Card>
            </div>

            {/* Dialogues montés en permanence : la prop `open` seule bascule,
                pour laisser Radix nettoyer son body-lock à la fermeture. */}

            {/* Étape 1 — la pièce d'identité. Ce dialogue ne saisit RIEN : il
                choisit lequel des deux formulaires s'ouvre, puis se ferme. */}
            <SupplierChoiceDialog
                open={choixOuvert}
                onOpenChange={(value) => !value && setChoixOuvert(false)}
                onChoisir={(parcours) => {
                    setChoixOuvert(false);
                    setCreation(parcours);
                }}
            />

            {/* Étape 2a — matricule fiscal. Un seul champ, puis le registre
                national remplit le reste. */}
            <SupplierRegistreDialog
                open={creation === 'matricule'}
                onOpenChange={(value) => !value && setCreation(null)}
                onEnvoyerTej={ouvrirEnvoiApresCreation}
            />

            {/* Modification d'un TIERS. Ce dialogue ne crée plus rien : le
                parcours « personne physique hors parc » a été retiré du choix
                (voir l'en-tête de `supplier-choice-dialog`). Monté seulement
                quand une fiche est visée — son contrat exige un `supplier`. */}
            {editSupplier !== null && !ficheProprietaire && (
                <SupplierDialog open onOpenChange={(value) => !value && setEditSupplier(null)} supplier={editSupplier} />
            )}

            {/* La fiche fiscale d'un PROPRIÉTAIRE, créée ou complétée — c'est
                le même geste et donc le même écran. Il sait aussi corriger
                l'identité sur le compte, ce que son prédécesseur ne savait pas :
                on y sélectionnait un propriétaire sans CIN pour s'entendre dire
                que sa fiche bloquerait la clôture, sans le moindre champ. */}
            <OwnerFicheDialog
                open={creation === 'proprietaire' || ficheProprietaire}
                onOpenChange={(value) => {
                    if (value) return;
                    setCreation(null);
                    setEditSupplier(null);
                }}
                proprietaires={proprietaires ?? []}
                ownerId={ficheProprietaire ? (editSupplier?.proprietaire?.id ?? null) : null}
                onEnvoyerTej={ouvrirEnvoiApresCreation}
            />

            {/* Envoi vers TEJ : geste HUMAIN, jamais un job ni un observer.
                Ce dialogue est le seul chemin de l'écran vers l'écriture. */}
            <TejSendDialog
                open={tejSupplier !== null}
                onOpenChange={(value) => !value && setTejSupplier(null)}
                supplier={tejSupplierFrais}
                ecriture={ecriture}
            />

            {/* Suppression : la confirmation DIT ce qui part — effacement
                définitif ici et retrait chez TEJ, ou simple masquage d'une fiche
                que des lignes de retenue retiennent. Le détail est dans
                `supplier-delete-dialog`. */}
            <TejRapprochementDialog open={rapprochementOuvert} onOpenChange={setRapprochementOuvert} peutImporter={can.create} />

            <SupplierDeleteDialog
                open={deleteSupplier !== null}
                onOpenChange={(value) => !value && setDeleteSupplier(null)}
                supplier={deleteSupplierFrais}
            />
        </AppLayout>
    );
}
