import { expect, test } from "bun:test";
import {
  buildEnrichmentFiles,
  hasNoMetric,
  isPendingForTool,
  keywordsForScope,
  TOOL_LIMITS,
} from "./export-enrichment";
import type { Keyword } from "./types";

// Minimal factory — only the fields the util reads matter; cast for the rest.
function kw(
  term: string,
  opts: {
    country?: string;
    lang?: string;
    primarySource?: Keyword["primarySource"];
    metrics?: Keyword["metrics"];
    enrichmentExports?: Keyword["enrichmentExports"];
  } = {},
): Keyword {
  return {
    id: `id-${term}`,
    term,
    locale: { lang: opts.lang ?? "fr", country: opts.country ?? "FR" },
    primarySource: opts.primarySource ?? "gsc",
    metrics: opts.metrics ?? {},
    enrichmentExports: opts.enrichmentExports ?? {},
  } as unknown as Keyword;
}

test("hasNoMetric: true when the primary source has no volume, false otherwise", () => {
  expect(hasNoMetric(kw("a"))).toBe(true);
  expect(
    hasNoMetric(
      kw("b", {
        primarySource: "semrush",
        metrics: { semrush: { volume: 100 } as never },
      }),
    ),
  ).toBe(false);
  expect(
    hasNoMetric(
      kw("c", {
        primarySource: "semrush",
        metrics: { semrush: { volume: 0 } as never },
      }),
    ),
  ).toBe(false);
});

test("buildEnrichmentFiles mode 'all': exporte toute la sélection (rafraîchir), dédup conservée", () => {
  const files = buildEnrichmentFiles(
    [
      kw("Audit"),
      kw("audit"),
      kw("has data", {
        primarySource: "semrush",
        metrics: { semrush: { volume: 50 } as never },
      }),
    ],
    "mangools",
    "all",
  );
  expect(files).toHaveLength(1);
  expect(files[0].terms).toEqual(["Audit", "has data"]);
});

test("buildEnrichmentFiles: keeps only missing-metric keywords, dedups by lowercased term", () => {
  const files = buildEnrichmentFiles(
    [
      kw("Audit"),
      kw("audit"),
      kw("has data", {
        primarySource: "semrush",
        metrics: { semrush: { volume: 50 } as never },
      }),
      kw("b2b energie"),
    ],
    "mangools",
  );
  expect(files).toHaveLength(1);
  expect(files[0].country).toBe("FR");
  expect(files[0].terms).toEqual(["Audit", "b2b energie"]);
  expect(files[0].index).toBe(1);
  expect(files[0].total).toBe(1);
});

test("buildEnrichmentFiles: porte les ids des mots-clés écrits (dédup → plusieurs ids par terme)", () => {
  const files = buildEnrichmentFiles([kw("Audit"), kw("audit")], "mangools");
  expect(files[0].terms).toEqual(["Audit"]);
  expect(files[0].ids).toEqual(["id-Audit", "id-audit"]);
});

test("buildEnrichmentFiles: chunks each country at the tool limit", () => {
  const many = Array.from({ length: TOOL_LIMITS.mangools + 1 }, (_, i) =>
    kw(`t${i}`),
  );
  const files = buildEnrichmentFiles(many, "mangools");
  expect(files).toHaveLength(2);
  expect(files[0].terms).toHaveLength(700);
  expect(files[0]).toMatchObject({ index: 1, total: 2 });
  expect(files[1].terms).toHaveLength(1);
  expect(files[1]).toMatchObject({ index: 2, total: 2 });
});

test("mode 'missing': exclut les mots-clés en attente de retour (déjà exportés vers l'outil)", () => {
  const files = buildEnrichmentFiles(
    [
      kw("pending", { enrichmentExports: { mangools: "2026-06-01" } }),
      kw("fresh"),
    ],
    "mangools",
  );
  expect(files).toHaveLength(1);
  expect(files[0].terms).toEqual(["fresh"]);
});

test("mode 'all': inclut aussi les mots-clés en attente (rafraîchir ignore le suivi)", () => {
  const files = buildEnrichmentFiles(
    [
      kw("pending", { enrichmentExports: { mangools: "2026-06-01" } }),
      kw("fresh"),
    ],
    "mangools",
    "all",
  );
  expect(files).toHaveLength(1);
  expect(files[0].terms).toEqual(["pending", "fresh"]);
});

test("le suivi est par outil : exporté vers mangools ≠ en attente pour ahrefs", () => {
  const k = kw("only-mangools", {
    enrichmentExports: { mangools: "2026-06-01" },
  });
  expect(isPendingForTool(k, "mangools")).toBe(true);
  expect(isPendingForTool(k, "ahrefs")).toBe(false);
  const files = buildEnrichmentFiles([k], "ahrefs");
  expect(files).toHaveLength(1);
  expect(files[0].terms).toEqual(["only-mangools"]);
});

test("isPendingForTool: les métriques revenues lèvent l'attente", () => {
  const k = kw("returned", {
    enrichmentExports: { mangools: "2026-06-01" },
    metrics: { mangools: { volume: 10 } as never },
  });
  expect(isPendingForTool(k, "mangools")).toBe(false);
});

