"use client";

import { useCallback, useMemo, useState, type ReactNode } from "react";
import { DataTable } from "@/components/data-table/data-table";
import {
  keywordColumns,
  KEYWORD_NUMERIC_COLUMNS,
  type KeywordTableRow,
} from "@/components/keywords/keyword-columns";
import { KeywordCards } from "@/components/keywords/keyword-cards";
import { AttachDialog, ListDialog, TagDialog } from "@/components/keywords/keyword-dialogs";
import {
  KeywordBulkActions,
  type DialogKind,
  type OpenDialog,
} from "@/components/keywords/keyword-row-actions";
import { KeywordSheet } from "@/components/keywords/keyword-sheet";
import { TooltipProvider } from "@/components/ui/tooltip";
import type { KeywordList, Tag } from "@/lib/keywords";

/** Ce que l'écran fournit à la table : la cible, et les facettes que les dialogues proposent. */
export type KeywordsTableContext = {
  siteId: string;
  targetId: string;
  /** « France · français » — l'en-tête du panneau de détail le rappelle (décision A6). */
  targetLabel: string;
  lists: KeywordList[];
  tags: Tag[];
};

type OpenState = {
  kind: DialogKind;
  keywordIds: string[];
  keyword?: string;
  onDone?: () => void;
};

const EMPTY_MESSAGE = "Aucun mot-clé ne correspond à ces filtres.";

/**
 * Le réservoir sur la table générique du projet — jamais une table locale (spec §7.5).
 *
 * En **mode serveur** : la page affichée est celle rendue par `GET /keywords`, la recherche
 * et les filtres passent par `toolbar` (ils pilotent l'URL) et la pagination par `footer`
 * (curseur api). Ce que la table garde en propre : le masquage de colonnes et la sélection,
 * qui sont locaux par nature.
 *
 * **Deux rendus, une seule source de lignes.** Au-dessus de `sm`, la table ; en dessous, une
 * carte par mot-clé — huit colonnes ne tiennent pas sur 375 px, et la règle du projet interdit
 * à la fenêtre de scroller horizontalement. Les deux ouvrent le même panneau de détail.
 *
 * **Les dialogues sont montés ici, pas dans les cellules.** Deux raisons, et la seconde est
 * la vraie : une cellule TanStack est remontée dès que l'identité de ses colonnes change, or
 * une action réussie revalide la page — le dialogue disparaissait donc au moment exact où il
 * affichait son décompte. Et une ligne comme une sélection ouvrent le *même* dialogue : le
 * monter une fois plutôt qu'une fois par ligne est aussi ce qui rend ce partage littéral.
 */
export function KeywordsTable({
  rows,
  context,
  toolbar,
  footer,
}: {
  rows: KeywordTableRow[];
  context: KeywordsTableContext;
  toolbar: ReactNode;
  footer: ReactNode;
}) {
  const [open, setOpen] = useState<OpenState | null>(null);
  const [detail, setDetail] = useState<KeywordTableRow | null>(null);
  const [detailOpen, setDetailOpen] = useState(false);
  // `useState` rend `setOpen` stable, donc `openDialog` l'est aussi : c'est ce qui permet aux
  // colonnes de ne dépendre que de valeurs stables.
  const openDialog = useCallback<OpenDialog>((dialog) => setOpen(dialog), []);
  const openDetail = useCallback((row: KeywordTableRow) => {
    setDetail(row);
    setDetailOpen(true);
  }, []);

  const { siteId, targetId, targetLabel, lists, tags } = context;
  // Dépendances **primitives** : `lists` et `tags` changent d'identité à chaque rendu serveur
  // et n'ont rien à faire ici — ce sont les dialogues, en dehors de la table, qui les lisent.
  const columns = useMemo(() => keywordColumns({ openDetail }), [openDetail]);
  const bulkActions = useMemo(() => bulkActionsFor(openDialog), [openDialog]);

  const close = (isOpen: boolean) => {
    if (isOpen) return;
    open?.onDone?.();
    setOpen(null);
  };
  const ids = open?.keywordIds ?? [];

  return (
    // Le fournisseur d'infobulles couvre les deux rendus : chaque cellule métrique en ouvre
    // une pour montrer les autres sources (décision A5).
    <TooltipProvider>
      <div className="flex min-w-0 flex-col gap-4">
        {toolbar}

        <div className="hidden min-w-0 sm:block">
          <DataTable
            server
            columns={columns}
            data={rows}
            footer={footer}
            emptyMessage={EMPTY_MESSAGE}
            getRowId={(row) => row.id}
            // La cible : deux cibles peuvent montrer les mêmes mots-clés (mutualisation à
            // l'organisation), une sélection ne doit jamais traverser l'une vers l'autre.
            selectionKey={targetId}
            bulkActions={bulkActions}
            alignRight={KEYWORD_NUMERIC_COLUMNS}
            onRowClick={openDetail}
          />
        </div>

        <div className="flex min-w-0 flex-col gap-4 sm:hidden">
          <KeywordCards rows={rows} emptyMessage={EMPTY_MESSAGE} onOpen={openDetail} />
          {footer}
        </div>
      </div>

      <KeywordSheet
        row={detail}
        open={detailOpen}
        onOpenChange={setDetailOpen}
        siteId={siteId}
        targetId={targetId}
        targetLabel={targetLabel}
        openDialog={openDialog}
      />

      <AttachDialog
        open={open?.kind === "attach"}
        onOpenChange={close}
        siteId={siteId}
        targetId={targetId}
        keywordIds={ids}
        keyword={open?.keyword}
      />
      <ListDialog
        open={open?.kind === "list-add" || open?.kind === "list-remove"}
        onOpenChange={close}
        siteId={siteId}
        targetId={targetId}
        keywordIds={ids}
        lists={lists}
        mode={open?.kind === "list-remove" ? "remove" : "add"}
      />
      <TagDialog
        open={open?.kind === "tag-add" || open?.kind === "tag-remove"}
        onOpenChange={close}
        siteId={siteId}
        keywordIds={ids}
        tags={tags}
        mode={open?.kind === "tag-remove" ? "remove" : "add"}
      />
    </TooltipProvider>
  );
}

/**
 * Le rendu des actions de masse, construit **hors du composant** : c'est une fonction de
 * rendu passée en prop, pas un composant redéfini à chaque rendu.
 */
function bulkActionsFor(openDialog: OpenDialog) {
  return (selectedIds: string[], clearSelection: () => void) => (
    <KeywordBulkActions
      selectedIds={selectedIds}
      clearSelection={clearSelection}
      openDialog={openDialog}
    />
  );
}
