import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { Switch } from '@/components/ui/switch';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Textarea } from '@/components/ui/textarea';
import { cn } from '@/lib/utils';
import type { AutomationLanguage, AutomationSource, AutomationTrigger, PlaceholderOption, PropertyOption, RuleRow } from '@/types/automatisations';
import { useForm } from '@inertiajs/react';
import { AlertTriangle, Info } from 'lucide-react';
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'sonner';
import { describeTrigger, insertAtCursor, TONE_CLASSES, usesOffset, usesSendTime } from './automatisations-format';
import { errorText, sourceLabel, triggerLabel, type Translate } from './labels';
import { PropertyChecklist } from './PropertyChecklist';
import {
    BODY_MAX,
    emptyRuleForm,
    ruleFormFromRow,
    rulePayload,
    SEND_TIME_MAX,
    SEND_TIME_MIN,
    SUBJECT_MAX,
    validateRuleForm,
    type RuleFormData,
} from './rule-form';
import { RulePreview } from './RulePreview';

const NS = 'automatisations';

interface Props {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    /** La règle modifiée, ou null pour en créer une. */
    rule: RuleRow | null;
    /** Création seulement : le formulaire de départ (modèle, copie d'une règle) au lieu d'une règle vide. */
    initial?: RuleFormData | null;
    properties: PropertyOption[];
    placeholders: PlaceholderOption[];
    triggers: AutomationTrigger[];
    sources: AutomationSource[];
    locale: string;
    t: Translate;
}

function FieldError({ message, t }: { message: string | undefined; t: Translate }) {
    if (!message) return null;

    return <p className="text-xs text-red-600">{errorText(message, t)}</p>;
}

function Section({ title, hint, children }: { title: string; hint?: string; children: React.ReactNode }) {
    return (
        <section className="flex flex-col gap-2">
            <div>
                <h4 className="text-sm font-medium">{title}</h4>
                {hint && <p className="text-muted-foreground text-xs">{hint}</p>}
            </div>
            {children}
        </section>
    );
}

/**
 * Créer / modifier une règle. Le formulaire est contrôlé côté écran par le
 * même schéma que le serveur (rule-form.ts), puis envoyé par `useForm`.
 *
 * Les variables s'insèrent au curseur dans le texte de la langue ouverte ;
 * l'aperçu (RulePreview) montre le message réel des prochaines réservations.
 */
