import { afterAll, beforeEach, expect, mock, test } from "bun:test";
import { eq, inArray } from "drizzle-orm";
import { assertTestDb } from "@/lib/db/assert-test-db";
import { organization, user } from "@/lib/db/auth-schema";
import { client, db } from "@/lib/db/client";
import {
  contentItems,
  contentKeywords,
  keywords,
  sitePages,
  sites,
} from "@/lib/db/domain-schema";

// Mock next/cache so revalidatePath doesn't throw outside a Next.js request context.
mock.module("next/cache", () => ({
  revalidatePath: () => {},
  revalidateTag: () => {},
  updateTag: () => {},
  unstable_cache: (fn: unknown) => fn,
}));
// the tenancy guard needs a session (headers()) — stub the org check, but keep
// the siteId resolution REAL: the actions' same-site scoping (ELEVA-72) relies
// on the returned siteId.
mock.module("@/lib/sites/assert-site", () => ({
  siteBelongsToOrg: async () => true,
  assertSiteInActiveOrg: async () => {},
  assertKeywordInActiveOrg: async (id: string) => {
    const [row] = await db
      .select({ siteId: keywords.siteId })
      .from(keywords)
      .where(eq(keywords.id, id));
    if (!row)
      throw new Error("Keyword does not belong to the active organization");
    return row.siteId;
  },
  assertContentItemInActiveOrg: async (id: string) => {
    const [row] = await db
      .select({ siteId: contentItems.siteId })
      .from(contentItems)
      .where(eq(contentItems.id, id));
    if (!row)
      throw new Error(
        "Content item does not belong to the active organization",
      );
    return row.siteId;
  },
}));

const {
  createContentFromKeywordsAction,
  getContentItemsForSiteAction,
  updateContentStatusAction,
} = await import("./actions");

const SITE = "content-site";
const SITE_B = "content-site-b"; // autre site (même org) — cross-site ELEVA-72
const ORG = "content-org";
const USR = "content-user";

/** Insert a keyword for SITE (or `siteId`) matching the seeding pattern of kw-1. */
async function seedKw(id: string, term: string, siteId = SITE): Promise<void> {
  await db.insert(keywords).values({
    id,
    siteId,
    term,
    lang: "fr",
    country: "FR",
    status: "active",
    primarySource: "mangools",
    sources: ["mangools"],
  });
}

/** Insert a site_page (module Pages inventory) for tenancy + suggestion tests. */
async function seedPage(
  id: string,
  url: string,
  title: string | null,
  siteId = SITE,
): Promise<void> {
  await db.insert(sitePages).values({
    id,
    siteId,
    url,
    title,
    h1: title,
    crawledAt: new Date(),
  });
}

beforeEach(async () => {
  await assertTestDb();
  await db
    .delete(contentKeywords)
    .where(
      inArray(
        contentKeywords.contentItemId,
        db
          .select({ id: contentItems.id })
          .from(contentItems)
          .where(eq(contentItems.siteId, SITE)),
      ),
    );
  await db.delete(contentItems).where(eq(contentItems.siteId, SITE));
  await db.delete(keywords).where(inArray(keywords.siteId, [SITE, SITE_B]));
  await db.delete(sitePages).where(inArray(sitePages.siteId, [SITE, SITE_B]));
  await db.delete(sites).where(inArray(sites.id, [SITE, SITE_B]));
  await db.delete(organization).where(eq(organization.id, ORG));
  await db.delete(user).where(eq(user.id, USR));
  await db.insert(user).values({
    id: USR,
    name: "U",
    email: `content-${Date.now()}@e.com`,
    emailVerified: true,
  });
  await db.insert(organization).values({
    id: ORG,
    name: "O",
    slug: `content-${Date.now()}`,
    createdAt: new Date(),
  });
  await db.insert(sites).values([
    {
      id: SITE,
      orgId: ORG,
      ownerUserId: USR,
      name: "S",
      url: "https://e.com",
      primaryLanguage: "fr",
      targetCountries: ["FR"],
    },
    {
      id: SITE_B,
      orgId: ORG,
      ownerUserId: USR,
      name: "S2",
      url: "https://e2.com",
      primaryLanguage: "fr",
      targetCountries: ["FR"],
    },
  ]);
  await db.insert(keywords).values({
    id: "kw-1",
    siteId: SITE,
    term: "panneau solaire",
    lang: "fr",
    country: "FR",
    status: "active",
    primarySource: "mangools",
    sources: ["mangools"],
    metrics: {
      mangools: { volume: 100, kd: 20, cpc: 1, growthPct: null, updatedAt: "" },
    },
  });
});

