import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { Spinner } from '@/components/ui/spinner';
import { useTranslation } from '@/hooks/use-translation';
import { cn } from '@/lib/utils';
import { Link, router } from '@inertiajs/react';
import axios from 'axios';
import { CircleCheck, CloudUpload, FileText, TriangleAlert } from 'lucide-react';
import { useMemo, useRef, useState, type DragEvent } from 'react';
import { toast } from 'sonner';
import { fmtMontant } from '../format';
import type { AnalyseRapport, Devise, SejourRapport } from '../types';
import { Point, SejourLigne, type Tr } from './sejour-rapport';

interface RapportAirbnbSheetProps {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    tvaDefaut: number;
}

type Regroupement = 'global' | 'proprietaire';

/** Même plafond que `RapportAirbnbRequest` (config `factures.import.max_kb`). */
const TAILLE_MAX = 10 * 1024 * 1024;

const estSelectionnable = (s: SejourRapport) => s.erreur === null && s.deja_facturee === null && s.ht !== null;

function messageErreur(e: unknown): string | null {
    return axios.isAxiosError<{ message?: string }>(e) ? (e.response?.data?.message ?? null) : null;
}

/**
 * Rapport de versement Airbnb → brouillon de la facture de commission.
 *
 * Le serveur lit le rapport, retrouve chaque réservation et calcule la ligne
 * selon la règle de paiement du logement ; ce panneau montre le calcul de
 * chaque séjour pour qu'il soit relu AVANT le brouillon, et laisse choisir le
 * logement d'un séjour que le Manager n'a pas su rapprocher. Aucun montant
 * n'est calculé ici.
 */