export function RuleSheet({ open, onOpenChange, rule, initial = null, properties, placeholders, triggers, sources, locale, t }: Props) {
    const form = useForm<RuleFormData>(emptyRuleForm(sources));
    const [clientErrors, setClientErrors] = useState<Record<string, string>>({});
    const [bodyLang, setBodyLang] = useState<AutomationLanguage>('fr');
    const bodyRefs = { fr: useRef<HTMLTextAreaElement>(null), en: useRef<HTMLTextAreaElement>(null) };
    const pendingCursor = useRef<{ lang: AutomationLanguage; position: number } | null>(null);
    const knownTokens = useMemo(() => placeholders.map((placeholder) => placeholder.key), [placeholders]);

    // Le formulaire repart de la règle (ou d'un modèle, d'une copie, d'une règle neuve) à chaque ouverture.
    useEffect(() => {
        if (!open) return;
        form.setData(rule ? ruleFormFromRow(rule) : (initial ?? emptyRuleForm(sources)));
        form.clearErrors();
        setClientErrors({});
        setBodyLang('fr');
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [open, rule, initial]);

    const data = form.data;
    const serverErrors = form.errors as Record<string, string>;
    const errors: Record<string, string> = { ...serverErrors, ...clientErrors };
    const errorFor = (...keys: string[]): string | undefined => keys.map((key) => errors[key]).find((message) => message !== undefined);
    const propertyError = errorFor('property_ids') ?? Object.entries(errors).find(([key]) => key.startsWith('property_ids.'))?.[1];

    const setField = <K extends keyof RuleFormData>(key: K, value: RuleFormData[K]) => {
        form.setData(key, value);
        if (Object.keys(clientErrors).length > 0) setClientErrors({});
    };

    const previewPayload = useMemo(
        () => (Object.keys(validateRuleForm(data, knownTokens, true)).length === 0 ? rulePayload(data) : null),
        [data, knownTokens],
    );

    const insertToken = (key: string) => {
        const textarea = bodyRefs[bodyLang].current;
        const current = data.bodies[bodyLang];
        const start = textarea?.selectionStart ?? current.length;
        const end = textarea?.selectionEnd ?? current.length;
        const next = insertAtCursor(current, `{${key}}`, start, end);

        pendingCursor.current = { lang: bodyLang, position: next.cursor };
        setField('bodies', { ...data.bodies, [bodyLang]: next.text });
    };

    // Le curseur se replace APRÈS que React a écrit la nouvelle valeur : une
    // valeur posée par programme renvoie le curseur en fin de texte, et un
    // requestAnimationFrame passait AVANT l'écriture (mesuré dans le navigateur).
    useLayoutEffect(() => {
        const pending = pendingCursor.current;
        if (!pending) return;
        pendingCursor.current = null;

        const element = bodyRefs[pending.lang].current;
        if (!element) return;
        element.focus();
        element.setSelectionRange(pending.position, pending.position);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [data.bodies]);

    const toggleSource = (source: AutomationSource) => {
        setField('sources', data.sources.includes(source) ? data.sources.filter((value) => value !== source) : [...data.sources, source]);
    };

    const submit = () => {
        const found = validateRuleForm(data, knownTokens);
        if (Object.keys(found).length > 0) {
            setClientErrors(found);
            toast.error(errorText(Object.values(found)[0], t));
            return;
        }

        form.transform((values) => rulePayload(values));
        const options = {
            preserveScroll: true,
            onSuccess: () => onOpenChange(false),
            onError: (received: Record<string, string>) => {
                const first = Object.values(received)[0];
                toast.error(first ?? t(NS, 'toast_error', 'Une erreur est survenue.'));
            },
        };

        if (rule) {
            form.put(route('automatisations.rules.update', rule.id), options);
        } else {
            form.post(route('automatisations.rules.store'), options);
        }
    };

    const becomesAuto = rule !== null && rule.isActive && rule.mode !== 'auto' && data.mode === 'auto';

    return (
        <Sheet open={open} onOpenChange={onOpenChange}>
            <SheetContent side="right" className="flex w-[min(96vw,760px)] flex-col gap-0 p-0 sm:max-w-none">
                <SheetHeader className="border-b">
                    <SheetTitle>{rule ? t(NS, 'sheet_edit_title', 'Modifier la règle') : t(NS, 'sheet_create_title', 'Nouvelle règle')}</SheetTitle>
                    <SheetDescription>
                        {rule
                            ? t(
                                  NS,
                                  'sheet_edit_description',
                                  'Si le texte ou le moment change, les messages pas encore partis sont re-préparés avec la nouvelle version.',
                              )
                            : t(
                                  NS,
                                  'sheet_create_description',
                                  'Une nouvelle règle est enregistrée éteinte : rien ne part avant que vous l’activiez.',
                              )}
                    </SheetDescription>
                </SheetHeader>

                <div className="flex flex-1 flex-col gap-5 overflow-y-auto p-4">
                    {Object.keys(serverErrors).length > 0 && (
                        <div className={cn('rounded-md border px-3 py-2 text-xs', TONE_CLASSES.red)}>
                            <p className="font-medium">{t(NS, 'errors_title', 'Impossible d’enregistrer :')}</p>
                            <ul className="mt-1 list-disc pl-4">
                                {Object.entries(serverErrors).map(([key, message]) => (
                                    <li key={key}>{errorText(message, t)}</li>
                                ))}
                            </ul>
                        </div>
                    )}

                    <div className="flex flex-col gap-1">
                        <Label htmlFor="automation-rule-name">{t(NS, 'field_name', 'Nom de la règle')}</Label>
                        <Input
                            id="automation-rule-name"
                            value={data.name}
                            maxLength={120}
                            onChange={(event) => setField('name', event.target.value)}
                            placeholder={t(NS, 'field_name_placeholder', 'Ex. Instructions la veille de l’arrivée')}
                        />
                        <FieldError message={errorFor('name')} t={t} />
                    </div>

                    <Section title={t(NS, 'section_when', 'Quand')}>
                        <div className="grid gap-3 sm:grid-cols-3">
                            <div className="flex flex-col gap-1 sm:col-span-1">
                                <Label className="text-xs">{t(NS, 'field_trigger', 'Moment')}</Label>
                                <Select value={data.trigger} onValueChange={(value) => setField('trigger', value as AutomationTrigger)}>
                                    <SelectTrigger>
                                        <SelectValue />
                                    </SelectTrigger>
                                    <SelectContent>
                                        {triggers.map((trigger) => (
                                            <SelectItem key={trigger} value={trigger}>
                                                {triggerLabel(trigger, t)}
                                            </SelectItem>
                                        ))}
                                    </SelectContent>
                                </Select>
                            </div>

                            {usesOffset(data.trigger) && (
                                <div className="flex flex-col gap-1">
                                    <Label htmlFor="automation-rule-offset" className="text-xs">
                                        {t(NS, 'field_offset', 'Nombre de jours')}
                                    </Label>
                                    <Input
                                        id="automation-rule-offset"
                                        type="number"
                                        inputMode="numeric"
                                        min={1}
                                        max={60}
                                        value={data.offset_days}
                                        onChange={(event) => setField('offset_days', event.target.value)}
                                    />
                                    <FieldError message={errorFor('offset_days')} t={t} />
                                </div>
                            )}

                            {usesSendTime(data.trigger) ? (
                                <div className="flex flex-col gap-1">
                                    <Label htmlFor="automation-rule-time" className="text-xs">
                                        {t(NS, 'field_send_time', 'Heure d’envoi')}
                                    </Label>
                                    <Input
                                        id="automation-rule-time"
                                        type="time"
                                        min={SEND_TIME_MIN}
                                        max={SEND_TIME_MAX}
                                        step={300}
                                        value={data.send_time}
                                        onChange={(event) => setField('send_time', event.target.value)}
                                    />
                                    <FieldError message={errorFor('send_time')} t={t} />
                                </div>
                            ) : (
                                <div className="flex flex-col gap-1">
                                    <Label htmlFor="automation-rule-delay" className="text-xs">
                                        {t(NS, 'field_delay', 'Délai (minutes)')}
                                    </Label>
                                    <Input
                                        id="automation-rule-delay"
                                        type="number"
                                        inputMode="numeric"
                                        min={0}
                                        max={1440}
                                        value={data.delay_minutes}
                                        onChange={(event) => setField('delay_minutes', event.target.value)}
                                    />
                                    <FieldError message={errorFor('delay_minutes')} t={t} />
                                </div>
                            )}
                        </div>
                        <p className="text-muted-foreground text-xs">
                            <span className="text-foreground font-medium">
                                {describeTrigger(
                                    {
                                        trigger: data.trigger,
                                        offsetDays: Number.parseInt(data.offset_days, 10) || 0,
                                        sendTime: usesSendTime(data.trigger) ? data.send_time : null,
                                        delayMinutes: Number.parseInt(data.delay_minutes, 10) || 0,
                                    },
                                    locale,
                                )}
                            </span>
                            {' — '}
                            {t(NS, 'quiet_hours_hint', 'heure de Tunis ; le message part dans l’heure qui suit, jamais entre 22:00 et 08:00.')}
                        </p>
                    </Section>

                    <Section title={t(NS, 'section_mode', 'Mode')}>
                        <RadioGroup value={data.mode} onValueChange={(value) => setField('mode', value as RuleFormData['mode'])} className="gap-2">
                            <label
                                htmlFor="automation-mode-approval"
                                className="hover:bg-muted/40 flex cursor-pointer items-start gap-2 rounded-md border p-2.5"
                            >
                                <RadioGroupItem id="automation-mode-approval" value="approval" className="mt-0.5" />
                                <span>
                                    <span className="text-sm font-medium">{t(NS, 'mode_approval', 'À valider')}</span>
                                    <span className="text-muted-foreground block text-xs">
                                        {t(
                                            NS,
                                            'mode_approval_hint',
                                            'Le message est préparé au bon moment et attend votre relecture dans « À traiter ».',
                                        )}
                                    </span>
                                </span>
                            </label>
                            <label
                                htmlFor="automation-mode-auto"
                                className="hover:bg-muted/40 flex cursor-pointer items-start gap-2 rounded-md border p-2.5"
                            >
                                <RadioGroupItem id="automation-mode-auto" value="auto" className="mt-0.5" />
                                <span>
                                    <span className="text-sm font-medium">{t(NS, 'mode_auto', 'Automatique')}</span>
                                    <span className="text-muted-foreground block text-xs">
                                        {t(NS, 'mode_auto_hint', 'Le message part seul au voyageur, sans relecture.')}
                                    </span>
                                </span>
                            </label>
                        </RadioGroup>
                        {becomesAuto && (
                            <p className={cn('flex items-start gap-2 rounded-md border px-2.5 py-2 text-xs', TONE_CLASSES.amber)}>
                                <AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden="true" />
                                {t(
                                    NS,
                                    'mode_auto_active_warning',
                                    'Cette règle est active : dès l’enregistrement, ses messages partiront seuls — y compris ceux qui attendent aujourd’hui une validation.',
                                )}
                            </p>
                        )}
                    </Section>

                    <Section
                        title={t(NS, 'section_sources', 'Provenances')}
                        hint={t(NS, 'section_sources_hint', 'Les réservations concernées, selon leur canal.')}
                    >
                        <div className="flex flex-wrap gap-1.5">
                            {sources.map((source) => {
                                const active = data.sources.includes(source);

                                return (
                                    <button
                                        key={source}
                                        type="button"
                                        aria-pressed={active}
                                        onClick={() => toggleSource(source)}
                                        className={cn(
                                            'rounded-full border px-3 py-1 text-xs transition-colors',
                                            active
                                                ? 'border-primary bg-primary text-primary-foreground'
                                                : 'text-muted-foreground hover:bg-muted border-dashed',
                                        )}
                                    >
                                        {sourceLabel(source, t)}
                                    </button>
                                );
                            })}
                        </div>
                        <FieldError message={errorFor('sources')} t={t} />
                    </Section>

                    <Section title={t(NS, 'section_properties', 'Logements')}>
                        <RadioGroup
                            value={data.property_scope}
                            onValueChange={(value) => setField('property_scope', value as RuleFormData['property_scope'])}
                            className="flex flex-wrap gap-4"
                        >
                            <label htmlFor="automation-scope-all" className="flex cursor-pointer items-center gap-2 text-sm">
                                <RadioGroupItem id="automation-scope-all" value="all" />
                                {t(NS, 'scope_all', 'Tous les logements')}
                            </label>
                            <label htmlFor="automation-scope-selected" className="flex cursor-pointer items-center gap-2 text-sm">
                                <RadioGroupItem id="automation-scope-selected" value="selected" />
                                {t(NS, 'scope_selected', 'Une sélection')}
                            </label>
                        </RadioGroup>
                        {data.property_scope === 'selected' && (
                            <PropertyChecklist
                                properties={properties}
                                selected={data.property_ids}
                                onChange={(ids) => setField('property_ids', ids)}
                                t={t}
                            />
                        )}
                        <FieldError message={propertyError} t={t} />
                    </Section>

                    {data.trigger !== 'booking_confirmed' && (
                        <label className="flex cursor-pointer items-start justify-between gap-3 rounded-md border p-2.5">
                            <span>
                                <span className="text-sm font-medium">{t(NS, 'field_catch_up', 'Rattrapage')}</span>
                                <span className="text-muted-foreground block text-xs">
                                    {t(
                                        NS,
                                        'field_catch_up_hint',
                                        'Envoyer aussi aux réservations faites après le moment prévu, tant que le séjour n’a pas commencé.',
                                    )}
                                </span>
                            </span>
                            <Switch checked={data.catch_up} onCheckedChange={(value) => setField('catch_up', value)} />
                        </label>
                    )}

                    <Section
                        title={t(NS, 'section_message', 'Message')}
                        hint={t(NS, 'section_message_hint', 'Chaque voyageur reçoit sa langue ; à défaut, l’autre texte.')}
                    >
                        <Tabs value={bodyLang} onValueChange={(value) => setBodyLang(value as AutomationLanguage)} className="gap-1">
                            <TabsList>
                                <TabsTrigger value="fr" className="px-3">
                                    {t(NS, 'lang_fr', 'Français')}
                                </TabsTrigger>
                                <TabsTrigger value="en" className="px-3">
                                    {t(NS, 'lang_en', 'Anglais')}
                                </TabsTrigger>
                            </TabsList>
                            {(['fr', 'en'] as const).map((lang) => (
                                <TabsContent key={lang} value={lang} className="flex flex-col gap-2">
                                    <Textarea
                                        ref={bodyRefs[lang]}
                                        rows={7}
                                        maxLength={BODY_MAX}
                                        value={data.bodies[lang]}
                                        onChange={(event) => setField('bodies', { ...data.bodies, [lang]: event.target.value })}
                                        placeholder={
                                            lang === 'fr'
                                                ? t(NS, 'body_placeholder_fr', 'Bonjour {prenom}, …')
                                                : t(NS, 'body_placeholder_en', 'Hello {prenom}, …')
                                        }
                                        className="min-h-40"
                                    />
                                    <FieldError message={errorFor(`bodies.${lang}`)} t={t} />
                                    <div className="flex flex-col gap-1">
                                        <Label htmlFor={`automation-subject-${lang}`} className="text-xs">
                                            {t(NS, 'field_subject', 'Objet de l’e-mail (facultatif)')}
                                        </Label>
                                        <Input
                                            id={`automation-subject-${lang}`}
                                            // Le remplissage automatique du navigateur y a posé une
                                            // adresse e-mail (test du 28/09/2026) : objet envoyé
                                            // tel quel au voyageur.
                                            autoComplete="off"
                                            maxLength={SUBJECT_MAX}
                                            value={data.email_subjects[lang]}
                                            onChange={(event) => setField('email_subjects', { ...data.email_subjects, [lang]: event.target.value })}
                                            placeholder={
                                                lang === 'fr'
                                                    ? t(NS, 'subject_placeholder_fr', 'Votre séjour — {logement}')
                                                    : t(NS, 'subject_placeholder_en', 'Your stay — {logement}')
                                            }
                                        />
                                        <p className="text-muted-foreground text-xs">
                                            {t(
                                                NS,
                                                'field_subject_hint',
                                                'Utilisé seulement quand le message part par e-mail (réservation sans fil de messagerie).',
                                            )}
                                        </p>
                                        <FieldError message={errorFor(`email_subjects.${lang}`)} t={t} />
                                    </div>
                                </TabsContent>
                            ))}
                        </Tabs>
                        <FieldError message={errorFor('bodies')} t={t} />

                        <div className="flex flex-col gap-1">
                            <p className="text-muted-foreground text-xs">
                                {t(
                                    NS,
                                    'variables_hint',
                                    'Cliquez une variable pour l’insérer au curseur. Une variable vide pour un voyageur écarte son message.',
                                )}
                            </p>
                            <div className="flex flex-wrap gap-1">
                                {placeholders.map((placeholder) => (
                                    <button
                                        key={placeholder.key}
                                        type="button"
                                        title={placeholder.label}
                                        onMouseDown={(event) => event.preventDefault()}
                                        onClick={() => insertToken(placeholder.key)}
                                        className="bg-muted hover:bg-muted/70 rounded border px-1.5 py-0.5 font-mono text-[11px]"
                                    >
                                        {`{${placeholder.key}}`}
                                    </button>
                                ))}
                            </div>
                        </div>

                        <p className="text-muted-foreground flex items-start gap-1.5 text-xs">
                            <Info className="mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden="true" />
                            {t(
                                NS,
                                'platform_warning',
                                'Les plateformes masquent parfois les liens et coordonnées : gardez « À valider » pour les messages qui en contiennent.',
                            )}
                        </p>
                    </Section>

                    <RulePreview payload={previewPayload} enabled={open} t={t} />
                </div>

                {/* pr-24 : le lanceur flottant de l'assistant (coin bas-droit, z-60, au-dessus
                    de la feuille) recouvrait « Enregistrer ». */}
                <SheetFooter className="flex-row justify-end border-t pr-24">
                    <Button variant="outline" onClick={() => onOpenChange(false)}>
                        {t(NS, 'cancel', 'Annuler')}
                    </Button>
                    <Button onClick={submit} disabled={form.processing}>
                        {t(NS, 'save', 'Enregistrer')}
                    </Button>
                </SheetFooter>
            </SheetContent>
        </Sheet>
    );
}
