import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { Input } from '@/components/ui/input';
import { Spinner } from '@/components/ui/spinner';
import { useTranslation } from '@/hooks/use-translation';
import { cn } from '@/lib/utils';
import { AlertTriangle, ChevronRight, Copy, ExternalLink, FileSpreadsheet, Palette, RefreshCcw } from 'lucide-react';
import * as React from 'react';
import { toast } from 'sonner';
import { remplir } from './api';
import { LegendeCouleurs } from './legende-couleurs';
import type { EtatImport, OngletFichier } from './types';

interface Props {
    etat: EtatImport | null;
    /** Le chargement de l'état a échoué — et pourquoi. */
    erreur: string | null;
    chargement: boolean;
    ongletsChoisis: ReadonlySet<number>;
    onBasculerOnglet: (gid: number) => void;
    onChangerFichier: (lien: string) => void;
    onRecharger: () => void;
    onMettreAJourCouleurs: () => void;
    couleursEnCours: boolean;
}

/**
 * Étape 1 — le fichier et ses onglets.
 *
 * Rien n'est lu ligne à ligne ici : on choisit QUOI lire. Le serveur dit quels
 * onglets il sait lire (`reconnu`), et pourquoi pas les autres.
 */
export function EtapeFichier({
    etat,
    erreur,
    chargement,
    ongletsChoisis,
    onBasculerOnglet,
    onChangerFichier,
    onRecharger,
    onMettreAJourCouleurs,
    couleursEnCours,
}: Props) {
    const { t } = useTranslation();

    if (etat === null) {
        return (
            <div className="flex flex-col gap-3 py-6">
                {chargement && (
                    <p className="text-muted-foreground flex items-center gap-2 text-sm">
                        <Spinner size="sm" />
                        {t('retenue', 'import_etat_chargement', 'Lecture du fichier des paiements…')}
                    </p>
                )}
                {erreur && (
                    <Alerte>
                        <span>{erreur}</span>
                        <Button variant="outline" size="sm" className="w-fit" onClick={onRecharger}>
                            <RefreshCcw className="mr-2 h-4 w-4" />
                            {t('retenue', 'import_reessayer', 'Réessayer')}
                        </Button>
                    </Alerte>
                )}
            </div>
        );
    }

    const lisible = etat.configure && etat.spreadsheet !== null;
    // Les onglets lisibles d'abord : un vrai fichier en compte une trentaine,
    // dont la plupart ne sont pas des mois. L'ordre du fichier est gardé dans
    // chaque groupe.
    const reconnus = etat.onglets.filter((o) => o.reconnu);
    const nonReconnus = etat.onglets.filter((o) => !o.reconnu);

    return (
        <div className="flex flex-col gap-4">
            {erreur && <Alerte>{erreur}</Alerte>}

            {!lisible && (
                <Alerte>
                    <span>
                        {etat.message ?? t('retenue', 'import_non_configure', 'Le fichier des paiements ne peut pas être lu pour le moment.')}
                    </span>
                    {etat.compte_service && <PartageCompte email={etat.compte_service} />}
                </Alerte>
            )}

            {etat.spreadsheet && (
                <div className="flex min-w-0 items-center gap-2">
                    <FileSpreadsheet className="text-muted-foreground h-5 w-5 shrink-0" />
                    <a
                        href={etat.spreadsheet.url}
                        target="_blank"
                        rel="noopener noreferrer"
                        className="inline-flex min-w-0 items-center gap-1.5 text-sm font-medium underline-offset-2 hover:underline"
                    >
                        <span className="truncate">{etat.spreadsheet.titre}</span>
                        <ExternalLink className="h-3.5 w-3.5 shrink-0" />
                    </a>
                </div>
            )}

            {/* Le lien se change dès que le compte sait lire — y compris quand
                le fichier actuel est illisible : c'est alors la seule sortie. */}
            {etat.configure && <ChangerFichier ouvertParDefaut={etat.spreadsheet === null} chargement={chargement} onChanger={onChangerFichier} />}

            {etat.spreadsheet && (
                <fieldset className="flex flex-col gap-2">
                    <legend className="mb-1 text-sm font-medium">{t('retenue', 'import_onglets_titre', 'Onglets à lire')}</legend>
                    {etat.onglets.length === 0 ? (
                        <p className="text-muted-foreground text-sm">{t('retenue', 'import_onglets_vide', 'Ce fichier n’a aucun onglet lisible.')}</p>
                    ) : reconnus.length === 0 ? (
                        <p className="text-muted-foreground text-sm">
                            {t(
                                'retenue',
                                'import_onglets_aucun_reconnu',
                                'Aucun onglet de ce fichier n’est reconnu : la liste ci-dessous dit pourquoi.',
                            )}
                        </p>
                    ) : (
                        <ul className="grid grid-cols-1 gap-1.5 sm:grid-cols-2 lg:grid-cols-3">
                            {reconnus.map((onglet) => {
                                const id = `import-onglet-${onglet.gid}`;

                                return (
                                    <li key={onglet.gid} className="flex items-start gap-2 rounded-md border px-2.5 py-2">
                                        <Checkbox
                                            id={id}
                                            className="mt-0.5"
                                            checked={ongletsChoisis.has(onglet.gid)}
                                            onCheckedChange={() => onBasculerOnglet(onglet.gid)}
                                        />
                                        <label htmlFor={id} className="flex min-w-0 cursor-pointer flex-col">
                                            <span className="truncate text-sm">{onglet.titre}</span>
                                        </label>
                                    </li>
                                );
                            })}
                        </ul>
                    )}
                    {nonReconnus.length > 0 && <OngletsNonReconnus onglets={nonReconnus} ouvertParDefaut={reconnus.length === 0} />}
                </fieldset>
            )}

            {etat.spreadsheet && <SuiviDuFichier etat={etat} onMettreAJourCouleurs={onMettreAJourCouleurs} couleursEnCours={couleursEnCours} />}
        </div>
    );
}

