// src/lib/mangools/locations.test.ts — résolution pays (ISO2) → location_id
// Mangools avec cache DB (postgres, section gated DB_TESTS=1, même pattern que
// connectors/store.test.ts). `resolveLocationId` prend un `deps.search` injecté :
// les tests le MOCKENT intégralement (zéro réseau) et comptent ses appels pour
// prouver le cache. On vérifie : hit DB seedé (FR=2250) sans appel search ;
// miss → search → persistance (2ᵉ appel = plus de search) ; introuvable → null
// SANS rien persister ; code inconnu de countryLabel → null sans throw.
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { eq, inArray } from "drizzle-orm";
import { assertTestDb } from "@/lib/db/assert-test-db";
import { db } from "@/lib/db/client";
import { mangoolsLocations } from "@/lib/db/domain-schema";
import { resolveLocationId } from "./locations";
import type { MangoolsLocation } from "./types";

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

/** Codes de test (jamais FR, qui est seedé par la migration et partagé). */
const TEST_CODES = ["DE", "ES", "ZZ"];

/** Fabrique un `deps.search` mocké qui enregistre ses appels (zéro réseau). */
function mockSearch(results: MangoolsLocation[]) {
  const calls: string[] = [];
  return {
    calls,
    deps: {
      search: async (q: string) => {
        calls.push(q);
        return results;
      },
    },
  };
}

describe.skipIf(!RUN)("mangools locations resolver (postgres)", () => {
  async function cleanupTestCodes() {
    // Ne touche JAMAIS à FR (seed migration) — uniquement les codes de test.
    await db
      .delete(mangoolsLocations)
      .where(inArray(mangoolsLocations.countryCode, TEST_CODES));
  }

  async function readRow(code: string) {
    const [row] = await db
      .select()
      .from(mangoolsLocations)
      .where(eq(mangoolsLocations.countryCode, code));
    return row ?? null;
  }

  beforeAll(async () => {
    await assertTestDb();
    await cleanupTestCodes();
  });

  afterAll(async () => {
    await cleanupTestCodes();
  });

  test("hit DB : FR seedé (2250) résolu SANS appel search", async () => {
    const { calls, deps } = mockSearch([]);
    // Accepte aussi l'entrée minuscule : `fr` doit être normalisé en `FR`.
    expect(await resolveLocationId("fr", deps)).toBe(2250);
    expect(await resolveLocationId("FR", deps)).toBe(2250);
    // Le cache DB court-circuite entièrement le réseau.
    expect(calls).toHaveLength(0);
  });

  test("miss → search → hit Country persisté (2ᵉ appel = plus de search)", async () => {
    await cleanupTestCodes();
    // La réponse mélange la bonne ligne Country avec du bruit (régions/villes)
    // qu'il faut écarter via target_type. country_code en minuscule côté API →
    // la comparaison doit normaliser en majuscule.
    const { calls, deps } = mockSearch([
      {
        _id: 111,
        label: "Bavaria, Germany",
        country_code: "DE",
        target_type: "Region",
      },
      {
        _id: 3220,
        label: "Germany",
        country_code: "de",
        target_type: "Country",
      },
      {
        _id: 222,
        label: "Berlin, Germany",
        country_code: "DE",
        target_type: "City",
      },
    ]);

    expect(await resolveLocationId("DE", deps)).toBe(3220);
    // La recherche est faite par NOM de pays (countryLabel), pas par code.
    expect(calls).toEqual(["Germany"]);

    // Persistance : la ligne DE existe avec l'_id du hit et son label.
    const row = await readRow("DE");
    expect(row?.locationId).toBe(3220);
    expect(row?.label).toBe("Germany");

    // 2ᵉ résolution : servie par le cache, AUCUN nouvel appel search.
    expect(await resolveLocationId("DE", deps)).toBe(3220);
    expect(calls).toHaveLength(1);
  });

  test("introuvable (aucun hit Country) → null ET rien persisté", async () => {
    await cleanupTestCodes();
    // Que du bruit : régions/villes, plus une Country du MAUVAIS pays.
    const { calls, deps } = mockSearch([
      {
        _id: 900,
        label: "Some Region, Spain",
        country_code: "ES",
        target_type: "Region",
      },
      { _id: 901, label: "France", country_code: "FR", target_type: "Country" },
    ]);

    expect(await resolveLocationId("ES", deps)).toBeNull();
    expect(calls).toEqual(["Spain"]); // la recherche a bien eu lieu (miss DB)
    // Aucune ligne persistée pour ES (jamais de fallback silencieux).
    expect(await readRow("ES")).toBeNull();
  });

  test("code inconnu de countryLabel → null SANS throw ni appel search", async () => {
    await cleanupTestCodes();
    const { calls, deps } = mockSearch([
      { _id: 999, label: "Zzz", country_code: "ZZ", target_type: "Country" },
    ]);
    // "ZZ" n'est pas un pays Google Ads → countryLabel renvoie le code → on
    // s'arrête avant tout appel réseau.
    expect(await resolveLocationId("ZZ", deps)).toBeNull();
    expect(calls).toHaveLength(0);
    expect(await readRow("ZZ")).toBeNull();
  });
});