export function RapportAirbnbSheet({ open, onOpenChange, tvaDefaut }: RapportAirbnbSheetProps) {
    const { t } = useTranslation();
    const tr = (cle: string, fr: string) => t('factures', cle, fr);

    const champ = useRef<HTMLInputElement>(null);
    const [analyse, setAnalyse] = useState<AnalyseRapport | null>(null);
    const [affectations, setAffectations] = useState<Record<string, number>>({});
    const [selection, setSelection] = useState<Set<string>>(() => new Set());
    const [regroupement, setRegroupement] = useState<Regroupement>('global');
    const [lecture, setLecture] = useState(false);
    const [recalcul, setRecalcul] = useState<string | null>(null);
    const [edition, setEdition] = useState<string | null>(null);
    const [erreur, setErreur] = useState<string | null>(null);
    const [survol, setSurvol] = useState(false);
    const [envoi, setEnvoi] = useState(false);

    const devise: Devise = analyse?.devise ?? 'USD';
    const occupe = lecture || envoi || recalcul !== null;

    /** Nouvelle analyse : garde les séjours déjà cochés qui restent facturables, et coche `aussi`. */
    const appliquer = (data: AnalyseRapport, precedente: Set<string> | null, aussi?: string) => {
        setAnalyse(data);
        const facturables = data.sejours.filter(estSelectionnable).map((s) => s.cle);
        setSelection(new Set(precedente === null ? facturables : facturables.filter((c) => precedente.has(c) || c === aussi)));
    };

    const lire = (f: File | undefined) => {
        if (!f || occupe) return;
        if (!/\.(pdf|csv|txt)$/i.test(f.name)) {
            setErreur(tr('rapport.erreur_type', 'Déposez le rapport en PDF ou en CSV.'));
            return;
        }
        if (f.size > TAILLE_MAX) {
            setErreur(tr('rapport.erreur_taille', 'Le fichier dépasse 10 Mo.'));
            return;
        }

        const formulaire = new FormData();
        formulaire.append('fichier', f);
        setErreur(null);
        setLecture(true);
        axios
            .post<AnalyseRapport>(route('factures.rapport-airbnb.analyse'), formulaire)
            .then(({ data }) => {
                setAffectations({});
                setEdition(null);
                appliquer(data, null);
            })
            .catch((e: unknown) => setErreur(messageErreur(e) ?? tr('rapport.erreur_lecture', 'La lecture du rapport a échoué. Réessayez.')))
            .finally(() => {
                setLecture(false);
                if (champ.current) champ.current.value = '';
            });
    };

    const affecter = (cle: string, logementId: number) => {
        if (!analyse || occupe) return;
        const suivantes = { ...affectations, [cle]: logementId };
        setRecalcul(cle);
        axios
            .post<AnalyseRapport>(route('factures.rapport-airbnb.analyse'), { jeton: analyse.jeton, affectations: suivantes })
            .then(({ data }) => {
                setAffectations(suivantes);
                setEdition(null);
                appliquer(data, selection, cle);
            })
            .catch((e: unknown) => toast.error(messageErreur(e) ?? tr('rapport.erreur_recalcul', 'Le recalcul a échoué. Réessayez.')))
            .finally(() => setRecalcul(null));
    };

    const recommencer = () => {
        setAnalyse(null);
        setAffectations({});
        setSelection(new Set());
        setEdition(null);
        setErreur(null);
    };

    const sejours = useMemo(() => analyse?.sejours ?? [], [analyse]);
    const selectionnables = useMemo(() => sejours.filter(estSelectionnable), [sejours]);
    // Factures (ou brouillons) qui portent déjà des séjours de ce rapport : dites
    // d'emblée, avec le lien pour les ouvrir, plutôt que découvertes ligne à ligne.
    const dejaSur = useMemo(() => {
        const parFacture = new Map<number, { id: number; numero: string | null; n: number }>();
        sejours.forEach((s) => {
            if (!s.deja_facturee) return;
            const f = parFacture.get(s.deja_facturee.id) ?? { ...s.deja_facturee, n: 0 };
            parFacture.set(f.id, { ...f, n: f.n + 1 });
        });
        return [...parFacture.values()];
    }, [sejours]);
    const choisis = useMemo(() => sejours.filter((s) => selection.has(s.cle)), [sejours, selection]);

    const totaux = useMemo(() => {
        const ht = choisis.reduce((acc, s) => acc + (s.ht ?? 0), 0);
        const recu = choisis.reduce((acc, s) => acc + s.recu, 0);
        return { ht, tva: (ht * tvaDefaut) / 100, recu };
    }, [choisis, tvaDefaut]);

    const nbBrouillons =
        choisis.length === 0 ? 0 : regroupement === 'global' ? 1 : new Set(choisis.map((s) => s.logement?.proprietaire_id ?? 0)).size;
    const toutCoche = selectionnables.length > 0 && selectionnables.every((s) => selection.has(s.cle));

    const basculer = (cle: string) =>
        setSelection((prec) => {
            const suivante = new Set(prec);
            if (suivante.has(cle)) suivante.delete(cle);
            else suivante.add(cle);
            return suivante;
        });

    const toutBasculer = () => setSelection(toutCoche ? new Set() : new Set(selectionnables.map((s) => s.cle)));

    const fermer = (ouvert: boolean) => {
        if (envoi || lecture) return;
        onOpenChange(ouvert);
    };

    const creer = () => {
        if (!analyse || choisis.length === 0 || occupe) return;
        router.post(
            route('factures.rapport-airbnb.store'),
            { jeton: analyse.jeton, cles: choisis.map((s) => s.cle), affectations, regroupement },
            {
                preserveScroll: true,
                preserveState: 'errors',
                onStart: () => setEnvoi(true),
                onFinish: () => setEnvoi(false),
                onError: (errors) =>
                    toast.error(errors.cles ?? errors.jeton ?? Object.values(errors)[0] ?? tr('rapport.erreur_creation', 'Création impossible.')),
            },
        );
    };

    const deposer = (event: DragEvent<HTMLDivElement>) => {
        event.preventDefault();
        setSurvol(false);
        lire(event.dataTransfer.files?.[0]);
    };

    const libelleCreer =
        nbBrouillons <= 1
            ? tr('rapport.creer_un', 'Créer le brouillon')
            : tr('rapport.creer_n', 'Créer :n brouillons').replace(':n', String(nbBrouillons));

    return (
        <Sheet open={open} onOpenChange={fermer}>
            <SheetContent side="right" className="w-full gap-0 sm:max-w-3xl">
                <SheetHeader className="border-b pr-10">
                    <SheetTitle>{tr('rapport.titre', 'Rapport de versement Airbnb')}</SheetTitle>
                    <SheetDescription>
                        {tr(
                            'rapport.description',
                            'Une ligne « commission sur location » par séjour du rapport, calculée selon la règle de paiement du logement. Relisez chaque calcul avant de créer le brouillon.',
                        )}
                    </SheetDescription>
                </SheetHeader>

                <div className="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4">
                    {analyse === null ? (
                        <>
                            {lecture ? (
                                <div
                                    className="flex flex-col items-center gap-3 rounded-xl border border-dashed px-4 py-10 text-center"
                                    role="status"
                                >
                                    <Spinner size="lg" />
                                    <div className="text-sm font-medium">
                                        {tr('rapport.lecture', 'Lecture du rapport et recherche des réservations…')}
                                    </div>
                                </div>
                            ) : (
                                <div
                                    onDragOver={(e) => {
                                        e.preventDefault();
                                        setSurvol(true);
                                    }}
                                    onDragLeave={() => setSurvol(false)}
                                    onDrop={deposer}
                                    className={cn(
                                        'flex flex-col items-center gap-3 rounded-xl border-2 border-dashed px-4 py-10 text-center transition-colors',
                                        survol ? 'border-secondary bg-secondary/5' : 'border-border bg-muted/30',
                                    )}
                                >
                                    <CloudUpload className={cn('size-8', survol ? 'text-secondary' : 'text-muted-foreground')} aria-hidden />
                                    <div className="text-sm">
                                        <span className="font-medium">{tr('rapport.deposer', 'Glissez le rapport Airbnb ici')}</span>{' '}
                                        <span className="text-muted-foreground">{tr('import.ou', 'ou')}</span>
                                    </div>
                                    <Button type="button" variant="outline" size="sm" onClick={() => champ.current?.click()}>
                                        {tr('import.parcourir', 'Parcourir')}
                                    </Button>
                                    <div className="text-muted-foreground max-w-md text-xs">
                                        {tr(
                                            'rapport.contraintes',
                                            'PDF ou CSV, 10 Mo au plus. Lignes attendues : Payout, Réservation, Versement du co-hôte.',
                                        )}
                                    </div>
                                    <input
                                        ref={champ}
                                        type="file"
                                        accept=".pdf,.csv,.txt,application/pdf,text/csv"
                                        className="sr-only"
                                        tabIndex={-1}
                                        onChange={(e) => lire(e.target.files?.[0])}
                                    />
                                </div>
                            )}
                            {erreur && (
                                <div
                                    className="flex items-start gap-2 rounded-lg border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-800"
                                    role="alert"
                                >
                                    <TriangleAlert className="mt-0.5 size-4 shrink-0" aria-hidden />
                                    {erreur}
                                </div>
                            )}
                        </>
                    ) : (
                        <>
                            {/* Fichier + contrôle du rapport */}
                            <div className="flex items-center gap-3 rounded-lg border px-3 py-2">
                                <FileText className="text-primary size-5 shrink-0" aria-hidden />
                                <div className="min-w-0 flex-1">
                                    <div className="truncate text-sm font-medium" title={analyse.fichier}>
                                        {analyse.fichier}
                                    </div>
                                    <div className="text-muted-foreground text-xs">
                                        {tr('rapport.nb_sejours', ':n séjour(s) lus').replace(':n', String(sejours.length))}
                                    </div>
                                </div>
                                <Button type="button" variant="outline" size="sm" onClick={recommencer} disabled={occupe}>
                                    {tr('rapport.autre', 'Autre rapport')}
                                </Button>
                            </div>

                            <ControleRapport analyse={analyse} tr={tr} />

                            {dejaSur.length > 0 && (
                                <div
                                    className="flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900"
                                    role="status"
                                >
                                    <TriangleAlert className="mt-0.5 size-4 shrink-0" aria-hidden />
                                    <span>
                                        {tr('rapport.deja_factures', ':n séjour(s) de ce rapport sont déjà facturés, donc non cochables :').replace(
                                            ':n',
                                            String(dejaSur.reduce((t, f) => t + f.n, 0)),
                                        )}{' '}
                                        {dejaSur.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 ?? tr('rapport.brouillon', 'un brouillon')}
                                                </Link>
                                            </span>
                                        ))}
                                        .{' '}
                                        {tr(
                                            'rapport.deja_factures_aide',
                                            'Pour les refacturer, supprimez ou annulez cette pièce, puis déposez le rapport à nouveau.',
                                        )}
                                    </span>
                                </div>
                            )}

                            <RadioGroup
                                value={regroupement}
                                onValueChange={(v) => setRegroupement(v === 'proprietaire' ? 'proprietaire' : 'global')}
                                className="grid gap-2 sm:grid-cols-2"
                                disabled={occupe}
                            >
                                {(
                                    [
                                        {
                                            valeur: 'global',
                                            titre: tr('commission.global', 'Une facture « Propriétaires »'),
                                            aide: tr('commission.global_aide', "Comme aujourd'hui : un seul client, une ligne par réservation"),
                                        },
                                        {
                                            valeur: 'proprietaire',
                                            titre: tr('commission.par_proprietaire', 'Une facture par propriétaire'),
                                            aide: tr('commission.par_proprietaire_aide', 'Chaque propriétaire reçoit la sienne, à son nom'),
                                        },
                                    ] as const
                                ).map((o) => (
                                    <Label
                                        key={o.valeur}
                                        htmlFor={`rapport-regroupement-${o.valeur}`}
                                        className={cn(
                                            'flex cursor-pointer items-start gap-3 rounded-lg border p-3 font-normal transition-colors',
                                            regroupement === o.valeur ? 'border-secondary bg-secondary/5' : 'hover:bg-muted/40',
                                        )}
                                    >
                                        <RadioGroupItem value={o.valeur} id={`rapport-regroupement-${o.valeur}`} className="mt-0.5" />
                                        <span className="grid gap-0.5">
                                            <span className="text-sm font-medium">{o.titre}</span>
                                            <span className="text-muted-foreground text-xs">{o.aide}</span>
                                        </span>
                                    </Label>
                                ))}
                            </RadioGroup>

                            {/* Récapitulatif de la sélection */}
                            <div className="bg-muted/40 flex flex-wrap items-center gap-x-3 gap-y-1 rounded-lg border px-3 py-2 text-sm tabular-nums">
                                <span>
                                    <span className="font-semibold">{choisis.length}</span> {tr('rapport.selectionnes', 'séjour(s) sélectionné(s)')}
                                </span>
                                <Point />
                                <span>
                                    {tr('commission.commission_ht', 'Commission HT')}{' '}
                                    <span className="text-primary font-semibold">{fmtMontant(totaux.ht, devise)}</span>
                                </span>
                                <Point />
                                <span title={tr('commission.tva_estimee', 'Estimation : le montant exact est calculé sur la facture.')}>
                                    {tr('commission.tva', 'TVA :taux %').replace(':taux', String(tvaDefaut))} ≈ {fmtMontant(totaux.tva, devise)}
                                </span>
                                <Point />
                                <span>
                                    {tr('rapport.montant_airbnb', 'Reçu d’Airbnb')} {fmtMontant(totaux.recu, devise)}
                                </span>
                            </div>

                            {/* Séjours */}
                            <div className="shrink-0 overflow-hidden rounded-lg border">
                                <div className="bg-muted/40 flex items-center gap-3 border-b px-3 py-2">
                                    <Checkbox
                                        checked={toutCoche ? true : choisis.length === 0 ? false : 'indeterminate'}
                                        onCheckedChange={toutBasculer}
                                        disabled={occupe || selectionnables.length === 0}
                                        aria-label={tr('commission.tout_cocher', 'Tout cocher')}
                                    />
                                    <span className="text-muted-foreground flex-1 text-xs font-medium">
                                        {tr('rapport.col_sejour', 'Séjour et calcul')}
                                    </span>
                                    <span className="text-muted-foreground text-xs font-medium">{tr('rapport.col_ht', 'Ligne HT')}</span>
                                </div>
                                <ul className="divide-y">
                                    {sejours.map((s) => (
                                        <SejourLigne
                                            key={s.cle}
                                            sejour={s}
                                            devise={devise}
                                            logements={analyse.logements}
                                            tr={tr}
                                            actif={estSelectionnable(s)}
                                            occupe={occupe}
                                            enEdition={edition === s.cle}
                                            enRecalcul={recalcul === s.cle}
                                            onEdition={(ouverte) => setEdition(ouverte ? s.cle : null)}
                                            onAffecter={(logementId) => affecter(s.cle, logementId)}
                                            selection={{ cochee: selection.has(s.cle), onBasculer: () => basculer(s.cle) }}
                                        />
                                    ))}
                                </ul>
                            </div>
                        </>
                    )}
                </div>

                {analyse !== null && (
                    <SheetFooter className="mt-0 flex-col gap-3 border-t pr-24 sm:flex-row sm:items-center sm:justify-between">
                        <p className="text-muted-foreground text-xs">
                            {tr('rapport.exclus', 'Non cochables : séjours déjà facturés, sans logement, ou dont le montant est à vérifier.')}
                        </p>
                        <Button
                            type="button"
                            className="bg-secondary hover:bg-secondary/90"
                            onClick={creer}
                            disabled={choisis.length === 0 || occupe}
                        >
                            {envoi && <Spinner size="sm" className="text-white" />}
                            {libelleCreer}
                        </Button>
                    </SheetFooter>
                )}
            </SheetContent>
        </Sheet>
    );
}