/** Les onglets que le serveur ne sait pas lire — repliés, grisés, avec leur motif en petit. */
function OngletsNonReconnus({ onglets, ouvertParDefaut }: { onglets: OngletFichier[]; ouvertParDefaut: boolean }) {
    const { t } = useTranslation();
    const [ouvert, setOuvert] = React.useState(ouvertParDefaut);

    return (
        <Collapsible open={ouvert} onOpenChange={setOuvert}>
            <CollapsibleTrigger className="text-muted-foreground hover:text-foreground flex items-center gap-1.5 text-xs">
                <ChevronRight className={cn('h-3.5 w-3.5 transition-transform', ouvert && 'rotate-90')} />
                {remplir(t('retenue', 'import_onglets_non_reconnus', ':n autres onglets non reconnus'), { n: onglets.length })}
            </CollapsibleTrigger>
            <CollapsibleContent>
                <ul className="mt-2 grid grid-cols-1 gap-1 sm:grid-cols-2 lg:grid-cols-3">
                    {onglets.map((onglet) => (
                        <li
                            key={onglet.gid}
                            className="bg-muted/40 flex min-w-0 flex-col rounded-md border px-2.5 py-1.5 opacity-70"
                            title={onglet.motif ?? undefined}
                        >
                            <span className="truncate text-sm">{onglet.titre}</span>
                            {onglet.motif && <span className="text-muted-foreground line-clamp-2 text-[11px]">{onglet.motif}</span>}
                        </li>
                    ))}
                </ul>
            </CollapsibleContent>
        </Collapsible>
    );
}

function Alerte({ children }: { children: React.ReactNode }) {
    return (
        <div className="flex gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900">
            <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
            <div className="flex min-w-0 flex-col gap-2">{children}</div>
        </div>
    );
}

/** « Partagez le fichier avec … en tant qu'Éditeur », et de quoi copier l'adresse. */
function PartageCompte({ email }: { email: string }) {
    const { t } = useTranslation();

    const copier = async () => {
        try {
            await navigator.clipboard.writeText(email);
            toast.success(t('retenue', 'import_copie_ok', 'Adresse copiée.'));
        } catch {
            toast.error(t('retenue', 'import_copie_ko', 'Copie impossible : sélectionnez l’adresse à la main.'));
        }
    };

    return (
        <div className="flex flex-col gap-1.5">
            <span>{t('retenue', 'import_partager', 'Partagez le fichier des paiements avec cette adresse, en tant qu’Éditeur :')}</span>
            <div className="flex flex-wrap items-center gap-2">
                <code className="rounded border border-amber-200 bg-white px-1.5 py-0.5 text-xs break-all select-all">{email}</code>
                <Button type="button" variant="outline" size="sm" onClick={copier}>
                    <Copy className="mr-1.5 h-3.5 w-3.5" />
                    {t('retenue', 'import_copier', 'Copier')}
                </Button>
            </div>
        </div>
    );
}

