import { useTranslation } from '@/hooks/use-translation';
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem, type SharedData } from '@/types';
import type { ActivityEntry, ActivityPageProps, ActivityTeamRow } from '@/types/activity';
import { Button } from '@/components/ui/button';
import { Head, router, usePage } from '@inertiajs/react';
import { Download } from 'lucide-react';
import * as React from 'react';
import {
    activeFilterCount,
    CLEARED_FILTERS,
    exportParams,
    FEED_ONLY_CLEARED,
    queryFromFilters,
    queryToParams,
    type ActivityQuery,
} from './components/activity-format';
import { ActivityDetailSheet } from './components/ActivityDetailSheet';
import { ActivityFeed } from './components/ActivityFeed';
import { ActivityFilters } from './components/ActivityFilters';
import { ActivityKpis } from './components/ActivityKpis';
import { ActivityTabs } from './components/ActivityTabs';
import { ActivityTeamTable } from './components/ActivityTeamTable';

const NS = 'activity';

/** Horloge partagée des temps relatifs (« il y a 4 min »), rafraîchie sans recharger la page. */
function useNow(intervalMs: number): Date {
    const [now, setNow] = React.useState(() => new Date());

    React.useEffect(() => {
        const timer = window.setInterval(() => setNow(new Date()), intervalMs);
        return () => window.clearInterval(timer);
    }, [intervalMs]);

    return now;
}

/**
 * Journal d'activité (TL-293) — qui a créé, modifié, supprimé quoi, et quand ;
 * et qui ne fait plus rien.
 *
 * Hiérarchie : coup d'œil (compteurs) → balayage (flux par jour) → étude
 * (panneau de détail avec avant / après et l'historique de l'élément).
 *
 * Contrat des props : `resources/js/types/activity.ts`, partagé avec le serveur.
 */