afterAll(async () => {
  // NB: do NOT client.end() — shared singleton; closing breaks directory runs.
  void client;
});

test("createContentFromKeywords creates one idea item per keyword, skips dupes", async () => {
  const r1 = await createContentFromKeywordsAction(SITE, ["kw-1"]);
  expect(r1).toEqual({ created: 1 });
  const r2 = await createContentFromKeywordsAction(SITE, ["kw-1"]);
  expect(r2).toEqual({ created: 0 }); // already linked
  const items = await getContentItemsForSiteAction(SITE);
  expect(items.length).toBe(1);
  expect(items[0].title).toBe("panneau solaire");
  expect(items[0].status).toBe("idea");
  expect(items[0].keywordTerm).toBe("panneau solaire");
  expect(items[0].country).toBe("FR");
});

test("updateContentStatus changes status", async () => {
  await createContentFromKeywordsAction(SITE, ["kw-1"]);
  const [it] = await getContentItemsForSiteAction(SITE);
  await updateContentStatusAction(it.id, "writing");
  const [after] = await getContentItemsForSiteAction(SITE);
  expect(after.status).toBe("writing");
});

test("promote one-page: 1 item, roles primary/secondary, primaryKeywordId sync", async () => {
  await seedKw("k1", "alpha");
  await seedKw("k2", "bravo");
  await seedKw("k3", "charlie");
  const { promoteKeywordsToContentAction } = await import("./actions");
  const res = await promoteKeywordsToContentAction(
    SITE,
    ["k1", "k2", "k3"],
    "one-page",
    "k2",
  );
  expect(res.created).toBe(1);
  const items = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.siteId, SITE));
  expect(items.length).toBe(1);
  expect(items[0].primaryKeywordId).toBe("k2");
  expect(items[0].title).toBe("bravo");
  const links = await db
    .select()
    .from(contentKeywords)
    .where(eq(contentKeywords.contentItemId, items[0].id));
  expect(links.length).toBe(3);
  expect(links.find((l) => l.keywordId === "k2")?.role).toBe("primary");
  expect(links.filter((l) => l.role === "secondary").length).toBe(2);
});

test("promote per-keyword: N items + primary links + dedup skip", async () => {
  await seedKw("k1", "alpha");
  await seedKw("k2", "bravo");
  const { promoteKeywordsToContentAction } = await import("./actions");
  const r1 = await promoteKeywordsToContentAction(SITE, ["k1"], "per-keyword");
  expect(r1.created).toBe(1);
  const r2 = await promoteKeywordsToContentAction(
    SITE,
    ["k1", "k2"],
    "per-keyword",
  );
  expect(r2.created).toBe(1); // k1 skipped (already primary of an item)
  expect(r2.skipped).toEqual(["alpha"]);
  // Scope to this site's items so rows from other test files can't leak in.
  const links = await db
    .select({
      keywordId: contentKeywords.keywordId,
      role: contentKeywords.role,
    })
    .from(contentKeywords)
    .innerJoin(contentItems, eq(contentKeywords.contentItemId, contentItems.id))
    .where(eq(contentItems.siteId, SITE));
  expect(links.filter((l) => l.role === "primary").length).toBe(2);
});

test("getContentKeywordsAction falls back to primaryKeywordId when no link rows", async () => {
  await seedKw("k1", "alpha");
  await db.insert(contentItems).values({
    id: "ci1",
    siteId: SITE,
    title: "alpha",
    status: "idea",
    primaryKeywordId: "k1",
    lang: "fr",
    country: "FR",
  });
  const { getContentKeywordsAction } = await import("./actions");
  expect(await getContentKeywordsAction("ci1")).toEqual([
    { keywordId: "k1", term: "alpha", role: "primary" },
  ]);
});

