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

// Le gating des snapshotRuns (table GLOBALE cross-tenant) porte désormais sur le
// statut super-admin plateforme, PAS sur le rôle d'org (décision Bastien
// 2026-07-23). On pilote isCurrentUserSuperAdmin via une variable mutable.
let IS_SUPER_ADMIN = true;
mock.module("@/lib/active-org", () => ({
  getActiveOrgId: async () => "gsc-health-org",
  getActiveUserId: async () => "gsc-health-user",
}));
// Mock COMPLET de @/lib/auth/super-admin (mock.module est global : un mock
// partiel fuit entre fichiers). Seul isCurrentUserSuperAdmin porte le gating testé.
mock.module("@/lib/auth/super-admin", () => ({
  emailInCsv: () => IS_SUPER_ADMIN,
  isSuperAdmin: () => IS_SUPER_ADMIN,
  isCurrentUserSuperAdmin: async () => IS_SUPER_ADMIN,
  assertSuperAdmin: async () => {},
  getSuperAdminSession: async () => null,
  assertSuperAdminWith2FA: async () => {},
}));

const { getGscHealthAction } = await import("./health-actions");

// GscTracking exige clicks/impressions/ctr/position/updatedAt : seul updatedAt
// porte le signal testé (max lexicographique), le reste est du remplissage.
const gscAt = (updatedAt: string) => ({
  clicks: 0,
  impressions: 0,
  ctr: 0,
  position: 0,
  updatedAt,
});

beforeEach(async () => {
  await assertTestDb();
  IS_SUPER_ADMIN = true;
  await db
    .delete(gscSnapshotRuns)
    .where(
      inArray(gscSnapshotRuns.id, [
        "r1",
        "r2",
        "r3",
        ...Array.from({ length: 9 }, (_, i) => `bulk-${i}`),
      ]),
    );
  await db
    .delete(keywords)
    .where(
      inArray(keywords.siteId, ["gsc-health-site-a", "gsc-health-site-b"]),
    );
  await db.delete(sitesTable).where(eq(sitesTable.orgId, "gsc-health-org"));
  await db.delete(member).where(eq(member.organizationId, "gsc-health-org"));
  await db.delete(organization).where(eq(organization.id, "gsc-health-org"));
  await db.delete(user).where(eq(user.id, "gsc-health-user"));
  await db
    .insert(user)
    .values({
      id: "gsc-health-user",
      name: "U",
      email: `gsc-health-${Date.now()}@example.com`,
      emailVerified: true,
    });
  await db
    .insert(organization)
    .values({
      id: "gsc-health-org",
      name: "Org",
      slug: `gsc-health-${Date.now()}`,
      createdAt: new Date(),
    });
  await db
    .insert(member)
    .values({
      id: "gsc-health-m",
      organizationId: "gsc-health-org",
      userId: "gsc-health-user",
      role: "owner",
      createdAt: new Date(),
    });
});
afterAll(() => mock.restore());

test("org sans sites → syncBySite vide, runs remontés (super-admin)", async () => {
  await db
    .insert(gscSnapshotRuns)
    .values({
      id: "r1",
      startedAt: new Date("2026-07-23T06:00:00Z"),
      finishedAt: new Date("2026-07-23T06:04:00Z"),
      sitesOk: 2,
      sitesFailed: 0,
      rowsUpserted: 1200,
      error: null,
    });
  const health = await getGscHealthAction();
  expect(health.syncBySite).toEqual([]);
  expect(health.snapshotRuns.map((r) => r.id)).toContain("r1");
});

test("lastGscSyncAt par site : max(gsc->>updatedAt), null si jamais synchronisé", async () => {
  await db.insert(sitesTable).values([
    {
      id: "gsc-health-site-a",
      orgId: "gsc-health-org",
      name: "A",
      url: "https://a.example",
    },
    {
      id: "gsc-health-site-b",
      orgId: "gsc-health-org",
      name: "B",
      url: "https://b.example",
    },
  ]);
  await db.insert(keywords).values([
    {
      id: "gsc-health-site-a-k1",
      siteId: "gsc-health-site-a",
      term: "k1",
      status: "active",
      primarySource: "gsc",
      gsc: gscAt("2026-07-20T10:00:00Z"),
    },
    {
      id: "gsc-health-site-a-k2",
      siteId: "gsc-health-site-a",
      term: "k2",
      status: "active",
      primarySource: "gsc",
      gsc: gscAt("2026-07-22T10:00:00Z"),
    },
  ]);
  const health = await getGscHealthAction();
  const bySite = new Map(
    health.syncBySite.map((s) => [s.siteId, s.lastGscSyncAt]),
  );
  expect(bySite.get("gsc-health-site-a")).toBe("2026-07-22T10:00:00Z");
  expect(bySite.get("gsc-health-site-b")).toBeNull();
});

test("runs : tri décroissant par startedAt, limite 7", async () => {
  const runs = Array.from({ length: 9 }, (_, i) => ({
    id: `bulk-${i}`,
    startedAt: new Date(Date.UTC(2026, 6, 10 + i, 6)),
    finishedAt: new Date(Date.UTC(2026, 6, 10 + i, 6, 5)),
    sitesOk: 1,
    sitesFailed: 0,
    rowsUpserted: 10,
    error: null,
  }));
  await db.insert(gscSnapshotRuns).values(runs);
  try {
    const health = await getGscHealthAction();
    // Sur 9 runs bulk-0..8 (startedAt croissants), on attend exactement les 7
    // PLUS RÉCENTS, triés décroissant : bulk-8 → bulk-2.
    expect(health.snapshotRuns.map((r) => r.id)).toEqual([
      "bulk-8",
      "bulk-7",
      "bulk-6",
      "bulk-5",
      "bulk-4",
      "bulk-3",
      "bulk-2",
    ]);
  } finally {
    await db.delete(gscSnapshotRuns).where(
      inArray(
        gscSnapshotRuns.id,
        runs.map((r) => r.id),
      ),
    );
  }
});

test("owner NON super-admin → snapshotRuns vide, syncBySite servi quand même", async () => {
  IS_SUPER_ADMIN = false;
  await db
    .insert(sitesTable)
    .values({
      id: "gsc-health-site-a",
      orgId: "gsc-health-org",
      name: "A",
      url: "https://a.example",
    });
  await db
    .insert(gscSnapshotRuns)
    .values({
      id: "r2",
      startedAt: new Date(),
      finishedAt: new Date(),
      sitesOk: 1,
      sitesFailed: 0,
      rowsUpserted: 5,
      error: null,
    });
  const health = await getGscHealthAction();
  expect(health.snapshotRuns).toEqual([]);
  expect(health.isSuperAdmin).toBe(false);
  // syncBySite reste servi même pour un non super-admin : le site est là, jamais synchronisé.
  const bySite = new Map(
    health.syncBySite.map((s) => [s.siteId, s.lastGscSyncAt]),
  );
  expect(bySite.get("gsc-health-site-a")).toBeNull();
});
