import { describe, expect, it } from 'vitest';
import { diffWords, MAX_DIFF_UNITS, type DiffSegment } from './diff';

/** Le diff doit toujours pouvoir reconstruire les deux textes — sinon l'aperçu ment. */
function reconstruit(segments: DiffSegment[]): { avant: string; apres: string } {
    return {
        avant: segments
            .filter((segment) => segment.type !== 'add')
            .map((segment) => segment.value)
            .join(''),
        apres: segments
            .filter((segment) => segment.type !== 'del')
            .map((segment) => segment.value)
            .join(''),
    };
}

describe('diffWords', () => {
    it('rend un seul segment « same » quand rien ne change', () => {
        expect(diffWords('Bonjour à vous', 'Bonjour à vous')).toEqual([{ type: 'same', value: 'Bonjour à vous' }]);
    });

    it('rend une liste vide sur deux textes vides', () => {
        expect(diffWords('', '')).toEqual([]);
    });

    it('isole le mot remplacé', () => {
        expect(diffWords('la clé est la', 'la clé est là')).toEqual([
            { type: 'same', value: 'la clé est ' },
            { type: 'del', value: 'la' },
            { type: 'add', value: 'là' },
        ]);
    });

    it('repère un mot ajouté', () => {
        expect(diffWords('Bonjour vous', 'Bonjour à vous')).toEqual([
            { type: 'same', value: 'Bonjour ' },
            { type: 'add', value: 'à ' },
            { type: 'same', value: 'vous' },
        ]);
    });

    it('repère un mot supprimé', () => {
        expect(diffWords('le le chat', 'le chat')).toEqual([
            { type: 'same', value: 'le ' },
            { type: 'del', value: 'le ' },
            { type: 'same', value: 'chat' },
        ]);
    });

    it('recolle en un seul bloc les unités supprimées qui se suivent', () => {
        expect(diffWords('un deux trois', 'un')).toEqual([
            { type: 'same', value: 'un' },
            { type: 'del', value: ' deux trois' },
        ]);
    });

    it('rend une paire del/add par mot changé quand les mots communs les séparent', () => {
        const segments = diffWords('un deux trois', 'un sept huit');

        expect(segments.map((segment) => segment.value)).toEqual(['un ', 'deux', 'sept', ' ', 'trois', 'huit']);
        expect(reconstruit(segments)).toEqual({ avant: 'un deux trois', apres: 'un sept huit' });
    });

    it('reconstruit exactement les deux textes, espaces compris', () => {
        const avant = 'bonjour,comment  ça va ? tres bien';
        const apres = 'Bonjour, comment ça va ? Très bien';

        expect(reconstruit(diffWords(avant, apres))).toEqual({ avant, apres });
    });

    it('montre bien une correction d’espaces multiples', () => {
        const segments = diffWords('Merci   beaucoup', 'Merci beaucoup');

        expect(reconstruit(segments)).toEqual({ avant: 'Merci   beaucoup', apres: 'Merci beaucoup' });
        expect(segments.some((segment) => segment.type === 'del')).toBe(true);
    });

    it('gère un texte devenu vide', () => {
        expect(diffWords('Bonjour', '')).toEqual([{ type: 'del', value: 'Bonjour' }]);
    });

    it('gère un texte parti de rien', () => {
        expect(diffWords('', 'Bonjour')).toEqual([{ type: 'add', value: 'Bonjour' }]);
    });

    it('se rabat sur un « tout remplacé » au-delà de la limite, sans exploser la mémoire', () => {
        const avant = Array.from({ length: MAX_DIFF_UNITS + 10 }, (_, index) => `mot${index}`).join(' ');
        const apres = `${avant} fin`;
        const segments = diffWords(avant, apres);

        expect(segments.map((segment) => segment.type)).toEqual(['del', 'add']);
        expect(reconstruit(segments)).toEqual({ avant, apres });
    });
});