test("setContentPrimaryKeywordAction swaps roles and syncs primaryKeywordId", async () => {
  await seedKw("k1", "alpha");
  await seedKw("k2", "bravo");
  const {
    promoteKeywordsToContentAction,
    setContentPrimaryKeywordAction,
    getContentKeywordsAction,
  } = await import("./actions");
  await promoteKeywordsToContentAction(SITE, ["k1", "k2"], "one-page", "k1");
  const item = (
    await db.select().from(contentItems).where(eq(contentItems.siteId, SITE))
  )[0];
  await setContentPrimaryKeywordAction(item.id, "k2");
  const links = await getContentKeywordsAction(item.id);
  expect(links.find((l) => l.keywordId === "k2")?.role).toBe("primary");
  expect(links.find((l) => l.keywordId === "k1")?.role).toBe("secondary");
  const after = (
    await db.select().from(contentItems).where(eq(contentItems.id, item.id))
  )[0];
  expect(after.primaryKeywordId).toBe("k2");
});

test("removeContentKeywordAction blocks primary, removes secondary; add dedups", async () => {
  await seedKw("k1", "alpha");
  await seedKw("k2", "bravo");
  await seedKw("k3", "charlie");
  const {
    promoteKeywordsToContentAction,
    removeContentKeywordAction,
    addContentKeywordsAction,
    getContentKeywordsAction,
  } = await import("./actions");
  await promoteKeywordsToContentAction(SITE, ["k1", "k2"], "one-page", "k1");
  const item = (
    await db.select().from(contentItems).where(eq(contentItems.siteId, SITE))
  )[0];
  expect((await removeContentKeywordAction(item.id, "k1")).ok).toBe(false); // primary blocked
  expect((await removeContentKeywordAction(item.id, "k2")).ok).toBe(true);
  await addContentKeywordsAction(item.id, ["k3", "k3"]);
  await addContentKeywordsAction(item.id, ["k3"]); // idempotent
  const links = await getContentKeywordsAction(item.id);
  expect(links.length).toBe(2); // k1 primary + k3 secondary
});

test("addContentKeywordsAction materializes the legacy primary (invariant kept)", async () => {
  await seedKw("lk1", "legacy-alpha");
  await seedKw("lk2", "new-bravo");
  await db.insert(contentItems).values({
    id: "ci-legacy",
    siteId: SITE,
    title: "legacy-alpha",
    status: "idea",
    primaryKeywordId: "lk1",
    lang: "fr",
    country: "FR",
  });
  const { addContentKeywordsAction, getContentKeywordsAction } = await import(
    "./actions"
  );
  await addContentKeywordsAction("ci-legacy", ["lk2"]);
  const links = await getContentKeywordsAction("ci-legacy");
  expect(links).toEqual([
    { keywordId: "lk1", term: "legacy-alpha", role: "primary" },
    { keywordId: "lk2", term: "new-bravo", role: "secondary" },
  ]);
  // adding the legacy primary itself must keep it primary, not duplicate as secondary
  await addContentKeywordsAction("ci-legacy", ["lk1"]);
  const links2 = await getContentKeywordsAction("ci-legacy");
  expect(links2.filter((l) => l.role === "primary").length).toBe(1);
  expect(links2.length).toBe(2);
});

test("addContentKeywordsAction drops keywords from another site (ELEVA-72)", async () => {
  await seedKw("k1", "alpha");
  await seedKw("k2", "bravo");
  await seedKw("kz", "foreign", SITE_B);
  const {
    promoteKeywordsToContentAction,
    addContentKeywordsAction,
    getContentKeywordsAction,
  } = await import("./actions");
  await promoteKeywordsToContentAction(SITE, ["k1"], "per-keyword");
  const item = (
    await db.select().from(contentItems).where(eq(contentItems.siteId, SITE))
  )[0];
  await addContentKeywordsAction(item.id, ["kz", "k2"]);
  const links = await getContentKeywordsAction(item.id);
  // k2 (même site) lié, kz (autre site) écarté en silence
  expect(links.map((l) => l.keywordId).sort()).toEqual(["k1", "k2"]);
  const ghost = await db
    .select()
    .from(contentKeywords)
    .where(eq(contentKeywords.keywordId, "kz"));
  expect(ghost).toEqual([]);
});

