// src/lib/keywords/export-enrichment.ts — build tool-ready keyword lists from the
// Recherche keywords that still lack SEO metrics, chunked to each tool's bulk limit.
import { toGeoTarget } from "@/lib/geo/google-ads";
import type { EnrichmentTool, Keyword } from "./types";

// Défini dans types.ts (évite un cycle d'imports) ; ré-exporté ici pour les
// consommateurs existants de ce module.
export type { EnrichmentTool };

/** Max keywords per bulk import, per tool. */
export const TOOL_LIMITS: Record<EnrichmentTool, number> = {
  mangools: 700,
  semrush: 2000,
  ahrefs: 10000,
};

/** "No metric" = the primary source carries no volume — the same condition the
 *  table renders as N/A. These are the keywords worth re-enriching. */
export function hasNoMetric(k: Keyword): boolean {
  return k.metrics[k.primarySource]?.volume == null;
}

/** « En attente » pour un outil : déjà exporté vers lui ET ses métriques ne sont
 *  pas encore revenues. Le mode "missing" exclut ces mots-clés (ELEVA-49). */
export function isPendingForTool(k: Keyword, tool: EnrichmentTool): boolean {
  return k.enrichmentExports?.[tool] != null && k.metrics[tool] == null;
}

export type EnrichmentFile = {
  country: string;
  /** Keyword language code (the searcher's Google interface language). A single
   *  country can carry several languages (e.g. fr + ur in France — diaspora), so
   *  files are grouped per (country, language): that pair IS the external tool's
   *  targeting unit. */
  language: string;
  /** Google Ads location_id for `country` (undefined if the code is unknown). */
  locationId?: number;
  /** Google Ads language_id for `language` (undefined if the code is unknown). */
  languageId?: number;
  /** 1-based position within its (country, language) group */
  index: number;
  /** number of files in its (country, language) group */
  total: number;
  terms: string[];
  /** Ids des mots-clés dont les termes figurent dans ce fichier (dédup incluse :
   *  un terme écrit une fois peut porter plusieurs ids). Sert au marquage
   *  « exporté » au téléchargement. */
  ids: string[];
};

/** Quels mots-clés exporter : "missing" = sans métrique (enrichir, défaut) ;
 *  "all" = toute la sélection (rafraîchir les métriques existantes). */
export type EnrichmentMode = "missing" | "all";

/** Portée d'un export réservoir : la sélection cochée, ou un balayage de
 *  fraîcheur — par outil — sur le réservoir filtré. « stale-* » = data
 *  périmée pour l'outil OU jamais synchronisée (pas de data = infiniment
 *  vieille) ; « never-synced » = uniquement les sans-data. Toutes excluent
 *  les « en attente » (déjà exportés vers l'outil, métriques pas revenues)
 *  pour ne pas re-télécharger les mêmes listes en boucle. */
export type ExportScope =
  | "selection"
  | "stale-week"
  | "stale-month"
  | "never-synced";

export const EXPORT_SCOPES: ExportScope[] = [
  "selection",
  "stale-week",
  "stale-month",
  "never-synced",
];

const STALE_DAYS: Record<"stale-week" | "stale-month", number> = {
  "stale-week": 7,
  "stale-month": 30,
};

export function keywordsForScope(
  pool: Keyword[],
  selectedIds: ReadonlySet<string>,
  tool: EnrichmentTool,
  scope: ExportScope,
  now: number,
): Keyword[] {
  return pool.filter((k) => {
    if (isPendingForTool(k, tool)) return false;
    if (scope === "selection") return selectedIds.has(k.id);
    const m = k.metrics[tool];
    if (scope === "never-synced") return m == null;
    // stale-week / stale-month — les jamais-synchro en font partie : pas de
    // data = infiniment vieille.
    if (m == null) return true;
    const at = Date.parse(m.updatedAt);
    return Number.isNaN(at) || at < now - STALE_DAYS[scope] * 86_400_000;
  });
}

/** Keywords → grouped by (country, language) — the external tool's targeting
 *  unit (location_id × language_id) — deduped by lowercased term, each group
 *  sliced into files of at most TOOL_LIMITS[tool] terms. */
export function buildEnrichmentFiles(
  keywords: Keyword[],
  tool: EnrichmentTool,
  mode: EnrichmentMode = "missing",
): EnrichmentFile[] {
  const limit = TOOL_LIMITS[tool];
  // (country, language) -> { country, lang, lowerTerm -> { term, ids } }.
  // Grouping by the (country, language) pair — not country alone — because that
  // pair is the external tool's targeting unit (location_id × language_id): a
  // country can host several search languages (diaspora), each a distinct file.
  const byLocale = new Map<
    string,
    {
      country: string;
      lang: string;
      terms: Map<string, { term: string; ids: string[] }>;
    }
  >();
  for (const k of keywords) {
    if (mode === "missing" && (!hasNoMetric(k) || isPendingForTool(k, tool)))
      continue;
    const term = k.term.trim();
    if (!term) continue;
    const country = k.locale.country;
    const lang = k.locale.lang;
    const groupKey = `${country} ${lang}`;
    let group = byLocale.get(groupKey);
    if (!group) {
      group = { country, lang, terms: new Map() };
      byLocale.set(groupKey, group);
    }
    const key = term.toLowerCase();
    const entry = group.terms.get(key);
    if (entry) entry.ids.push(k.id);
    else group.terms.set(key, { term, ids: [k.id] });
  }

  const files: EnrichmentFile[] = [];
  for (const { country, lang, terms } of byLocale.values()) {
    const list = [...terms.values()];
    const total = Math.ceil(list.length / limit);
    // Resolve the Mangools/KWFinder numeric targeting IDs once per group.
    const { location_id, language_id } = toGeoTarget(lang, country);
    for (let i = 0; i < total; i++) {
      const slice = list.slice(i * limit, (i + 1) * limit);
      files.push({
        country,
        language: lang,
        locationId: location_id,
        languageId: language_id,
        index: i + 1,
        total,
        terms: slice.map((e) => e.term),
        ids: slice.flatMap((e) => e.ids),
      });
    }
  }
  return files;
}
