// src/lib/keywords/enrich-status.test.ts — rail de statut d'enrichissement par
// mot-clé (postgres, section gated DB_TESTS=1, même pattern que
// connectors/store.test.ts + actions.test.ts). On prouve la sémantique clé du
// rail : provider-agnostique (mangools et seranking coexistent sans écrasement
// croisé), transitions d'état, retrait ciblé par provider, no-op tableau vide
// (0 requête), et le scoping site de l'action de lecture.
import { beforeEach, describe, expect, mock, spyOn, test } from "bun:test";

// enrich-status.ts importe "server-only" (throw hors Next) — stub.
mock.module("server-only", () => ({}));
// L'action passe par la garde tenancy (headers()/session absents en test) —
// stub : le scoping site est prouvé au niveau SQL par ces tests (filtre siteId).
mock.module("@/lib/sites/assert-site", () => ({
  siteBelongsToOrg: async () => true,
  assertSiteInActiveOrg: async () => {},
  assertKeywordInActiveOrg: async () => "mock-site",
  assertContentItemInActiveOrg: async () => "mock-site",
}));

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

describe.skipIf(!RUN)("keyword enrich-status rail (postgres)", () => {
  let db: typeof import("@/lib/db/client").db;
  let schema: typeof import("@/lib/db/domain-schema");
  let store: typeof import("./enrich-status");
  let actions: typeof import("./enrich-status-actions");

  const SITE_A = "enrich-site-a";
  const SITE_B = "enrich-site-b";

  async function insertKeyword(id: string, siteId: string, term: string) {
    await db.insert(schema.keywords).values({
      id,
      siteId,
      term,
      lang: "fr",
      country: "FR",
      status: "idea",
      primarySource: "mangools",
      sources: ["mangools"],
      trend: [],
    });
  }

  async function readEnrichment(id: string) {
    const { eq } = await import("drizzle-orm");
    const [row] = await db
      .select({ enrichment: schema.keywords.enrichment })
      .from(schema.keywords)
      .where(eq(schema.keywords.id, id));
    return row?.enrichment ?? null;
  }

  beforeEach(async () => {
    ({ db } = await import("@/lib/db/client"));
    schema = await import("@/lib/db/domain-schema");
    store = await import("./enrich-status");
    actions = await import("./enrich-status-actions");
    const { assertTestDb } = await import("@/lib/db/assert-test-db");
    await assertTestDb();
    await db.delete(schema.keywords);
    await db.delete(schema.sites);
    await db.insert(schema.sites).values([
      {
        id: SITE_A,
        orgId: "demo-org",
        ownerUserId: "seed-owner",
        name: "Enrich Site A",
        url: "https://enrich-a.test",
        primaryLanguage: "fr",
        languages: [],
        targetCountries: ["FR"],
      },
      {
        id: SITE_B,
        orgId: "demo-org",
        ownerUserId: "seed-owner",
        name: "Enrich Site B",
        url: "https://enrich-b.test",
        primaryLanguage: "fr",
        languages: [],
        targetCountries: ["FR"],
      },
    ]);
  });

  test("setEnrichStatus : pose le statut du provider sans écraser un autre provider (multi-provider)", async () => {
    await insertKeyword("k1", SITE_A, "alpha");

    await store.setEnrichStatus(["k1"], "mangools", {
      status: "queued",
      at: "2026-07-25T10:00:00.000Z",
    });
    await store.setEnrichStatus(["k1"], "seranking", {
      status: "syncing",
      at: "2026-07-25T10:01:00.000Z",
    });

    // Les deux providers coexistent : poser seranking n'a pas effacé mangools.
    expect(await readEnrichment("k1")).toEqual({
      mangools: { status: "queued", at: "2026-07-25T10:00:00.000Z" },
      seranking: { status: "syncing", at: "2026-07-25T10:01:00.000Z" },
    });
  });

  test("setEnrichStatus : transitions queued → syncing → done (remplace la clé du provider)", async () => {
    await insertKeyword("k1", SITE_A, "alpha");

    await store.setEnrichStatus(["k1"], "mangools", {
      status: "queued",
      at: "2026-07-25T10:00:00.000Z",
    });
    await store.setEnrichStatus(["k1"], "mangools", {
      status: "syncing",
      at: "2026-07-25T10:00:30.000Z",
    });
    await store.setEnrichStatus(["k1"], "mangools", {
      status: "done",
      at: "2026-07-25T10:01:00.000Z",
    });

    expect(await readEnrichment("k1")).toEqual({
      mangools: { status: "done", at: "2026-07-25T10:01:00.000Z" },
    });
  });

  test("setEnrichStatus : error porte le champ error", async () => {
    await insertKeyword("k1", SITE_A, "alpha");
    await store.setEnrichStatus(["k1"], "mangools", {
      status: "error",
      at: "2026-07-25T10:00:00.000Z",
      error: "quota exceeded",
    });
    expect(await readEnrichment("k1")).toEqual({
      mangools: {
        status: "error",
        at: "2026-07-25T10:00:00.000Z",
        error: "quota exceeded",
      },
    });
  });

  test("clearEnrichStatus : retire uniquement la clé du provider, laisse les autres", async () => {
    await insertKeyword("k1", SITE_A, "alpha");
    await store.setEnrichStatus(["k1"], "mangools", {
      status: "done",
      at: "2026-07-25T10:00:00.000Z",
    });
    await store.setEnrichStatus(["k1"], "seranking", {
      status: "done",
      at: "2026-07-25T10:01:00.000Z",
    });

    await store.clearEnrichStatus(["k1"], "mangools");

    // seranking survit, mangools disparu.
    expect(await readEnrichment("k1")).toEqual({
      seranking: { status: "done", at: "2026-07-25T10:01:00.000Z" },
    });
  });

  test("setEnrichStatus / clearEnrichStatus : no-op sur tableau vide (0 requête)", async () => {
    const updateSpy = spyOn(db, "update");
    try {
      await store.setEnrichStatus([], "mangools", {
        status: "queued",
        at: "2026-07-25T10:00:00.000Z",
      });
      await store.clearEnrichStatus([], "mangools");
      expect(updateSpy).not.toHaveBeenCalled();
    } finally {
      updateSpy.mockRestore();
    }
  });

  test("fetchEnrichStatusesAction : renvoie la map id → enrichment (site-scopée)", async () => {
    await insertKeyword("k1", SITE_A, "alpha");
    await insertKeyword("k2", SITE_A, "bravo");
    await store.setEnrichStatus(["k1"], "mangools", {
      status: "syncing",
      at: "2026-07-25T10:00:00.000Z",
    });

    const map = await actions.fetchEnrichStatusesAction(SITE_A, ["k1", "k2"]);
    expect(map).toEqual({
      k1: { mangools: { status: "syncing", at: "2026-07-25T10:00:00.000Z" } },
      k2: {}, // pas d'état → défaut {}
    });
  });

  test("fetchEnrichStatusesAction : un id d'un AUTRE site n'est pas renvoyé (scoping SQL)", async () => {
    await insertKeyword("k1", SITE_A, "alpha");
    await insertKeyword("kx", SITE_B, "foreign");
    await store.setEnrichStatus(["kx"], "mangools", {
      status: "done",
      at: "2026-07-25T10:00:00.000Z",
    });

    // On demande kx (site B) en interrogeant le site A : il doit être ignoré.
    const map = await actions.fetchEnrichStatusesAction(SITE_A, ["k1", "kx"]);
    expect(map).toEqual({ k1: {} });
    expect(map).not.toHaveProperty("kx");
  });

  test("fetchEnrichStatusesAction : tableau d'ids vide → map vide", async () => {
    expect(await actions.fetchEnrichStatusesAction(SITE_A, [])).toEqual({});
  });
});