export default function ActivityIndex() {
    const { t, locale } = useTranslation();
    const { entries, filters, kpis, module_counts, options, team, tracking_since, is_headquarters, subject_info, can_export } =
        usePage<ActivityPageProps & SharedData>().props;
    const now = useNow(30_000);

    const breadcrumbs: BreadcrumbItem[] = [{ title: t(NS, 'title', "Journal d'activité"), href: '/activity' }];

    /*
     * FILTRES : l'URL fait foi. `query` est la copie locale, mise à jour tout
     * de suite (les listes affichent le choix sans attendre le serveur), puis
     * réalignée sur `filters` à chaque réponse. Inertia annule une visite en
     * cours quand une autre part : la dernière réponse est donc toujours celle
     * du dernier choix. `queryRef` évite qu'un second choix rapide parte d'un
     * état périmé.
     */
    const [query, setQuery] = React.useState<ActivityQuery>(() => queryFromFilters(filters));
    const queryRef = React.useRef(query);
    const [searchInput, setSearchInput] = React.useState(filters.search ?? '');

    React.useEffect(() => {
        const next = queryFromFilters(filters);
        queryRef.current = next;
        setQuery(next);
    }, [filters]);

    const apply = React.useCallback((patch: Partial<ActivityQuery>) => {
        const next = { ...queryRef.current, ...patch };
        queryRef.current = next;
        setQuery(next);

        // Le champ de recherche suit une recherche IMPOSÉE (réinitialisation,
        // changement d'onglet), jamais l'écho de ce qu'on est en train de taper.
        if ('search' in patch) {
            const imposed = patch.search ?? null;
            setSearchInput((typed) => ((typed.trim() || null) === imposed ? typed : (imposed ?? '')));
        }

        router.get(route('activity.index'), queryToParams(next), { preserveState: true, preserveScroll: true, replace: true });
    }, []);

    /**
     * Tout autre choix emporte le texte DÉJÀ tapé : sans quoi une recherche
     * encore en attente de ses 350 ms partirait après coup, sur des filtres
     * périmés, et annulerait le choix qu'on vient de faire.
     */
    const searchRef = React.useRef(searchInput);
    React.useEffect(() => {
        searchRef.current = searchInput;
    }, [searchInput]);
    const applyNow = React.useCallback((patch: Partial<ActivityQuery>) => apply({ search: searchRef.current.trim() || null, ...patch }), [apply]);

    const reset = React.useCallback(() => apply({ ...CLEARED_FILTERS }), [apply]);

    const changeTab = (tab: ActivityQuery['tab']) => {
        if (tab === queryRef.current.tab) return;
        // L'onglet Équipe ne dépend que de la période et de la franchise : les
        // filtres propres au flux sont retirés pour que ses chiffres ne
        // dépendent jamais d'un critère invisible.
        apply(tab === 'team' ? { tab, ...FEED_ONLY_CLEARED } : { tab });
    };

    const openMember = (row: ActivityTeamRow) => apply({ tab: 'feed', user: String(row.id) });

    /*
     * Export : les filtres EN VIGUEUR, onglet Flux seulement (on n'exporte pas
     * le tableau d'équipe). `route().has` : tant que la route n'est pas
     * déclarée côté serveur, le bouton reste absent au lieu de faire planter
     * la page — Ziggy lève une exception sur une route inconnue.
     */
    const exportHref = can_export && query.tab === 'feed' && route().has('activity.export') ? route('activity.export', exportParams(query)) : null;

    // Panneau de détail : la ligne reste en mémoire après fermeture, pour
    // que l'animation de sortie montre encore son contenu.
    const [selected, setSelected] = React.useState<ActivityEntry | null>(null);
    const [sheetOpen, setSheetOpen] = React.useState(false);

    const openEntry = React.useCallback((entry: ActivityEntry) => {
        setSelected(entry);
        setSheetOpen(true);
    }, []);

    return (
        <AppLayout breadcrumbs={breadcrumbs}>
            <Head title={t(NS, 'title', "Journal d'activité")} />

            <div className="flex h-full flex-1 flex-col gap-4 p-3 sm:p-4 lg:p-6">
                <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                    <div>
                        <h1 className="text-2xl font-semibold tracking-tight">{t(NS, 'title', "Journal d'activité")}</h1>
                        <p className="text-muted-foreground text-sm">{t(NS, 'subtitle', 'Qui a fait quoi, et quand, sur tout le Manager')}</p>
                    </div>
                    <div className="flex flex-wrap items-center gap-2">
                        {exportHref && (
                            // Un vrai lien de téléchargement, pas une visite Inertia : le
                            // serveur répond par un fichier, que le navigateur enregistre.
                            <Button asChild variant="outline" className="h-9" data-tour="activity-export">
                                <a href={exportHref} download title={t(NS, 'export_title', 'Télécharger le flux affiché, avec ses filtres (CSV)')}>
                                    <Download className="h-4 w-4" aria-hidden="true" />
                                    {t(NS, 'export', 'Exporter')}
                                </a>
                            </Button>
                        )}
                        <ActivityTabs tab={query.tab} onChange={changeTab} t={t} />
                    </div>
                </div>

                <ActivityKpis kpis={kpis} locale={locale} t={t} />

                <ActivityFilters
                    query={query}
                    searchInput={searchInput}
                    onSearchInput={setSearchInput}
                    onApply={applyNow}
                    onReset={reset}
                    options={options}
                    kpis={kpis}
                    isHeadquarters={is_headquarters}
                    subjectInfo={subject_info}
                    now={now}
                    locale={locale}
                    t={t}
                />

                {/* Le CONTENU suit l'onglet de la réponse serveur (la pilule, elle,
                    bascule tout de suite) : sans quoi l'onglet Équipe s'afficherait
                    « vide » le temps que `team` arrive — un faux état vide. */}
                {filters.tab === 'team' ? (
                    <ActivityTeamTable
                        team={team ?? []}
                        showFranchise={options.franchises !== null}
                        loginsTracked={tracking_since.logins !== null}
                        now={now}
                        locale={locale}
                        t={t}
                        onOpenMember={openMember}
                    />
                ) : (
                    <ActivityFeed
                        entries={entries}
                        trackingSince={tracking_since}
                        modules={options.modules}
                        moduleCounts={module_counts}
                        activeModule={query.module}
                        onModule={(module) => applyNow({ module })}
                        activeFilterCount={activeFilterCount(query, now)}
                        now={now}
                        locale={locale}
                        t={t}
                        onOpen={openEntry}
                        onReset={reset}
                    />
                )}
            </div>

            <ActivityDetailSheet entry={selected} open={sheetOpen} onOpenChange={setSheetOpen} onSelectEntry={setSelected} now={now} locale={locale} t={t} />
        </AppLayout>
    );
}
