import { describe, expect, it } from "vitest";
import { listCountries, listLaunchPlan, listsCensus } from "@/lib/api-import-lists";
import type { CensusCountry, CountryChoice } from "@/lib/import-countries";
import type { ListPreview } from "@/lib/integrations";
import type { Target } from "@/lib/sites";

// La machine de décision du wizard « listes KWFinder ». Ce qu'elle défend, et que rien d'autre
// ne défendrait : **une liste n'est envoyée que sur les marchés qu'elle contient vraiment**.
// Croiser bêtement N listes × M cibles fabriquerait des imports à zéro ligne, avec un rapport
// vide et rien pour dire pourquoi.

function country(
  code: string,
  label: string,
  rows: number,
  suggested: string | null = null,
): {
  raw: string;
  code: string | null;
  label: string;
  rows: number;
  suggested_language: string | null;
  target_ids: string[];
} {
  return { raw: label, code, label, rows, suggested_language: suggested, target_ids: [] };
}

function preview(id: string, name: string, countries: ReturnType<typeof country>[]): ListPreview {
  return {
    list_id: id,
    name,
    keywords_total: countries.reduce((sum, entry) => sum + entry.rows, 0),
    countries,
  };
}

const FR: Target = {
  id: "target-fr",
  country: "FR",
  language: "fr",
  is_default: true,
} as unknown as Target;

const US: Target = {
  id: "target-us",
  country: "US",
  language: "en",
  is_default: false,
} as unknown as Target;

const pick =
  (selected: Record<string, string>) =>
  (entry: CensusCountry): CountryChoice => ({
    selected: entry.code in selected,
    language: selected[entry.code] ?? entry.suggestedLanguage ?? "",
  });

describe("listsCensus", () => {
  it("fusionne les marchés de toutes les listes et sépare les libellés non résolus", () => {
    const { countries, unresolved } = listsCensus([
      preview("a", "Liste A", [country("FR", "France", 3), country(null as never, "New York", 1)]),
      preview("b", "Liste B", [country("FR", "France", 2), country("US", "United States", 5)]),
    ]);
    expect(countries.map((entry) => [entry.code, entry.rows])).toEqual([
      ["FR", 5],
      ["US", 5],
    ]);
    expect(unresolved.map((entry) => entry.label)).toEqual(["New York"]);
  });
});

describe("listCountries", () => {
  it("ne retient que les codes résolus, en majuscules", () => {
    const codes = listCountries(
      preview("a", "A", [country("fr", "France", 1), country(null as never, "New York", 1)]),
    );
    expect([...codes]).toEqual(["FR"]);
  });
});

describe("listLaunchPlan", () => {
  it("n'envoie une liste que sur les marchés qu'elle contient", () => {
    const previews = [
      preview("fr", "Liste France", [country("FR", "France", 91)]),
      preview("us", "Liste USA", [country("US", "United States", 21)]),
      preview("mix", "Liste mixte", [
        country("FR", "France", 4),
        country("US", "United States", 6),
      ]),
    ];
    const { countries } = listsCensus(previews);
    const plan = listLaunchPlan({
      targets: [FR, US],
      previews,
      countries,
      choice: pick({ FR: "fr", US: "en" }),
    });
    expect(plan.blockers).toEqual([]);
    expect(plan.imports.map((entry) => [entry.country, entry.listIds])).toEqual([
      ["FR", ["fr", "mix"]],
      ["US", ["us", "mix"]],
    ]);
    //  Les cibles existent déjà : rien à créer.
    expect(plan.creations).toBe(0);
    expect(plan.droppedLists).toEqual([]);
  });

  it("nomme les listes cochées qu'aucun marché retenu ne porte", () => {
    const previews = [
      preview("fr", "Liste France", [country("FR", "France", 91)]),
      preview("us", "Liste USA", [country("US", "United States", 21)]),
    ];
    const { countries } = listsCensus(previews);
    const plan = listLaunchPlan({
      targets: [FR, US],
      previews,
      countries,
      choice: pick({ FR: "fr" }),
    });
    //  Taire « Liste USA » ferait croire qu'elle part : elle a été cochée, elle doit être dite.
    expect(plan.droppedLists.map((entry) => entry.name)).toEqual(["Liste USA"]);
    expect(plan.imports).toHaveLength(1);
  });

  it("compte une cible à créer quand le couple (pays, langue) n'existe pas", () => {
    const previews = [preview("us", "Liste USA", [country("US", "United States", 21, "en")])];
    const { countries } = listsCensus(previews);
    const plan = listLaunchPlan({
      targets: [FR],
      previews,
      countries,
      choice: pick({ US: "en" }),
    });
    expect(plan.creations).toBe(1);
    expect(plan.imports[0]?.targetId).toBeNull();
    expect(plan.imports[0]?.language).toBe("en");
  });

  it("bloque quand une langue manque, en nommant le marché", () => {
    const previews = [preview("ph", "Liste PH", [country("PH", "Philippines", 12)])];
    const { countries } = listsCensus(previews);
    const plan = listLaunchPlan({
      targets: [FR],
      previews,
      countries,
      choice: () => ({ selected: true, language: "" }),
    });
    expect(plan.blockers).toEqual(["Choisissez une langue pour Philippines."]);
    expect(plan.imports).toEqual([]);
  });

  it("bloque quand rien n'est coché, et quand rien n'est sélectionné", () => {
    const previews = [preview("fr", "Liste France", [country("FR", "France", 3)])];
    const { countries } = listsCensus(previews);
    expect(
      listLaunchPlan({ targets: [FR], previews, countries, choice: pick({}) }).blockers,
    ).toEqual(["Cochez au moins un marché à importer."]);
    expect(
      listLaunchPlan({ targets: [FR], previews: [], countries: [], choice: pick({}) }).blockers,
    ).toEqual(["Choisissez au moins une liste."]);
  });

  it("bloque avant la boucle quand le plafond de cibles du site serait dépassé", () => {
    const previews = [
      preview("a", "A", [country("US", "United States", 1, "en")]),
      preview("b", "B", [country("DE", "Allemagne", 1, "de")]),
    ];
    const { countries } = listsCensus(previews);
    const saturated = Array.from({ length: 19 }, (_, index) => ({
      ...FR,
      id: `t-${index}`,
      country: "FR",
      language: `l${index}`,
    })) as Target[];
    const plan = listLaunchPlan({
      targets: saturated,
      previews,
      countries,
      choice: pick({ US: "en", DE: "de" }),
    });
    //  19 existantes + 2 à créer > 20 : dit **avant** d'en créer une seule.
    expect(plan.blockers.join(" ")).toContain("plafond de 20 cibles");
  });

  it("fond deux libellés du même couple en un seul import, sans répéter les listes", () => {
    const previews = [
      preview("a", "Liste A", [country("US", "United States", 4), country("US", "USA", 2)]),
    ];
    const { countries } = listsCensus(previews);
    const plan = listLaunchPlan({
      targets: [US],
      previews,
      countries,
      choice: pick({ US: "en" }),
    });
    expect(plan.imports).toHaveLength(1);
    expect(plan.imports[0]?.listIds).toEqual(["a"]);
    //  Le recensement a déjà additionné les deux libellés sous le code.
    expect(plan.imports[0]?.rows).toBe(6);
  });
});
