import { Button } from '@/components/ui/button';
import { Calendar } from '@/components/ui/calendar';
import { Input } from '@/components/ui/input';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { cn } from '@/lib/utils';
import { CalendarIcon } from 'lucide-react';
import * as React from 'react';

/**
 * Date input that always displays in French jj/mm/aaaa format regardless of
 * the user's browser locale (the native <input type="date"> can't be forced
 * to a specific format — on an en-US browser it renders mm/dd/yyyy). Click the
 * calendar icon to pick a date via the shadcn Calendar popover, or type the
 * date directly in jj/mm/aaaa.
 *
 * Values are exchanged with the parent in ISO `YYYY-MM-DD` so backend
 * payloads stay consistent.
 */
interface Props {
    value: string; // YYYY-MM-DD
    onChange: (iso: string) => void;
    disabled?: boolean;
    className?: string;
    inputClassName?: string;
    onCommit?: () => void; // fired on blur — same semantics as the old `onBlur`
    /** Earliest selectable day, ISO. Typed values outside the bounds are rejected. */
    min?: string;
    /** Latest selectable day, ISO. */
    max?: string;
    /** Forwarded to the text field so a <Label htmlFor> actually focuses it. */
    id?: string;
    /** Kept so replacing a native `<input type="date" required>` doesn't drop
     *  the browser-side "champ obligatoire" guard on submit. */
    required?: boolean;
    /** Forwarded to the text field (native date inputs ignore it, ours doesn't). */
    placeholder?: string;
}

function isoToFr(iso: string): string {
    if (!iso) return '';
    const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
    if (!m) return iso;
    return `${m[3]}/${m[2]}/${m[1]}`;
}

function frToIso(fr: string): string | null {
    const m = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/.exec(fr.trim());
    if (!m) return null;
    const dd = m[1].padStart(2, '0');
    const mo = m[2].padStart(2, '0');
    const yy = m[3];
    // Sanity: reject impossible values
    const day = Number(dd),
        mon = Number(mo);
    if (day < 1 || day > 31 || mon < 1 || mon > 12) return null;
    return `${yy}-${mo}-${dd}`;
}

/**
 * Calendar day → ISO using its LOCAL parts. `toISOString()` would convert to
 * UTC first, so east-of-Greenwich users (Tunisia is UTC+1) would get the
 * PREVIOUS day for every pick.
 */
function toLocalIso(date: Date): string {
    const m = String(date.getMonth() + 1).padStart(2, '0');
    const d = String(date.getDate()).padStart(2, '0');
    return `${date.getFullYear()}-${m}-${d}`;
}

/**
 * Aujourd'hui en ISO local. À utiliser partout plutôt que
 * `new Date().toISOString().slice(0, 10)` : `toISOString()` bascule en UTC, donc
 * en Tunisie (UTC+1) toute saisie faite entre minuit et 1 h du matin serait
 * datée de la VEILLE.
 */
export function todayIso(): string {
    return toLocalIso(new Date());
}