/**
 * Contrôle du rapport : la somme de ses lignes doit égaler le versement
 * Airbnb. Vert = tout le rapport est lu ; ambre = une ligne a échappé.
 */
function ControleRapport({ analyse, tr }: { analyse: AnalyseRapport; tr: Tr }) {
    const devise = analyse.devise;
    const juste = analyse.payout !== null && Math.abs(analyse.payout - analyse.somme) < 0.01;

    return (
        <div
            className={cn(
                'flex items-start gap-2 rounded-lg border px-3 py-2 text-sm',
                juste ? 'border-emerald-200 bg-emerald-50 text-emerald-900' : 'border-amber-200 bg-amber-50 text-amber-900',
            )}
            role="status"
        >
            {juste ? (
                <CircleCheck className="mt-0.5 size-4 shrink-0" aria-hidden />
            ) : (
                <TriangleAlert className="mt-0.5 size-4 shrink-0" aria-hidden />
            )}
            <div className="grid gap-0.5">
                <span>
                    {analyse.payout === null
                        ? tr('rapport.sans_payout', 'Pas de ligne « Payout » dans le rapport : somme des lignes :somme.').replace(
                              ':somme',
                              fmtMontant(analyse.somme, devise),
                          )
                        : juste
                          ? tr('rapport.controle_ok', 'Toutes les lignes additionnées font le versement Airbnb : :payout.').replace(
                                ':payout',
                                fmtMontant(analyse.payout, devise),
                            )
                          : tr('rapport.controle_ecart', 'Les lignes font :somme pour un versement de :payout : une ligne n’a pas été lue.')
                                .replace(':somme', fmtMontant(analyse.somme, devise))
                                .replace(':payout', fmtMontant(analyse.payout, devise))}
                </span>
                {analyse.autres.length > 0 && (
                    <span className="text-xs">
                        {tr('rapport.autres', 'Non facturées (autre type de ligne) :')}{' '}
                        {analyse.autres.map((a) => `${a.type} ${fmtMontant(a.montant, devise)}`).join(' · ')}
                    </span>
                )}
            </div>
        </div>
    );
}
