/* Dialogs and notifications: an in-app confirmation dialog, flash toasts and the membership timeline. */
import { usePage } from '@inertiajs/react';
import { useEffect, useRef, useState, type ReactNode } from 'react';
import { AlertTriangle, CheckCircle2, X } from 'lucide-react';
import type { SharedProps } from '../types';
import { Button, cx } from './ui';

/** Always holds the newest version of a callback, so effects can call it without re-running when it changes. */
function useLatest<T>(value: T) {
    const ref = useRef(value);
    ref.current = value;
    return ref;
}

/** A dialog asking "are you sure?". Esc or the backdrop cancels. */
export function ConfirmDialog({ open, title, children, confirmLabel = 'Confirm', tone = 'primary', busy, onConfirm, onCancel }: {
    open: boolean;
    title: string;
    children?: ReactNode;
    confirmLabel?: string;
    tone?: 'primary' | 'danger';
    busy?: boolean;
    onConfirm: () => void;
    onCancel: () => void;
}) {
    const confirmRef = useRef<HTMLButtonElement>(null);
    const cancel = useLatest(onCancel);
    // runs only when the dialog opens: re-running on every render would pull focus out of a field inside it while typing
    useEffect(() => {
        if (!open) return;
        const back = document.activeElement as HTMLElement | null;
        const first = confirmRef.current?.closest('[role=alertdialog]')?.querySelector<HTMLElement>('input,textarea,select');
        (first ?? confirmRef.current)?.focus();
        const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') cancel.current(); };
        document.addEventListener('keydown', onKey);
        return () => { document.removeEventListener('keydown', onKey); back?.focus(); };
    }, [open, cancel]);
    if (!open) return null;
    return (
        <div className="fixed inset-0 z-50 grid place-items-center p-4">
            <div className="animate-fade-in absolute inset-0 bg-ink/50 backdrop-blur-[2px]" onClick={onCancel} />
            <div role="alertdialog" aria-modal="true" aria-labelledby="confirm-title" className="animate-pop-in relative w-full max-w-md rounded-2xl bg-panel p-6 shadow-pop">
                <div className="flex gap-4">
                    <span className={cx('grid size-11 shrink-0 place-items-center rounded-full', tone === 'danger' ? 'bg-rose-50 text-rose-600' : 'bg-brand/10 text-brand')}>
                        <AlertTriangle className="size-5" aria-hidden />
                    </span>
                    <div className="min-w-0">
                        <h2 id="confirm-title" className="text-base font-bold text-ink">{title}</h2>
                        {children && <div className="mt-1.5 text-sm leading-relaxed text-muted">{children}</div>}
                    </div>
                </div>
                <div className="mt-6 flex justify-end gap-2">
                    <Button variant="secondary" onClick={onCancel} disabled={busy}>Cancel</Button>
                    <Button ref={confirmRef} variant={tone === 'danger' ? 'danger' : 'primary'} onClick={onConfirm} disabled={busy}>
                        {busy ? 'Working…' : confirmLabel}
                    </Button>
                </div>
            </div>
        </div>
    );
}

/** A dialog holding a form or details. Esc or the backdrop closes it. */
export function Modal({ open, title, subtitle, children, onClose, width = 'max-w-lg' }: {
    open: boolean;
    title: string;
    subtitle?: ReactNode;
    children: ReactNode;
    onClose: () => void;
    width?: string;
}) {
    const boxRef = useRef<HTMLDivElement>(null);
    const close = useLatest(onClose);
    useEffect(() => {
        if (!open) return;
        const back = document.activeElement as HTMLElement | null;
        boxRef.current?.querySelector<HTMLElement>('input,select,textarea,button')?.focus();
        const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') close.current(); };
        document.addEventListener('keydown', onKey);
        return () => { document.removeEventListener('keydown', onKey); back?.focus(); };
    }, [open, close]);
    if (!open) return null;
    return (
        <div className="fixed inset-0 z-50 grid place-items-center overflow-y-auto p-4">
            <div className="animate-fade-in fixed inset-0 bg-ink/50 backdrop-blur-[2px]" onClick={onClose} />
            <div ref={boxRef} role="dialog" aria-modal="true" aria-labelledby="modal-title" className={cx('animate-pop-in relative w-full rounded-2xl bg-panel p-6 shadow-pop', width)}>
                <div className="flex items-start justify-between gap-4">
                    <div className="min-w-0">
                        <h2 id="modal-title" className="text-lg font-extrabold tracking-tight text-ink">{title}</h2>
                        {subtitle && <div className="mt-1 text-sm text-muted">{subtitle}</div>}
                    </div>
                    <button type="button" onClick={onClose} className="grid size-8 shrink-0 place-items-center rounded-lg text-muted hover:bg-line-2 hover:text-ink" aria-label="Close">
                        <X className="size-4" />
                    </button>
                </div>
                <div className="mt-5">{children}</div>
            </div>
        </div>
    );
}

/** Shows the server's flash message ("Payment approved…") as a toast for a few seconds. */
export function FlashToast() {
    const { flash, errors } = usePage<SharedProps>().props;
    const [msg, setMsg] = useState<string | null>(null);
    // "page expired" comes back as an error, so the form keeps what was typed; it is shown here like a message
    const text = flash?.status ?? errors?.expired ?? null;
    useEffect(() => {
        if (!text) return;
        setMsg(text);
        const t = setTimeout(() => setMsg(null), 8000);
        return () => clearTimeout(t);
    }, [flash, text]);
    if (!msg) return null;
    return (
        <div className="pointer-events-none fixed inset-x-0 top-20 z-50 flex justify-center px-4 lg:justify-end lg:pr-6">
            <div role="status" className="animate-toast-in pointer-events-auto flex max-w-md items-start gap-3 rounded-2xl border border-emerald-200 bg-white px-4 py-3 text-sm text-ink shadow-pop">
                <CheckCircle2 className="mt-0.5 size-5 shrink-0 text-emerald-600" aria-hidden />
                <p className="flex-1 leading-relaxed">{msg}</p>
                <button onClick={() => setMsg(null)} className="text-muted hover:text-ink" aria-label="Dismiss"><X className="size-4" /></button>
            </div>
        </div>
    );
}
