// src/lib/content/import-actions.test.ts — integration, gated on DB_TESTS=1.
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";

// server-only throws outside Next.js; stub it for the test runner.
mock.module("server-only", () => ({}));
// actions call revalidatePath, which throws outside a Next request scope.
mock.module("next/cache", () => ({
  revalidatePath: () => {},
  revalidateTag: () => {},
  updateTag: () => {},
  unstable_cache: (fn: unknown) => fn,
}));
// the tenancy guard needs a session (headers()) — stub it; site-scoping is
// covered at the SQL level by these tests and by assert-site.test.ts.
mock.module("@/lib/sites/assert-site", () => ({
  siteBelongsToOrg: async () => true,
  assertSiteInActiveOrg: async () => {},
  assertKeywordInActiveOrg: async () => "mock-site",
  assertContentItemInActiveOrg: async () => "mock-site",
}));

const RUN_DB = process.env.DB_TESTS === "1";

describe.skipIf(!RUN_DB)("content CSV import/export actions (postgres)", () => {
  let db: typeof import("@/lib/db/client").db;
  let client: typeof import("@/lib/db/client").client;
  let schema: typeof import("@/lib/db/domain-schema");
  let importActions: typeof import("./import-actions");
  let actions: typeof import("./actions");
  const ORG = "demo-org"; // guaranteed by db:seed / test DB setup
  const SITE_A = "wt-csv-a";
  const SITE_B = "wt-csv-b";

  const HEADER =
    "Titre,Statut,Mot-clé principal,Mots-clés secondaires,Langue,Pays,Assigné,Échéance,URL";

  async function seedKw(id: string, term: string): Promise<void> {
    await db.insert(schema.keywords).values({
      id,
      siteId: SITE_A,
      term,
      lang: "fr",
      country: "FR",
      status: "active",
      primarySource: "mangools",
      sources: ["mangools"],
    });
  }

  async function seedItem(
    id: string,
    siteId: string,
    title: string,
  ): Promise<void> {
    await db.insert(schema.contentItems).values({
      id,
      siteId,
      title,
      status: "idea",
      lang: "fr",
      country: "FR",
    });
  }

  beforeEach(async () => {
    ({ db, client } = await import("@/lib/db/client"));
    schema = await import("@/lib/db/domain-schema");
    importActions = await import("./import-actions");
    actions = await import("./actions");
    const { assertTestDb } = await import("@/lib/db/assert-test-db");
    const { eq, inArray } = await import("drizzle-orm");
    await assertTestDb();
    await db.delete(schema.contentKeywords).where(
      inArray(
        schema.contentKeywords.contentItemId,
        db
          .select({ id: schema.contentItems.id })
          .from(schema.contentItems)
          .where(inArray(schema.contentItems.siteId, [SITE_A, SITE_B])),
      ),
    );
    await db
      .delete(schema.contentItems)
      .where(inArray(schema.contentItems.siteId, [SITE_A, SITE_B]));
    await db.delete(schema.keywords).where(eq(schema.keywords.siteId, SITE_A));
    await db
      .delete(schema.sites)
      .where(inArray(schema.sites.id, [SITE_A, SITE_B]));
    await db.insert(schema.sites).values([
      {
        id: SITE_A,
        orgId: ORG,
        ownerUserId: "seed-owner",
        name: "WT CSV A",
        url: "https://wt-csv-a.com",
        // ≠ schema defaults (fr/FR) on purpose: proves create uses *site* defaults.
        primaryLanguage: "de",
        languages: [],
        targetCountries: ["DE"],
      },
      {
        id: SITE_B,
        orgId: ORG,
        ownerUserId: "seed-owner",
        name: "WT CSV B",
        url: "https://wt-csv-b.com",
        primaryLanguage: "fr",
        languages: [],
        targetCountries: ["FR"],
      },
    ]);
  });

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

  test("preview: new/update par titre (casse/trim), terme principal inconnu, counts", async () => {
    await seedItem("ci-csv-1", SITE_A, "Page Existante");
    await seedKw("kw-csv-1", "audit énergie");
    const csv = [
      HEADER,
      "Nouvelle Page,writing,audit énergie,,,,,,",
      '"  page existante  ",review,terme inconnu,,,,,,',
      ",idea,,,,,,,", // titre manquant → erreur
    ].join("\n");
    const preview = await importActions.previewContentImportAction(SITE_A, csv);
    expect(preview.rows).toEqual([
      {
        line: 2,
        title: "Nouvelle Page",
        action: "new",
        status: "writing",
        primaryKeywordTerm: "audit énergie",
        primaryKeywordFound: true,
      },
      {
        line: 3,
        title: "page existante",
        action: "update",
        status: "review",
        primaryKeywordTerm: "terme inconnu",
        primaryKeywordFound: false,
      },
    ]);
    expect(preview.errors).toEqual([{ line: 4, error: "Titre requis" }]);
    expect(preview.counts).toEqual({ created: 1, updated: 1, errors: 1 });
  });

  test("commit: crée avec défauts du site, met à jour par titre, site B intact", async () => {
    const { eq } = await import("drizzle-orm");
    await seedItem("ci-csv-1", SITE_A, "Page Existante");
    await seedItem("ci-csv-b", SITE_B, "Page Existante");
    const csv = [
      HEADER,
      "Article Neuf,brief,,,,,bob,2026-07-01,https://a.fr/x",
      "PAGE EXISTANTE,review,,,,,alice,,",
    ].join("\n");
    const res = await importActions.commitContentImportAction(SITE_A, csv);
    expect(res).toEqual({
      created: 1,
      updated: 1,
      errors: 0,
      keywordsLinked: 0,
      keywordsUnknown: 0,
    });
    const itemsA = await db
      .select()
      .from(schema.contentItems)
      .where(eq(schema.contentItems.siteId, SITE_A));
    expect(itemsA.length).toBe(2);
    const neuf = itemsA.find((i) => i.title === "Article Neuf");
    expect(neuf?.status).toBe("brief");
    expect(neuf?.lang).toBe("de"); // défaut du site, pas du schéma
    expect(neuf?.country).toBe("DE");
    expect(neuf?.assignee).toBe("bob");
    expect(neuf?.dueDate?.toISOString().slice(0, 10)).toBe("2026-07-01");
    expect(neuf?.url).toBe("https://a.fr/x");
    const exist = itemsA.find((i) => i.id === "ci-csv-1");
    expect(exist?.title).toBe("Page Existante"); // titre original conservé
    expect(exist?.status).toBe("review");
    expect(exist?.assignee).toBe("alice");
    expect(exist?.url).toBe(null); // champ vide → non touché
    // isolation : le site B garde son item tel quel
    const [itemB] = await db
      .select()
      .from(schema.contentItems)
      .where(eq(schema.contentItems.siteId, SITE_B));
    expect(itemB.status).toBe("idea");
    expect(itemB.assignee).toBe(null);
  });

  test("commit: lie principal (sync primaryKeywordId + role primary) et secondaires ; terme inconnu compté non bloquant", async () => {
    const { eq } = await import("drizzle-orm");
    await seedKw("kw-csv-1", "audit énergie");
    await seedKw("kw-csv-2", "panneaux");
    const csv = [
      HEADER,
      "Article KW,idea,Audit Énergie,panneaux|terme inconnu,,,,,",
    ].join("\n");
    const res = await importActions.commitContentImportAction(SITE_A, csv);
    expect(res).toEqual({
      created: 1,
      updated: 0,
      errors: 0,
      keywordsLinked: 2,
      keywordsUnknown: 1,
    });
    const [item] = await db
      .select()
      .from(schema.contentItems)
      .where(eq(schema.contentItems.siteId, SITE_A));
    expect(item.primaryKeywordId).toBe("kw-csv-1");
    const links = await db
      .select()
      .from(schema.contentKeywords)
      .where(eq(schema.contentKeywords.contentItemId, item.id));
    expect(links.length).toBe(2);
    expect(links.find((l) => l.keywordId === "kw-csv-1")?.role).toBe("primary");
    expect(links.find((l) => l.keywordId === "kw-csv-2")?.role).toBe(
      "secondary",
    );
  });

  test("getContentExportRowsAction: termes principal/secondaires joints, dueDate ISO court", async () => {
    await seedKw("kw-csv-1", "audit énergie");
    await seedKw("kw-csv-2", "panneaux");
    const csv = [
      HEADER,
      "Article KW,writing,audit énergie,panneaux,fr,FR,bob,2026-07-01,https://a.fr/x",
    ].join("\n");
    await importActions.commitContentImportAction(SITE_A, csv);
    const rows = await actions.getContentExportRowsAction(SITE_A);
    expect(rows).toEqual([
      {
        title: "Article KW",
        status: "writing",
        primaryKeywordTerm: "audit énergie",
        secondaryKeywordTerms: ["panneaux"],
        lang: "fr",
        country: "FR",
        assignee: "bob",
        dueDate: "2026-07-01",
        url: "https://a.fr/x",
      },
    ]);
  });
});