test("buildEnrichmentFiles: attaches Google Ads location_id/language_id per file", () => {
  const files = buildEnrichmentFiles([kw("audit")], "mangools");
  expect(files[0]).toMatchObject({
    country: "FR",
    language: "fr",
    locationId: 2250, // Google Ads geo target for FR (2000 + ISO 250)
    languageId: 1002, // Google Ads language criterion for French
  });
});

test("buildEnrichmentFiles: splits one country into per-language files (diaspora)", () => {
  // Same physical location (France), two search-interface languages → two
  // targeting units, each with its own language_id (location_id shared).
  const files = buildEnrichmentFiles(
    [
      kw("assurance", { country: "FR", lang: "fr" }),
      kw("bima", { country: "FR", lang: "ur" }), // Urdu interface in France
    ],
    "mangools",
  );
  expect(files).toHaveLength(2);
  const fr = files.find((f) => f.language === "fr");
  const ur = files.find((f) => f.language === "ur");
  expect(fr).toMatchObject({
    country: "FR",
    locationId: 2250,
    languageId: 1002,
  });
  expect(ur).toMatchObject({
    country: "FR",
    locationId: 2250,
    languageId: 1041,
  });
  expect(fr?.terms).toEqual(["assurance"]);
  expect(ur?.terms).toEqual(["bima"]);
});

test("buildEnrichmentFiles: groups by country", () => {
  const files = buildEnrichmentFiles(
    [
      kw("fr1", { country: "FR" }),
      kw("us1", { country: "US" }),
      kw("fr2", { country: "FR" }),
    ],
    "ahrefs",
  );
  const fr = files.find((f) => f.country === "FR");
  const us = files.find((f) => f.country === "US");
  expect(fr?.terms).toEqual(["fr1", "fr2"]);
  expect(us?.terms).toEqual(["us1"]);
});

// --- keywordsForScope (portées de l'export réservoir) ------------------------

const NOW = Date.parse("2026-07-16T12:00:00Z");
const daysAgo = (n: number) => new Date(NOW - n * 86_400_000).toISOString();
const mangoolsAt = (iso: string): Keyword["metrics"] => ({
  mangools: { volume: 10, updatedAt: iso } as never,
});

test("keywordsForScope: selection = ids cochés uniquement, pending exclus", () => {
  const pool = [
    kw("a"),
    kw("b"),
    kw("pending", { enrichmentExports: { mangools: daysAgo(2) } }),
  ];
  const rows = keywordsForScope(
    pool,
    new Set(["id-a", "id-pending"]),
    "mangools",
    "selection",
    NOW,
  );
  expect(rows.map((k) => k.term)).toEqual(["a"]);
});

test("keywordsForScope: never-synced = sans data pour l'outil, étanche aux périmés", () => {
  const pool = [
    kw("jamais"),
    kw("frais", { metrics: mangoolsAt(daysAgo(1)) }),
    kw("perime", { metrics: mangoolsAt(daysAgo(40)) }),
    // synchronisé par Semrush mais jamais par Mangools → dans la portée
    kw("autre-outil", {
      metrics: { semrush: { volume: 5, updatedAt: daysAgo(1) } as never },
    }),
  ];
  const rows = keywordsForScope(
    pool,
    new Set(),
    "mangools",
    "never-synced",
    NOW,
  );
  expect(rows.map((k) => k.term).sort()).toEqual(["autre-outil", "jamais"]);
});

test("keywordsForScope: stale-week/stale-month par fraîcheur de l'outil, jamais-synchro inclus (pas de data = infiniment vieille)", () => {
  const pool = [
    kw("jamais"),
    kw("j3", { metrics: mangoolsAt(daysAgo(3)) }),
    kw("j10", { metrics: mangoolsAt(daysAgo(10)) }),
    kw("j40", { metrics: mangoolsAt(daysAgo(40)) }),
  ];
  const week = keywordsForScope(pool, new Set(), "mangools", "stale-week", NOW);
  expect(week.map((k) => k.term).sort()).toEqual(["j10", "j40", "jamais"]);
  const month = keywordsForScope(
    pool,
    new Set(),
    "mangools",
    "stale-month",
    NOW,
  );
  expect(month.map((k) => k.term).sort()).toEqual(["j40", "jamais"]);
});

test("keywordsForScope: pending pour l'outil exclu de toutes les portées", () => {
  const pool = [
    kw("pending-jamais", { enrichmentExports: { mangools: daysAgo(1) } }),
    kw("pending-autre-outil", {
      enrichmentExports: { semrush: daysAgo(1) },
    }),
  ];
  const rows = keywordsForScope(
    pool,
    new Set(),
    "mangools",
    "never-synced",
    NOW,
  );
  // l'export Semrush en attente ne bloque pas un export Mangools
  expect(rows.map((k) => k.term)).toEqual(["pending-autre-outil"]);
});

test("keywordsForScope: updatedAt invalide traité comme périmé", () => {
  const pool = [kw("corrompu", { metrics: mangoolsAt("n/a") })];
  const rows = keywordsForScope(pool, new Set(), "mangools", "stale-week", NOW);
  expect(rows.map((k) => k.term)).toEqual(["corrompu"]);
});
