// src/lib/keywords/theme-store.ts — règle UNIQUE du thème par site.
//
// Point commun des deux chemins qui posent un thème : `createThemeAction`
// (saisie manuelle depuis le réservoir) et `importMangoolsListsAction` (une
// liste KWFinder = un thème, étape 3 du parcours d'entrée). Les deux DOIVENT
// dériver le slug du même appel et appliquer la même borne : `keywords.theme`
// stocke un slug SANS clé étrangère vers `keyword_themes.slug`, donc une
// divergence entre les deux chemins ne lèverait rien — le thème existerait sur
// les mots-clés sans apparaître dans la liste des thèmes du site, et les
// filtres du réservoir ne le trouveraient jamais.
//
// Ce module n'est PAS un point d'entrée : il ne vérifie AUCUNE tenancy, ses
// appelants (server actions) gardent leur `assertSiteInActiveOrg` en amont.
// Il charge `import-core` (wasm ciblé nodejs, incompatible navigateur) : il ne
// doit jamais être importé depuis un composant client — seule la borne, posée
// dans `./types`, franchit cette frontière.
import * as wasm from "import-core";
import type { db } from "@/lib/db/client";
import { keywordThemes } from "@/lib/db/domain-schema";
import { MAX_THEME_LABEL_LENGTH } from "./types";

/** `db` ou une transaction en cours : l'import de listes crée ses thèmes DANS
 *  la transaction d'écriture, pour qu'un refus de quota ne laisse pas derrière
 *  lui un thème sans le moindre mot-clé. */
type Executor = Parameters<Parameters<typeof db.transaction>[0]>[0] | typeof db;

export type KeywordThemeRow = typeof keywordThemes.$inferInsert;

/**
 * Slug d'un thème à partir d'un libellé saisi (ou d'un slug déjà formé — la
 * slugification est idempotente sur le chemin nominal).
 *
 * `wasm.slugify` est LE slugifieur de `keyword_themes` : aucune normalisation
 * maison ici, sinon les deux chemins produiraient deux valeurs pour un seul
 * geste utilisateur. Au-delà de la borne on ÉCARTE plutôt que tronquer — une
 * troncature ferait fusionner en silence deux thèmes distincts partageant leurs
 * 128 premiers caractères.
 */
export function resolveThemeSlug(raw: unknown): string | null {
  const trimmed = typeof raw === "string" ? raw.trim() : "";
  if (!trimmed || trimmed.length > MAX_THEME_LABEL_LENGTH) return null;
  return wasm.slugify(trimmed) || null;
}

/**
 * Ligne `keyword_themes` pour un libellé saisi, ou `null` si le libellé est
 * vide, hors borne, ou ne donne aucun slug.
 *
 * L'identifiant est `${siteId}-${slug}` : un thème est unique PAR SITE, pas
 * globalement (`keyword_themes` est une table par site — un site neuf n'en a
 * aucun, d'où la création à la volée).
 */
export function buildThemeRow(
  siteId: string,
  label: unknown,
): KeywordThemeRow | null {
  const trimmed = typeof label === "string" ? label.trim() : "";
  const slug = resolveThemeSlug(trimmed);
  if (!slug) return null;
  return { id: `${siteId}-${slug}`, siteId, slug, label: trimmed };
}

/**
 * Insertion IDEMPOTENTE des thèmes : deux listes cochées portant le même nom de
 * thème sont un cas nominal, pas un cas de bord — elles ne doivent pas échouer
 * sur un conflit de clé. Un thème déjà présent garde son libellé : une
 * ré-saisie ne renomme pas ce que l'utilisateur avait posé.
 */
export async function insertThemeRows(
  exec: Executor,
  rows: readonly KeywordThemeRow[],
): Promise<void> {
  if (rows.length === 0) return;
  await exec
    .insert(keywordThemes)
    .values([...rows])
    .onConflictDoNothing({ target: keywordThemes.id });
}
