import { Button } from '@/components/ui/button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { Skeleton } from '@/components/ui/skeleton';
import { formatDateTimeFr, formatRelative } from '@/lib/relative-time';
import { cn } from '@/lib/utils';
import type { ActivityChange, ActivityEntry, ActivityEvent, ActivityRecord } from '@/types/activity';
import { Link, router } from '@inertiajs/react';
import {
    ChevronRight,
    Download,
    ExternalLink,
    FilePlus2,
    LogIn,
    LogOut,
    PencilLine,
    RotateCcw,
    ShieldAlert,
    ShieldCheck,
    Trash2,
    type LucideIcon,
} from 'lucide-react';
import * as React from 'react';
import {
    eventTone,
    fill,
    hasNoSubjectBlock,
    isSessionEvent,
    sortNewestFirst,
    sourceKind,
    splitChanges,
    subjectKey,
    subjectLabel,
    TONE_CHIP_CLASSES,
    type Translate,
} from './activity-format';
import { ActionChip, ActivitySentence, ActorAvatar, ActorTeamBadge, eventLabel, sentenceText } from './ActivityShared';

const NS = 'activity';

interface Props {
    /** Dernière ligne ouverte — gardée après fermeture pour l'animation de sortie. */
    entry: ActivityEntry | null;
    open: boolean;
    onOpenChange: (open: boolean) => void;
    /** Clic sur une étape de l'historique : on l'affiche à la place. */
    onSelectEntry: (entry: ActivityEntry) => void;
    now: Date;
    locale: string;
    t: Translate;
}

interface HistoryState {
    /** Élément dont c'est l'historique (`Classe:id`). */
    key: string;
    status: 'loading' | 'ready' | 'error';
    items: ActivityEntry[];
}

const SECTION_TITLE = 'text-sm font-semibold';

const EVENT_ICONS: Record<ActivityEvent, LucideIcon> = {
    created: FilePlus2,
    updated: PencilLine,
    deleted: Trash2,
    restored: RotateCcw,
    login: LogIn,
    logout: LogOut,
    login_failed: ShieldAlert,
    permissions_updated: ShieldCheck,
    exported: Download,
};

/** Valeur d'un champ : « vide » dit explicitement, jamais une case blanche. */
function Value({ value, variant, t }: { value: string | null; variant: 'before' | 'after' | 'plain'; t: Translate }) {
    if (value === null || value === '') {
        return <span className="text-muted-foreground italic">{t(NS, 'empty_value', 'vide')}</span>;
    }

    return (
        <span
            className={cn(
                'break-words whitespace-pre-wrap',
                variant === 'before' && 'text-muted-foreground line-through',
                variant === 'after' && 'text-foreground font-semibold',
            )}
        >
            {value}
        </span>
    );
}

/**
 * Champ | Avant | Après. Une création n'a pas d'« avant » et une suppression
 * pas d'« après » : la colonne vide est retirée plutôt que remplie de tirets.
 */
function ChangesTable({ changes, event, t }: { changes: ActivityChange[]; event: ActivityEvent; t: Translate }) {
    const showOld = event !== 'created';
    const showNew = event !== 'deleted';
    const both = showOld && showNew;

    return (
        <div className="overflow-hidden rounded-lg border">
            <table className="w-full table-fixed text-sm">
                <thead className="bg-muted/50 text-muted-foreground text-xs">
                    <tr>
                        <th scope="col" className={cn('px-3 py-2 text-left font-medium', both ? 'w-[30%]' : 'w-[40%]')}>
                            {t(NS, 'changes_field', 'Champ')}
                        </th>
                        {showOld && (
                            <th scope="col" className="px-3 py-2 text-left font-medium">
                                {both ? t(NS, 'changes_before', 'Avant') : t(NS, 'changes_last_value', 'Dernière valeur')}
                            </th>
                        )}
                        {showNew && (
                            <th scope="col" className="px-3 py-2 text-left font-medium">
                                {both ? t(NS, 'changes_after', 'Après') : t(NS, 'changes_value', 'Valeur')}
                            </th>
                        )}
                    </tr>
                </thead>
                <tbody className="divide-y">
                    {changes.map((change) => (
                        <tr key={change.field} className="align-top">
                            <th scope="row" className="text-muted-foreground px-3 py-2 text-left font-normal break-words">
                                {change.label}
                            </th>
                            {showOld && (
                                <td className="px-3 py-2">
                                    <Value value={change.old} variant={both ? 'before' : 'plain'} t={t} />
                                </td>
                            )}
                            {showNew && (
                                <td className="px-3 py-2">
                                    <Value value={change.new} variant={both ? 'after' : 'plain'} t={t} />
                                </td>
                            )}
                        </tr>
                    ))}
                </tbody>
            </table>
        </div>
    );
}