export function FrDateInput({ value, onChange, disabled, className, inputClassName, onCommit, min, max, id, required, placeholder }: Props) {
    const [text, setText] = React.useState(isoToFr(value));
    const [open, setOpen] = React.useState(false);

    // Keep local text in sync when the parent value changes (e.g. after a
    // server round-trip that resets the row).
    React.useEffect(() => setText(isoToFr(value)), [value]);

    const commit = () => {
        // Champ vidé = date retirée. Le natif `<input type="date">` émettait ''
        // via son bouton d'effacement ; sans ce cas, une borne de filtre posée
        // une fois ne pouvait plus JAMAIS être retirée (le champ paraissait
        // vide mais la valeur précédente restait envoyée) — cf. « date vide =
        // toutes les dates » de la synchro des avis et la période
        // personnalisée des KPI logements.
        if (text.trim() === '') {
            if (value !== '') {
                onChange('');
            }
            onCommit?.();
            return;
        }

        const iso = frToIso(text);
        const withinBounds = iso !== null && (!min || iso >= min) && (!max || iso <= max);

        if (iso && withinBounds && iso !== value) {
            onChange(iso);
        } else if (!withinBounds) {
            // invalid or out of bounds — revert display to last good value
            setText(isoToFr(value));
        }
        onCommit?.();
    };

    const selectedDate = value ? new Date(value + 'T00:00:00') : undefined;

    return (
        <div className={cn('flex items-center gap-1', className)}>
            <Input
                id={id}
                type="text"
                inputMode="numeric"
                placeholder={placeholder ?? 'jj/mm/aaaa'}
                value={text}
                disabled={disabled}
                required={required}
                onChange={(e) => setText(e.target.value)}
                onBlur={commit}
                onKeyDown={(e) => {
                    if (e.key === 'Enter') {
                        e.preventDefault();
                        (e.target as HTMLInputElement).blur();
                    }
                }}
                className={cn('h-11 tabular-nums', inputClassName)}
            />
            {/* `modal` est indispensable, pour DEUX raisons :
                1. Dialogs (Encaisser / Décaisser, édition dépense, édition
                   recouvrement) : un Dialog Radix pose `pointer-events: none`
                   sur le body tant qu'il est ouvert ; le calendrier étant rendu
                   dans un portal enfant du body, les clics sur les jours
                   étaient avalés et la date semblait impossible à changer.
                2. Popovers imbriqués (filtres des avis) : le portal sort du DOM
                   du panneau parent, donc son clic compterait comme
                   « extérieur » et le refermerait. En `modal`, Radix neutralise
                   le renvoi des couches inférieures.
                Ne pas le retirer sans vérifier ces deux écrans. */}
            <Popover open={open} onOpenChange={setOpen} modal>
                <PopoverTrigger asChild>
                    <Button
                        type="button"
                        variant="ghost"
                        size="icon"
                        className="h-11 w-9 shrink-0"
                        disabled={disabled}
                        aria-label="Choisir une date"
                    >
                        <CalendarIcon className="h-4 w-4" />
                    </Button>
                </PopoverTrigger>
                {/*
                  * `pointer-events-auto` est indispensable : dans une boîte de
                  * dialogue Radix (modale), le <body> reçoit `pointer-events: none`
                  * et le calendrier — rendu dans un portail HORS du dialogue —
                  * en hérite. Il s'affiche alors normalement mais AUCUN clic ne
                  * l'atteint : impossible de choisir une date. Mesuré dans le
                  * navigateur, pas déduit.
                  *
                  * `z-[60]` : DialogContent est en z-50, un PopoverContent laissé
                  * en z-50 se retrouve peint SOUS le dialog. Les deux fixes
                  * couvrent deux bugs distincts du même calendrier-dans-Dialog —
                  * l'un l'interactivité, l'autre l'empilement visuel — et ne se
                  * chevauchent pas : ne retirer ni l'un ni l'autre.
                  */}
                <PopoverContent className="pointer-events-auto z-[60] w-auto p-0" align="start">
                    {/* `fromDate`/`toDate` en plus de `disabled` : le premier
                        borne la LISTE DES ANNÉES du nouvel en-tête (TL-267), le
                        second grise les jours. Sans les bornes, le champ « À »
                        proposerait des années antérieures à la date de début
                        déjà saisie dans « De ». */}
                    <Calendar
                        mode="single"
                        selected={selectedDate}
                        defaultMonth={selectedDate}
                        fromDate={min ? new Date(min + 'T00:00:00') : undefined}
                        toDate={max ? new Date(max + 'T00:00:00') : undefined}
                        disabled={[
                            ...(min ? [{ before: new Date(min + 'T00:00:00') }] : []),
                            ...(max ? [{ after: new Date(max + 'T00:00:00') }] : []),
                        ]}
                        onSelect={(d) => {
                            if (!d) return;
                            const iso = toLocalIso(d);
                            setText(isoToFr(iso));
                            onChange(iso);
                            setOpen(false);
                            onCommit?.();
                        }}
                    />
                </PopoverContent>
            </Popover>
        </div>
    );
}