test("setContentPrimaryKeywordAction rejects a keyword from another site (ELEVA-72)", async () => {
  await seedKw("k1", "alpha");
  await seedKw("kz", "foreign", SITE_B);
  const { promoteKeywordsToContentAction, setContentPrimaryKeywordAction } =
    await import("./actions");
  await promoteKeywordsToContentAction(SITE, ["k1"], "per-keyword");
  const item = (
    await db.select().from(contentItems).where(eq(contentItems.siteId, SITE))
  )[0];
  await expect(setContentPrimaryKeywordAction(item.id, "kz")).rejects.toThrow(
    "does not belong to the content item's site",
  );
  const after = (
    await db.select().from(contentItems).where(eq(contentItems.id, item.id))
  )[0];
  expect(after.primaryKeywordId).toBe("k1"); // intact
  const links = await db
    .select()
    .from(contentKeywords)
    .where(eq(contentKeywords.contentItemId, item.id));
  expect(links.map((l) => l.keywordId)).toEqual(["k1"]); // liens intacts
});

// ── Task 3 : promotion optimize vs create (fusion par URL, kind, cannibalisation) ──

test("resolvePagesForKeywordsAction: mapped > suggested > gap", async () => {
  await seedPage("attach-p-mapped", "https://e.com/guide-solaire", "Guide");
  await seedPage(
    "attach-p-sugg",
    "https://e.com/borne-recharge",
    "borne recharge",
  );
  await seedKw("attach-mapped", "guide solaire");
  await seedKw("attach-sugg", "borne recharge");
  await seedKw("attach-gap", "zzz introuvable xyz");
  // kw mappé explicitement (targetPage manual en base) → mapped.
  await db
    .update(keywords)
    .set({ targetPageId: "attach-p-mapped", targetPageSource: "manual" })
    .where(eq(keywords.id, "attach-mapped"));

  const { resolvePagesForKeywordsAction } = await import("./actions");
  const res = await resolvePagesForKeywordsAction(SITE, [
    "attach-mapped",
    "attach-sugg",
    "attach-gap",
  ]);
  const byId = new Map(res.map((r) => [r.keywordId, r]));

  expect(byId.get("attach-mapped")?.page?.confidence).toBe("mapped");
  expect(byId.get("attach-mapped")?.page?.pageId).toBe("attach-p-mapped");
  expect(byId.get("attach-mapped")?.page?.source).toBe("manual");

  expect(byId.get("attach-sugg")?.page?.confidence).toBe("suggested");
  expect(byId.get("attach-sugg")?.page?.pageId).toBe("attach-p-sugg");
  expect(byId.get("attach-sugg")?.page?.source).toBe("text");

  expect(byId.get("attach-gap")?.page).toBeNull();
});

test("promote optimize: managed URL → merge, no new item, kind+status flip", async () => {
  await seedPage("attach-p1", "https://e.com/guide", "Guide");
  await seedKw("attach-primary", "guide existant");
  await seedKw("attach-opt", "guide solaire");
  // Fiche managée existante (url BRUTE avec slash final), publiée, legacy (aucune
  // ligne content_keywords → primary via primaryKeywordId).
  await db.insert(contentItems).values({
    id: "attach-ci-managed",
    siteId: SITE,
    title: "Guide",
    status: "published",
    primaryKeywordId: "attach-primary",
    lang: "fr",
    country: "FR",
    url: "https://e.com/guide/",
    kind: "new",
  });

  const { promoteKeywordsToContentAction } = await import("./actions");
  const res = await promoteKeywordsToContentAction(
    SITE,
    ["attach-opt"],
    "per-keyword",
    undefined,
    {
      "attach-opt": {
        action: "optimize",
        pageUrl: "https://e.com/guide",
        pageId: "attach-p1",
      },
    },
  );
  expect(res).toEqual({ created: 0, optimized: 0, merged: 1, skipped: [] });

  // Aucun nouvel item : toujours 1 fiche sur le site.
  const items = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.siteId, SITE));
  expect(items.length).toBe(1);
  expect(items[0].kind).toBe("optimize");
  expect(items[0].status).toBe("writing"); // published → writing

  // Liens : primary legacy matérialisé + attach-opt en secondary.
  const links = await db
    .select()
    .from(contentKeywords)
    .where(eq(contentKeywords.contentItemId, "attach-ci-managed"));
  expect(links.find((l) => l.keywordId === "attach-primary")?.role).toBe(
    "primary",
  );
  expect(links.find((l) => l.keywordId === "attach-opt")?.role).toBe(
    "secondary",
  );

  // kw reçoit targetPage manual.
  const [kw] = await db
    .select()
    .from(keywords)
    .where(eq(keywords.id, "attach-opt"));
  expect(kw.targetPageId).toBe("attach-p1");
  expect(kw.targetPageSource).toBe("manual");
});