/** Ce qu'une ligne d'audit a changé ; les champs techniques sont repliés. */
function RecordChanges({ record, showHeader, t }: { record: ActivityRecord; showHeader: boolean; t: Translate }) {
    const { visible, technical } = splitChanges(record.changes);

    return (
        <div className="flex flex-col gap-2">
            {showHeader && (
                <div className="flex flex-wrap items-center gap-2 text-sm">
                    <ActionChip event={record.event} t={t} />
                    <span className="text-muted-foreground">{record.subject.type_label}</span>
                    <span className="font-medium break-words">{subjectLabel(record.subject)}</span>
                </div>
            )}

            {visible.length > 0 ? (
                <ChangesTable changes={visible} event={record.event} t={t} />
            ) : (
                <p className="text-muted-foreground text-sm">
                    {technical.length > 0
                        ? t(NS, 'changes_only_technical', 'Seuls des champs techniques ont changé.')
                        : t(NS, 'changes_none', 'Aucun champ modifié par cette action.')}
                </p>
            )}

            {technical.length > 0 && (
                <Collapsible>
                    <CollapsibleTrigger className="group text-muted-foreground hover:text-foreground focus-visible:ring-ring flex items-center gap-1 rounded text-xs font-medium focus-visible:ring-2 focus-visible:outline-none">
                        <ChevronRight className="h-3.5 w-3.5 transition-transform group-data-[state=open]:rotate-90" aria-hidden="true" />
                        {fill(t(NS, 'changes_technical', 'Champs techniques (:count)'), { count: technical.length })}
                    </CollapsibleTrigger>
                    <CollapsibleContent className="mt-2">
                        <ChangesTable changes={technical} event={record.event} t={t} />
                    </CollapsibleContent>
                </Collapsible>
            )}
        </div>
    );
}

function Fact({ label, value }: { label: string; value: string }) {
    return (
        <div className="min-w-0">
            <dt className="text-muted-foreground text-xs">{label}</dt>
            <dd className="font-medium break-words">{value}</dd>
        </div>
    );
}

/**
 * L'étude : tout ce que l'action a changé, qui l'a faite et depuis où, puis la
 * vie complète de l'élément. Ouvert au clic sur une ligne du flux.
 */