/** « Changer de fichier » : coller un lien recharge l'état sur ce fichier. */
function ChangerFichier({
    ouvertParDefaut,
    chargement,
    onChanger,
}: {
    ouvertParDefaut: boolean;
    chargement: boolean;
    onChanger: (lien: string) => void;
}) {
    const { t } = useTranslation();
    const [ouvert, setOuvert] = React.useState(ouvertParDefaut);
    const [lien, setLien] = React.useState('');

    React.useEffect(() => {
        if (ouvertParDefaut) setOuvert(true);
    }, [ouvertParDefaut]);

    const valider = (e: React.FormEvent) => {
        e.preventDefault();
        const propre = lien.trim();
        if (propre === '') return;
        onChanger(propre);
        setLien('');
    };

    return (
        <Collapsible open={ouvert} onOpenChange={setOuvert}>
            <CollapsibleTrigger className="text-muted-foreground hover:text-foreground flex items-center gap-1.5 text-xs">
                <ChevronRight className={cn('h-3.5 w-3.5 transition-transform', ouvert && 'rotate-90')} />
                {t('retenue', 'import_changer_fichier', 'Changer de fichier')}
            </CollapsibleTrigger>
            <CollapsibleContent>
                <form onSubmit={valider} className="mt-2 flex flex-col gap-2 sm:flex-row">
                    <Input
                        value={lien}
                        onChange={(e) => setLien(e.target.value)}
                        placeholder={t('retenue', 'import_lien_placeholder', 'Collez le lien du fichier des paiements')}
                        aria-label={t('retenue', 'import_lien_label', 'Lien du fichier')}
                        className="h-9 flex-1"
                    />
                    <Button type="submit" variant="outline" disabled={lien.trim() === '' || chargement}>
                        {chargement && <Spinner size="sm" className="mr-2 text-current" />}
                        {t('retenue', 'import_ouvrir_fichier', 'Ouvrir ce fichier')}
                    </Button>
                </form>
            </CollapsibleContent>
        </Collapsible>
    );
}

/** Le suivi des couleurs du fichier — ce qui est lié, ce qui reste à peindre, ce qui est perdu. */
function SuiviDuFichier({
    etat,
    onMettreAJourCouleurs,
    couleursEnCours,
}: {
    etat: EtatImport;
    onMettreAJourCouleurs: () => void;
    couleursEnCours: boolean;
}) {
    const { t } = useTranslation();
    const { suivi } = etat;

    return (
        <section className="flex flex-col gap-3 rounded-lg border px-3 py-3">
            <h3 className="text-sm font-medium">{t('retenue', 'import_suivi_titre', 'Suivi du fichier')}</h3>

            <dl className="grid grid-cols-1 gap-2 text-sm sm:grid-cols-3">
                <Chiffre libelle={t('retenue', 'import_suivi_total', 'Lignes suivies')} valeur={suivi.total} />
                <Chiffre libelle={t('retenue', 'import_suivi_en_attente', 'Couleurs à mettre à jour')} valeur={suivi.en_attente} />
                <Chiffre libelle={t('retenue', 'import_suivi_introuvables', 'Lignes introuvables')} valeur={suivi.introuvables.length} />
            </dl>

            {suivi.introuvables.length > 0 && (
                <div className="flex flex-col gap-1">
                    <p className="text-muted-foreground text-xs">
                        {t(
                            'retenue',
                            'import_suivi_introuvables_aide',
                            'Ces lignes du manager ne retrouvent plus leur ligne dans le fichier : leur couleur ne peut plus y être posée.',
                        )}
                    </p>
                    <ul className="max-h-32 overflow-y-auto rounded-md border text-xs">
                        {suivi.introuvables.map((l) => (
                            <li key={l.ligne_id} className="flex flex-wrap gap-x-2 border-b px-2 py-1 last:border-b-0">
                                <span className="font-medium">{l.nom}</span>
                                <span className="text-muted-foreground">
                                    {remplir(t('retenue', 'import_suivi_position', ':onglet, ligne :numero'), { onglet: l.onglet, numero: l.numero })}
                                </span>
                            </li>
                        ))}
                    </ul>
                </div>
            )}

            {suivi.derniere_erreur && (
                <p className="text-xs text-red-700">
                    {t('retenue', 'import_suivi_derniere_erreur', 'Dernière erreur :')} {suivi.derniere_erreur}
                </p>
            )}

            <LegendeCouleurs />

            <Button variant="outline" size="sm" className="w-fit" onClick={onMettreAJourCouleurs} disabled={couleursEnCours}>
                {couleursEnCours ? <Spinner size="sm" className="mr-2 text-current" /> : <Palette className="mr-2 h-4 w-4" />}
                {t('retenue', 'import_couleurs_bouton', 'Mettre à jour les couleurs du fichier')}
            </Button>
        </section>
    );
}

function Chiffre({ libelle, valeur }: { libelle: string; valeur: number }) {
    return (
        <div className="flex flex-col rounded-md border px-2.5 py-1.5">
            <dt className="text-muted-foreground text-[11px] tracking-wide uppercase">{libelle}</dt>
            <dd className="font-semibold tabular-nums">{valeur}</dd>
        </div>
    );
}
