/**
 * Faux `pg.Pool` pour les tests de routes.
 *
 * Objectif : tester le routage, l'authentification, la validation d'entrée et
 * la *forme* du SQL généré (allowlists de colonnes, casts, transactions) — sans
 * démarrer de conteneur Postgres. Un test qui monte un conteneur met des
 * dizaines de secondes ; celui-ci tourne en millisecondes.
 *
 * `handlers` associe un fragment de SQL (sous-chaîne ou RegExp) à un résultat.
 * Toutes les requêtes exécutées sont enregistrées dans `pool.calls`.
 *
 * Le faux client rend les transactions observables : `pool.committed` et
 * `pool.rolledBack` permettent d'affirmer qu'une route a bien annulé son travail
 * sur erreur, et `pool.released` qu'elle n'a pas fui de connexion.
 */
/**
 * @typedef {object} Handler
 * @property {string|RegExp} match         fragment de SQL à reconnaître
 * @property {any}           [result]      résultat pg, ou fonction (sql, values) => résultat
 * @property {Error|string}  [throws]      fait échouer la requête (chemins d'erreur)
 *
 * @param {Handler[]} [handlers]
 */
export function makeFakePool(handlers = []) {
  const calls = [];
  const state = { committed: false, rolledBack: false, released: 0, connections: 0 };
  let active = handlers;

  async function query(text, values) {
    const sql = typeof text === "string" ? text : text.text;
    calls.push({ sql, values: values ?? [] });

    if (sql === "COMMIT") state.committed = true;
    if (sql === "ROLLBACK") state.rolledBack = true;

    for (const { match, result, throws } of active) {
      const hit = match instanceof RegExp ? match.test(sql) : sql.includes(match);
      if (hit) {
        if (throws) throw throws instanceof Error ? throws : new Error(String(throws));
        return typeof result === "function" ? result(sql, values) : result;
      }
    }
    return { rows: [], rowCount: 0 };
  }

  const pool = {
    calls,
    query,

    /**
     * Reconfigure le pool sans en recréer un.
     *
     * Permet de construire l'app Fastify UNE fois par fichier de test (14 ms
     * par construction, multipliées par ~12 000 exécutions pendant un run de
     * mutation) au lieu d'une fois par cas de test.
     */
    reset(newHandlers = []) {
      active = newHandlers;
      calls.length = 0;
      state.committed = false;
      state.rolledBack = false;
      state.released = 0;
      state.connections = 0;
      return pool;
    },

    async connect() {
      state.connections++;
      return {
        query,
        release() {
          state.released++;
        },
      };
    },

    get committed() { return state.committed; },
    get rolledBack() { return state.rolledBack; },
    get released() { return state.released; },
    get connections() { return state.connections; },

    /** Retrouve la première requête exécutée contenant `fragment`. */
    find(fragment) {
      return calls.find((c) =>
        fragment instanceof RegExp ? fragment.test(c.sql) : c.sql.includes(fragment)
      );
    },

    /** Toutes les requêtes contenant `fragment`. */
    findAll(fragment) {
      return calls.filter((c) =>
        fragment instanceof RegExp ? fragment.test(c.sql) : c.sql.includes(fragment)
      );
    },

    /** Ordre d'exécution des requêtes correspondant à l'un des fragments. */
    sequence(fragments) {
      return calls
        .map((c) => fragments.find((f) => (f instanceof RegExp ? f.test(c.sql) : c.sql.includes(f))))
        .filter(Boolean);
    },
  };

  return pool;
}

/** Raccourci : un résultat pg minimal. */
export function rows(...items) {
  return { rows: items, rowCount: items.length };
}