export function ActivityDetailSheet({ entry, open, onOpenChange, onSelectEntry, now, locale, t }: Props) {
    const [history, setHistory] = React.useState<HistoryState | null>(null);
    /** Élément dont on attend l'historique : toute réponse pour un autre est ignorée. */
    const wanted = React.useRef<string | null>(null);
    const wasOpen = React.useRef(false);

    const loadHistory = React.useCallback((target: ActivityEntry) => {
        const key = subjectKey(target.subject);
        let settled = false;

        wanted.current = key;
        setHistory({ key, status: 'loading', items: [] });

        // Rechargement PARTIEL : seul `history` redescend, la liste, les
        // filtres et le défilement restent tels quels (router.reload préserve
        // l'état et le scroll par construction).
        router.reload({
            only: ['history'],
            data: { history: target.id },
            onSuccess: (page) => {
                settled = true;
                if (wanted.current !== key) return;

                const items = page.props.history as ActivityEntry[] | null | undefined;
                setHistory({ key, status: 'ready', items: sortNewestFirst(items ?? []) });
            },
            onFinish: () => {
                if (settled || wanted.current !== key) return;
                setHistory((current) => (current && current.key === key && current.status === 'loading' ? { ...current, status: 'error' } : current));
            },
        });
    }, []);

    // À chaque OUVERTURE on recharge (l'élément a pu bouger depuis). Pendant
    // que le panneau est ouvert, cliquer une étape du même élément ne
    // recharge rien : c'est le même historique.
    React.useEffect(() => {
        const justOpened = open && !wasOpen.current;
        wasOpen.current = open;

        if (!open || !entry) return;
        if (justOpened || wanted.current !== subjectKey(entry.subject)) {
            loadHistory(entry);
        }
    }, [open, entry, loadHistory]);

    if (!entry) {
        return null;
    }

    const currentKey = subjectKey(entry.subject);
    const shownHistory = history && history.key === currentKey ? history : null;
    // Connexions et export : rien n'est « modifié » — la section vide ne dirait rien d'utile.
    const hideChanges = (isSessionEvent(entry.event) || entry.event === 'exported') && entry.records.every((record) => record.changes.length === 0);
    const showSubject = !hasNoSubjectBlock(entry.event);
    const parent = entry.subject.parent;
    const unknown = t(NS, 'detail_unknown', 'Inconnu');

    const kind = sourceKind(entry.source);
    const sourceLabels: Record<typeof kind, string> = {
        web: t(NS, 'source_web', 'Manager'),
        webhook: t(NS, 'source_webhook', 'Application / API'),
        sync: t(NS, 'source_sync', 'Synchronisation des canaux'),
        console: t(NS, 'source_console', 'Tâche automatique'),
        admin: t(NS, 'source_admin', "Outil d'administration"),
        other: t(NS, 'source_other', 'Autre origine'),
    };

    return (
        <Sheet open={open} onOpenChange={onOpenChange}>
            <SheetContent side="right" className="flex w-full flex-col gap-0 p-0 sm:max-w-xl">
                <SheetHeader className="border-b px-6 pt-6 pr-12 pb-4">
                    <div className="flex flex-wrap items-center gap-2">
                        <ActionChip event={entry.event} t={t} />
                        <time dateTime={entry.created_at} className="text-muted-foreground text-xs tabular-nums">
                            {formatDateTimeFr(entry.created_at)}
                        </time>
                        <span className="text-muted-foreground text-xs">· {formatRelative(entry.created_at, locale, now)}</span>
                    </div>
                    <SheetTitle className="text-base leading-snug font-normal">
                        <ActivitySentence entry={entry} t={t} />
                    </SheetTitle>
                    <SheetDescription className="sr-only">{sentenceText(entry, t)}</SheetDescription>
                </SheetHeader>

                <div className="flex min-h-0 flex-1 flex-col gap-6 overflow-x-hidden overflow-y-auto px-6 py-5">
                    {showSubject && (
                        <section className="rounded-xl border p-4">
                            <div className="text-muted-foreground text-xs font-medium tracking-wide uppercase">{entry.subject.type_label}</div>
                            <div className="mt-1 flex flex-wrap items-center gap-2">
                                <span className="font-semibold break-words">{subjectLabel(entry.subject)}</span>
                                {entry.subject.deleted && (
                                    <span className="rounded-md border border-transparent bg-red-100 px-2 py-0.5 text-xs font-medium text-red-700">
                                        {t(NS, 'subject_deleted_badge', 'Supprimé')}
                                    </span>
                                )}
                            </div>
                            {entry.subject.url ? (
                                <Button asChild variant="outline" size="sm" className="mt-3">
                                    <Link href={entry.subject.url}>
                                        <ExternalLink className="mr-2 h-4 w-4" />
                                        {t(NS, 'detail_open_subject', "Ouvrir l'élément")}
                                    </Link>
                                </Button>
                            ) : (
                                entry.subject.deleted && (
                                    <p className="text-muted-foreground mt-2 text-xs">
                                        {t(NS, 'detail_subject_gone', "Cet élément n'existe plus ; son historique reste consultable ci-dessous.")}
                                    </p>
                                )
                            )}
                        </section>
                    )}

                    {/* La réservation dont dépend l'élément (planification, prolongation,
                        paiement…) : c'est souvent elle qu'on vient chercher. */}
                    {parent && (
                        <section className="flex flex-wrap items-center justify-between gap-3 rounded-xl border p-4">
                            <div className="min-w-0">
                                <div className="text-muted-foreground text-xs font-medium tracking-wide uppercase">{t(NS, 'detail_parent', 'Élément lié')}</div>
                                <div className="mt-1 break-words">
                                    <span className="text-muted-foreground">{parent.type_label}</span> <span className="font-semibold">{parent.label}</span>
                                </div>
                            </div>
                            {parent.url && (
                                <Button asChild variant="outline" size="sm">
                                    <Link href={parent.url}>
                                        <ExternalLink className="mr-2 h-4 w-4" />
                                        {t(NS, 'detail_parent_open', 'Ouvrir')}
                                    </Link>
                                </Button>
                            )}
                        </section>
                    )}

                    <section className="flex flex-col gap-3">
                        <h3 className={SECTION_TITLE}>{t(NS, 'detail_actor', 'Auteur')}</h3>
                        <div className="flex items-center gap-3">
                            <ActorAvatar actor={entry.actor} />
                            <div className="min-w-0">
                                <div className="font-medium break-words">
                                    {entry.actor.name}
                                    <ActorTeamBadge team={entry.actor.team} t={t} className="ml-1.5" />
                                </div>
                                <div className="text-muted-foreground text-xs">
                                    {entry.actor.is_system ? t(NS, 'detail_system_actor', 'Action automatique') : (entry.actor.role ?? t(NS, 'detail_no_role', 'Aucun rôle'))}
                                </div>
                            </div>
                        </div>
                        <dl className="grid grid-cols-1 gap-x-6 gap-y-2 text-sm sm:grid-cols-3">
                            <Fact label={t(NS, 'detail_source', 'Origine')} value={sourceLabels[kind]} />
                            <Fact label={t(NS, 'detail_device', 'Appareil')} value={entry.context.device ?? unknown} />
                            <Fact label={t(NS, 'detail_ip', 'Adresse IP')} value={entry.context.ip ?? unknown} />
                        </dl>
                    </section>

                    {!hideChanges && (
                        <section className="flex flex-col gap-3">
                            <h3 className={SECTION_TITLE}>{t(NS, 'changes_title', 'Ce qui a changé')}</h3>
                            {entry.records.map((record) => (
                                <RecordChanges key={record.id} record={record} showHeader={entry.records.length > 1} t={t} />
                            ))}
                        </section>
                    )}

                    <section className="flex flex-col gap-3">
                        <h3 className={SECTION_TITLE}>{t(NS, 'history_title', 'Historique de cet élément')}</h3>

                        {(shownHistory === null || shownHistory.status === 'loading') && (
                            <div className="flex flex-col gap-3" aria-busy="true" aria-label={t(NS, 'history_loading', "Chargement de l'historique…")}>
                                {[0, 1, 2].map((index) => (
                                    <div key={index} className="flex gap-3">
                                        <Skeleton className="h-8 w-8 shrink-0 rounded-full" />
                                        <Skeleton className="h-14 flex-1 rounded-lg" />
                                    </div>
                                ))}
                            </div>
                        )}

                        {shownHistory?.status === 'error' && (
                            <div className="flex flex-wrap items-center gap-3 text-sm">
                                <span className="text-muted-foreground">{t(NS, 'history_error', "L'historique n'a pas pu être chargé.")}</span>
                                <Button variant="outline" size="sm" onClick={() => loadHistory(entry)}>
                                    {t(NS, 'history_retry', 'Réessayer')}
                                </Button>
                            </div>
                        )}

                        {shownHistory?.status === 'ready' && shownHistory.items.length === 0 && (
                            <p className="text-muted-foreground text-sm">{t(NS, 'history_empty', 'Aucune autre action enregistrée sur cet élément.')}</p>
                        )}

                        {shownHistory?.status === 'ready' && shownHistory.items.length > 0 && (
                            <ol>
                                {shownHistory.items.map((item, index) => {
                                    const Icon = EVENT_ICONS[item.event] ?? PencilLine;
                                    const isCurrent = item.id === entry.id;

                                    return (
                                        <li key={item.id} className="relative flex gap-3 pb-4 last:pb-0">
                                            {index < shownHistory.items.length - 1 && (
                                                <span className="bg-border absolute top-9 bottom-0 left-[15px] w-px" aria-hidden="true" />
                                            )}
                                            <span
                                                className={cn(
                                                    'z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full border',
                                                    TONE_CHIP_CLASSES[eventTone(item.event)],
                                                )}
                                                aria-hidden="true"
                                            >
                                                <Icon className="h-4 w-4" />
                                            </span>
                                            <button
                                                type="button"
                                                onClick={() => onSelectEntry(item)}
                                                aria-current={isCurrent ? 'true' : undefined}
                                                className={cn(
                                                    'bg-card hover:bg-muted/40 focus-visible:ring-ring min-w-0 flex-1 rounded-lg border p-3 text-left shadow-sm transition-colors focus-visible:ring-2 focus-visible:outline-none',
                                                    isCurrent && 'border-primary ring-primary/30 ring-2',
                                                )}
                                            >
                                                <div className="flex flex-wrap items-start justify-between gap-x-2 gap-y-1">
                                                    <span className="text-sm">
                                                        <span className="font-medium">{eventLabel(item.event, t)}</span>{' '}
                                                        <span className="text-muted-foreground">{fill(t(NS, 'history_by', 'par :name'), { name: item.actor.name })}</span>
                                                        <ActorTeamBadge team={item.actor.team} t={t} className="ml-1" />
                                                    </span>
                                                    <time dateTime={item.created_at} className="text-muted-foreground shrink-0 text-xs tabular-nums">
                                                        {formatDateTimeFr(item.created_at)}
                                                    </time>
                                                </div>
                                                {item.summary_fields.length > 0 && (
                                                    <div className="text-muted-foreground mt-1 text-xs break-words">{item.summary_fields.join(' · ')}</div>
                                                )}
                                                {isCurrent && (
                                                    <div className="text-primary mt-1 text-xs font-medium">{t(NS, 'history_current', 'Action affichée')}</div>
                                                )}
                                            </button>
                                        </li>
                                    );
                                })}
                            </ol>
                        )}
                    </section>
                </div>
            </SheetContent>
        </Sheet>
    );
}
