import { QueryClient } from '@tanstack/react-query';
import { enfiler, lireFile, retirer, _viderPourTests, type Saisie } from './store';
import { rejouer, type ResultatEnvoi } from './synchro';

/** Le contrat D1/D2 de la file, testé sans réseau : rejeu DANS L'ORDRE,
 *  arrêt sur conflit (l'humain tranche), reprise après coupure. */

const qc = new QueryClient();

const saisie = (libelle: string): Saisie =>
  enfiler({
    type: 'TRANSITION',
    otId: 'ot-1',
    otReference: 'OT-TEST',
    libelle,
    baseUpdatedAt: '2026-07-17T10:00:00.000Z',
    payload: { to: 'IN_PROGRESS' },
  }) && lireFile().at(-1)!;

beforeEach(() => _viderPourTests());

describe('rejouer (file D1/D2)', () => {
  it('rejoue dans l’ordre et vide la file quand tout passe', async () => {
    saisie('a');
    saisie('b');
    const envoyes: string[] = [];
    await rejouer(qc, async (s) => {
      envoyes.push(s.libelle);
      return { ok: true };
    });
    expect(envoyes).toEqual(['a', 'b']);
    expect(lireFile()).toHaveLength(0);
  });

  it('conflit : la saisie passe en CONFLIT et la file S’ARRÊTE là — rien n’est perdu', async () => {
    saisie('a');
    saisie('b');
    saisie('c');
    const envoyer = async (s: Saisie): Promise<ResultatEnvoi> =>
      s.libelle === 'b'
        ? { ok: false, reseau: false, message: 'Conflit de version : modifié par Salma à 14 h 38' }
        : { ok: true };
    await rejouer(qc, envoyer);
    const file = lireFile();
    expect(file.map((s) => [s.libelle, s.statut])).toEqual([
      ['b', 'CONFLIT'],
      ['c', 'EN_ATTENTE'], // derrière le conflit : on attend l'humain
    ]);
    expect(file[0]!.erreur).toContain('Salma');
  });

  it('coupure réseau : tout reste EN_ATTENTE, rejouable au retour', async () => {
    saisie('a');
    saisie('b');
    await rejouer(qc, async () => ({ ok: false, reseau: true, message: 'Réseau indisponible' }));
    expect(lireFile().map((s) => s.statut)).toEqual(['EN_ATTENTE', 'EN_ATTENTE']);
    // le réseau revient
    await rejouer(qc, async () => ({ ok: true }));
    expect(lireFile()).toHaveLength(0);
  });

  it('après « abandonner » le conflit, le reste de la file repart', async () => {
    saisie('a');
    saisie('b');
    await rejouer(qc, async (s) =>
      s.libelle === 'a' ? { ok: false, reseau: false, message: 'refus métier' } : { ok: true },
    );
    expect(lireFile()).toHaveLength(2);
    retirer(lireFile()[0]!.id); // l'humain abandonne la saisie en conflit
    await rejouer(qc, async () => ({ ok: true }));
    expect(lireFile()).toHaveLength(0);
  });
});

describe('propagation de version (nos écritures ne se conflictent pas entre elles)', () => {
  it('au succès, la version fraîche se propage aux saisies restantes du même OT', async () => {
    saisie('a');
    saisie('b');
    await rejouer(qc, async (s) =>
      s.libelle === 'a' ? { ok: true, nouvelleVersion: '2026-07-17T11:00:00.000Z' } : { ok: true },
    );
    // b a été envoyée après propagation — vérifions via un rejeu espion
    _viderPourTests();
    saisie('c');
    saisie('d');
    const basesVues: string[] = [];
    await rejouer(qc, async (s) => {
      basesVues.push(s.baseUpdatedAt);
      return { ok: true, nouvelleVersion: 'V-FRAICHE' };
    });
    expect(basesVues).toEqual(['2026-07-17T10:00:00.000Z', 'V-FRAICHE']);
  });
});