test("promote optimize: unmanaged URL → 1 optimize item created", async () => {
  await seedPage("attach-p2", "https://e.com/nouvelle", "Nouvelle page");
  await seedKw("attach-new", "nouveau sujet");

  const { promoteKeywordsToContentAction } = await import("./actions");
  const res = await promoteKeywordsToContentAction(
    SITE,
    ["attach-new"],
    "per-keyword",
    undefined,
    {
      "attach-new": {
        action: "optimize",
        pageUrl: "https://e.com/nouvelle/",
        pageId: "attach-p2",
      },
    },
  );
  expect(res).toEqual({ created: 0, optimized: 1, merged: 0, skipped: [] });

  const items = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.siteId, SITE));
  expect(items.length).toBe(1);
  expect(items[0].kind).toBe("optimize");
  expect(items[0].url).toBe("https://e.com/nouvelle"); // normalisée (slash retiré)
  expect(items[0].title).toBe("Nouvelle page"); // titre site_pages
  expect(items[0].primaryKeywordId).toBe("attach-new");

  const links = await db
    .select()
    .from(contentKeywords)
    .where(eq(contentKeywords.contentItemId, items[0].id));
  expect(links).toHaveLength(1);
  expect(links[0].role).toBe("primary");
});

test("promote create-anyway then optimize same page → merges into existing item (no dup)", async () => {
  // Page serveur avec slash final : prouve l'URL server-trusted + la normalisation.
  await seedPage("attach-p4", "https://e.com/service/", "Service");
  await seedKw("attach-ca", "service local");
  const { promoteKeywordsToContentAction } = await import("./actions");

  // 1) « Créer quand même » : fiche kind new, url NULL, kw primaire.
  const create = await promoteKeywordsToContentAction(
    SITE,
    ["attach-ca"],
    "per-keyword",
    undefined,
    {
      "attach-ca": {
        action: "create",
        warnedPageUrl: "https://e.com/service",
      },
    },
  );
  expect(create).toEqual({ created: 1, optimized: 0, merged: 0, skipped: [] });

  // 2) Re-promotion « Optimiser » sur la même page → fusion, pas de 2ᵉ fiche.
  // pageUrl client bidon : ignoré, l'URL vient de site_pages (server-trusted).
  const opt = await promoteKeywordsToContentAction(
    SITE,
    ["attach-ca"],
    "per-keyword",
    undefined,
    {
      "attach-ca": {
        action: "optimize",
        pageUrl: "https://evil.example/injected",
        pageId: "attach-p4",
      },
    },
  );
  expect(opt).toEqual({ created: 0, optimized: 0, merged: 1, skipped: [] });

  // Une seule fiche : la « Créer quand même » a muté en optimize, pas de doublon.
  const items = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.siteId, SITE));
  expect(items.length).toBe(1);
  expect(items[0].primaryKeywordId).toBe("attach-ca"); // kw reste primaire
  expect(items[0].kind).toBe("optimize");
  expect(items[0].url).toBe("https://e.com/service"); // URL serveur normalisée

  // kw reçoit targetPage manual.
  const [kw] = await db
    .select()
    .from(keywords)
    .where(eq(keywords.id, "attach-ca"));
  expect(kw.targetPageId).toBe("attach-p4");
  expect(kw.targetPageSource).toBe("manual");
});

