// src/lib/content/pages-overview.test.ts — action overview « santé des pages »
// contre le VRAI Postgres (seo_insight_test, script `test` → DB_TESTS=1). Même
// pattern DB-gated que src/lib/pages/actions-core.test.ts : insert site +
// site_pages + keywords, mock `assertSite` (server-only, jamais exposé au
// réseau). Vérifie score/level (scorer Task 1), résolution du mot-clé cible
// (min(term) déterministe), tri par score croissant et les 4 KPIs.
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { eq, inArray } from "drizzle-orm";
import { assertTestDb } from "@/lib/db/assert-test-db";
import { organization } from "@/lib/db/auth-schema";
import { db } from "@/lib/db/client";
import { keywords, sitePages, sites } from "@/lib/db/domain-schema";
import { getContentPagesOverviewAction } from "./pages-overview";

const ORG = "org-covw-test";
const SITE_A = "site-covw-a";
const SITE_B = "site-covw-b";
const PG_A1 = "site-covw-a-p1"; // saine, mot-clé cible
const PG_A2 = "site-covw-a-p2"; // issues, sans mot-clé
const PG_B1 = "site-covw-b-p1"; // autre site (tenancy)
const KW_A1a = "site-covw-a-kw1a";
const KW_A1b = "site-covw-a-kw1b";

const SITE_IDS = [SITE_A, SITE_B];
const ASSERT_OK = async () => {};

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

async function cleanup() {
  await db.delete(keywords).where(inArray(keywords.siteId, SITE_IDS));
  await db.delete(sitePages).where(inArray(sitePages.siteId, SITE_IDS));
  await db.delete(sites).where(inArray(sites.id, SITE_IDS));
  await db.delete(organization).where(eq(organization.id, ORG));
}

describe.skipIf(!RUN)("content pages-overview (postgres)", () => {
  beforeAll(async () => {
    await assertTestDb();
    await cleanup();
    await db.insert(organization).values({
      id: ORG,
      name: "covw",
      slug: "covw",
      createdAt: new Date(),
    });
    await db.insert(sites).values([
      {
        id: SITE_A,
        orgId: ORG,
        name: "A",
        primaryLanguage: "fr",
        url: "https://a.example",
      },
      {
        id: SITE_B,
        orgId: ORG,
        name: "B",
        primaryLanguage: "fr",
        url: "https://b.example",
      },
    ]);
    await db.insert(sitePages).values([
      {
        id: PG_A1,
        siteId: SITE_A,
        url: "https://a.example/bague-or",
        status: 200,
        title: "Bague en or 18 carats — collection éternelle",
        metaDescription:
          "Une description meta de longueur tout à fait correcte.",
        h1: "Bague en or",
        h1Count: 1,
        wordCount: 800,
        issues: [], // score 100 → ok
        crawledAt: new Date(),
      },
      {
        id: PG_A2,
        siteId: SITE_A,
        url: "https://a.example/collier",
        status: 200,
        title: null,
        metaDescription: null,
        h1: null,
        h1Count: 0,
        wordCount: 120,
        // title-missing (20) + description-missing (12) + h1-missing (15) = 47
        // → score 53 → warn
        issues: ["title-missing", "description-missing", "h1-missing"],
        crawledAt: new Date(),
      },
      {
        id: PG_B1,
        siteId: SITE_B,
        url: "https://b.example/autre",
        status: 200,
        issues: [],
        crawledAt: new Date(),
      },
    ]);
    const baseKw = {
      lang: "fr",
      country: "FR",
      status: "idea" as const,
      intent: ["informational" as const],
      primarySource: "semrush" as const,
      sources: ["semrush" as const],
      metrics: {},
      trend: [],
    };
    // Deux kw ciblent PG_A1 → le mot-clé cible est déterministe = min(term).
    await db.insert(keywords).values([
      {
        id: KW_A1a,
        siteId: SITE_A,
        term: "bijou or",
        targetPageId: PG_A1,
        ...baseKw,
      },
      {
        id: KW_A1b,
        siteId: SITE_A,
        term: "bague or",
        targetPageId: PG_A1,
        ...baseKw,
      },
    ]);
  });

  afterAll(cleanup);

  test("gate: assertSite rejette → l'action propage", async () => {
    const boom = async () => {
      throw new Error("forbidden");
    };
    await expect(
      getContentPagesOverviewAction(SITE_A, { assertSite: boom }),
    ).rejects.toThrow("forbidden");
  });

  test("pages: score/level, mot-clé cible (min term) vs null, tri score croissant, tenancy", async () => {
    const { pages } = await getContentPagesOverviewAction(SITE_A, {
      assertSite: ASSERT_OK,
    });

    // Tenancy : seules les pages de SITE_A, PG_B1 exclue.
    expect(pages.map((p) => p.id)).toEqual([PG_A2, PG_A1]); // tri score croissant

    const a1 = pages.find((p) => p.id === PG_A1);
    const a2 = pages.find((p) => p.id === PG_A2);

    // Page saine → 100 / ok, mot-clé cible = min("bague or","bijou or").
    expect(a1?.score).toBe(100);
    expect(a1?.level).toBe("ok");
    expect(a1?.targetKeyword).toBe("bague or");
    expect(a1?.title).toBe("Bague en or 18 carats — collection éternelle");
    expect(a1?.wordCount).toBe(800);
    expect(a1?.h1Count).toBe(1);
    expect(a1?.crawledAt).toBeInstanceOf(Date);

    // Page à problèmes → 53 / warn, sans mot-clé cible.
    expect(a2?.score).toBe(53);
    expect(a2?.level).toBe("warn");
    expect(a2?.targetKeyword).toBeNull();
    expect(a2?.issues).toEqual([
      "title-missing",
      "description-missing",
      "h1-missing",
    ]);
    expect(a2?.title).toBeNull();
    expect(a2?.metaDescription).toBeNull();
  });

  test("kpis: tracked, avgScore (arrondi), toFix (level≠ok), withoutKeyword", async () => {
    const { kpis } = await getContentPagesOverviewAction(SITE_A, {
      assertSite: ASSERT_OK,
    });
    expect(kpis.tracked).toBe(2);
    // moyenne (100 + 53) / 2 = 76.5 → arrondi 77
    expect(kpis.avgScore).toBe(77);
    expect(kpis.toFix).toBe(1); // PG_A2 (warn)
    expect(kpis.withoutKeyword).toBe(1); // PG_A2
  });

  test("site sans page : KPIs à zéro, avgScore 0", async () => {
    const { pages } = await getContentPagesOverviewAction(SITE_B, {
      assertSite: ASSERT_OK,
    });
    // PG_B1 appartient à SITE_B → présent.
    expect(pages.map((p) => p.id)).toEqual([PG_B1]);
    const empty = await getContentPagesOverviewEmpty();
    expect(empty.kpis).toEqual({
      tracked: 0,
      avgScore: 0,
      toFix: 0,
      withoutKeyword: 0,
    });
  });
});

// Site inexistant (aucune page) → moyenne d'un ensemble vide = 0, pas NaN.
async function getContentPagesOverviewEmpty() {
  return getContentPagesOverviewAction("site-covw-void", {
    assertSite: ASSERT_OK,
  });
}