test("promote optimize: 2 kw same URL → 1 item + 1 merge; re-promotion skips", async () => {
  await seedPage("attach-p3", "https://e.com/energie", "Énergie");
  await seedKw("attach-e1", "energie solaire");
  await seedKw("attach-e2", "energie verte");

  const { promoteKeywordsToContentAction } = await import("./actions");
  const decision = {
    action: "optimize" as const,
    pageUrl: "https://e.com/energie",
    pageId: "attach-p3",
  };
  const res = await promoteKeywordsToContentAction(
    SITE,
    ["attach-e1", "attach-e2"],
    "per-keyword",
    undefined,
    { "attach-e1": decision, "attach-e2": decision },
  );
  // 1er crée la fiche optimize, 2ᵉ fusionne dedans (intra-lot).
  expect(res).toEqual({ created: 0, optimized: 1, merged: 1, skipped: [] });

  const items = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.siteId, SITE));
  expect(items.length).toBe(1); // pas de doublon

  // Re-promotion du même kw déjà lié → skip compté, aucun changement.
  const res2 = await promoteKeywordsToContentAction(
    SITE,
    ["attach-e1"],
    "per-keyword",
    undefined,
    { "attach-e1": decision },
  );
  expect(res2).toEqual({
    created: 0,
    optimized: 0,
    merged: 0,
    skipped: ["energie solaire"],
  });
  const itemsAfter = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.siteId, SITE));
  expect(itemsAfter.length).toBe(1);
});

test("promote create with warnedPageUrl → kind new + cannibalisation trace", async () => {
  await seedKw("attach-warn", "sujet neuf");
  const { promoteKeywordsToContentAction } = await import("./actions");
  const res = await promoteKeywordsToContentAction(
    SITE,
    ["attach-warn"],
    "per-keyword",
    undefined,
    {
      "attach-warn": {
        action: "create",
        warnedPageUrl: "https://e.com/page-ignoree",
      },
    },
  );
  expect(res).toEqual({ created: 1, optimized: 0, merged: 0, skipped: [] });
  const [item] = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.siteId, SITE));
  expect(item.kind).toBe("new");
  expect(item.warnedPageUrl).toBe("https://e.com/page-ignoree");
});

test("promote per-keyword without decisions → unchanged (create, kind new, warned null)", async () => {
  await seedKw("attach-plain", "sujet simple");
  const { promoteKeywordsToContentAction } = await import("./actions");
  const res = await promoteKeywordsToContentAction(
    SITE,
    ["attach-plain"],
    "per-keyword",
  );
  expect(res).toEqual({ created: 1, optimized: 0, merged: 0, skipped: [] });
  const [item] = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.siteId, SITE));
  expect(item.kind).toBe("new");
  expect(item.warnedPageUrl).toBeNull();
});

test("promote optimize: page from another site is refused (tenancy)", async () => {
  // Page + fiche managée appartiennent à SITE_B ; le kw est sur SITE.
  await seedPage("attach-pb", "https://e.com/partage", "Partagé", SITE_B);
  await seedKw("attach-foreign-primary", "partage b", SITE_B);
  await db.insert(contentItems).values({
    id: "attach-ci-b",
    siteId: SITE_B,
    title: "Partagé B",
    status: "published",
    primaryKeywordId: "attach-foreign-primary",
    lang: "fr",
    country: "FR",
    url: "https://e.com/partage",
    kind: "new",
  });
  await seedKw("attach-cross", "partage");

  const { promoteKeywordsToContentAction } = await import("./actions");
  const res = await promoteKeywordsToContentAction(
    SITE,
    ["attach-cross"],
    "per-keyword",
    undefined,
    {
      "attach-cross": {
        action: "optimize",
        pageUrl: "https://e.com/partage",
        pageId: "attach-pb", // page d'un AUTRE site
      },
    },
  );
  // pageId hors site → refusé, rien créé/fusionné.
  expect(res).toEqual({
    created: 0,
    optimized: 0,
    merged: 0,
    skipped: ["partage"],
  });
  // Aucune fiche sur SITE.
  const siteItems = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.siteId, SITE));
  expect(siteItems.length).toBe(0);
  // La fiche de SITE_B est intacte (kind new, publiée, kw non lié).
  const [bItem] = await db
    .select()
    .from(contentItems)
    .where(eq(contentItems.id, "attach-ci-b"));
  expect(bItem.kind).toBe("new");
  expect(bItem.status).toBe("published");
  const bLinks = await db
    .select()
    .from(contentKeywords)
    .where(eq(contentKeywords.keywordId, "attach-cross"));
  expect(bLinks).toEqual([]);
});
