import { describe, expect, it } from 'vitest';
import { lireOngletsMemorises, memoriserOnglets, selectionInitiale, type StockageCle } from './memoire-onglets';
import {
    basculerFiltre,
    clesAConfirmer,
    clesALier,
    clesSelectionnees,
    compteursParFiltre,
    estDateValide,
    estSelectionnable,
    filtreDuStatut,
    filtresParDefaut,
    lignesFiltrees,
    retenueDiffere,
} from './preview-filtres';
import type { LigneApercu, OngletApercu, StatutLigne } from './types';

function ligne(cle: string, statut: StatutLigne, extra: Partial<LigneApercu> = {}): LigneApercu {
    return {
        cle,
        numero: 1,
        nom_fichier: cle,
        logement: null,
        identifiant: '01234567',
        type_identifiant: 'cin',
        montant_brut: 1000,
        retenue_fichier: null,
        retenue_calculee: 100,
        date_paiement: '2026-01-31',
        statut,
        motifs: [],
        beneficiaire: null,
        ligne_manager: null,
        ...extra,
    };
}

function onglet(gid: number, lignes: LigneApercu[]): OngletApercu {
    return { gid, titre: `Onglet ${gid}`, date_onglet: '2026-01-31', date_source: 'entete', mois: '2026-01', lignes };
}

const ONGLETS: OngletApercu[] = [
    onglet(1, [
        ligne('a', 'a_importer'),
        ligne('b', 'avertissement'),
        ligne('c', 'bloquee'),
        ligne('d', 'deja_faite', { ligne_manager: { id: 9, reference: 'R9' } }),
        ligne('e', 'ignoree'),
    ]),
    onglet(2, [ligne('f', 'a_importer'), ligne('g', 'deja_faite')]),
];

describe('pastilles de l’aperçu', () => {
    it('range « avertissement » sous « À importer », et chaque autre statut sous le sien', () => {
        expect(filtreDuStatut('avertissement')).toBe('a_importer');
        expect(filtreDuStatut('a_importer')).toBe('a_importer');
        expect(filtreDuStatut('bloquee')).toBe('bloquee');
        expect(filtreDuStatut('deja_faite')).toBe('deja_faite');
        expect(filtreDuStatut('ignoree')).toBe('ignoree');
    });

    it('additionne à importer et avertissements dans le compteur « À importer »', () => {
        expect(compteursParFiltre({ a_importer: 4, avertissement: 2, bloquee: 1, deja_faite: 3, ignoree: 40 })).toEqual({
            a_importer: 6,
            bloquee: 1,
            deja_faite: 3,
            ignoree: 40,
        });
    });

    it('masque les ignorées par défaut', () => {
        const defaut = filtresParDefaut();
        expect(defaut.has('ignoree')).toBe(false);
        expect([...lignesFiltrees(ONGLETS[0].lignes, defaut)].map((l) => l.cle)).toEqual(['a', 'b', 'c', 'd']);
    });

    it('bascule une pastille sans muter l’ensemble d’origine', () => {
        const defaut = filtresParDefaut();
        const avecIgnorees = basculerFiltre(defaut, 'ignoree');
        expect(defaut.has('ignoree')).toBe(false);
        expect(avecIgnorees.has('ignoree')).toBe(true);
        expect(basculerFiltre(avecIgnorees, 'ignoree').has('ignoree')).toBe(false);
    });

    it('ne montre que les statuts des pastilles actives', () => {
        expect(lignesFiltrees(ONGLETS[0].lignes, new Set(['bloquee'])).map((l) => l.cle)).toEqual(['c']);
        expect(lignesFiltrees(ONGLETS[0].lignes, new Set())).toEqual([]);
    });
});

describe('sélection et confirmation', () => {
    it('seules les lignes à importer — avertissement compris — ont une case', () => {
        expect(estSelectionnable({ statut: 'a_importer' })).toBe(true);
        expect(estSelectionnable({ statut: 'avertissement' })).toBe(true);
        expect(estSelectionnable({ statut: 'bloquee' })).toBe(false);
        expect(estSelectionnable({ statut: 'deja_faite' })).toBe(false);
        expect(estSelectionnable({ statut: 'ignoree' })).toBe(false);
    });

    it('coche tout par défaut, sur tous les onglets, et retire les décochées', () => {
        expect(clesSelectionnees(ONGLETS, new Set())).toEqual(['a', 'b', 'f']);
        expect(clesSelectionnees(ONGLETS, new Set(['b']))).toEqual(['a', 'f']);
    });

    it('une décochée inconnue (ligne disparue à la relecture) est sans effet', () => {
        expect(clesSelectionnees(ONGLETS, new Set(['zzz']))).toEqual(['a', 'b', 'f']);
    });

    it('lie les déjà faites qui portent une ligne du manager, et elles seules', () => {
        expect(clesALier(ONGLETS)).toEqual(['d']);
    });

    it('confirme les cochées puis les lignes à lier, sans doublon', () => {
        expect(clesAConfirmer(ONGLETS, new Set(['a']))).toEqual(['b', 'f', 'd']);
    });
});

describe('retenueDiffere', () => {
    it('compare au millime', () => {
        expect(retenueDiffere({ retenue_fichier: 100, retenue_calculee: 100 })).toBe(false);
        expect(retenueDiffere({ retenue_fichier: 0.1 + 0.2, retenue_calculee: 0.3 })).toBe(false);
        expect(retenueDiffere({ retenue_fichier: 100.001, retenue_calculee: 100 })).toBe(true);
    });

    it('ne signale rien quand l’un des deux montants manque', () => {
        expect(retenueDiffere({ retenue_fichier: null, retenue_calculee: 100 })).toBe(false);
        expect(retenueDiffere({ retenue_fichier: 90, retenue_calculee: null })).toBe(false);
    });
});

describe('estDateValide', () => {
    it('accepte une date réelle et refuse le reste', () => {
        expect(estDateValide('2026-01-31')).toBe(true);
        expect(estDateValide('2026-02-30')).toBe(false);
        expect(estDateValide('2026-1-31')).toBe(false);
        expect(estDateValide('')).toBe(false);
    });
});

describe('mémoire des onglets choisis', () => {
    function stockage(): StockageCle & { donnees: Map<string, string> } {
        const donnees = new Map<string, string>();

        return {
            donnees,
            getItem: (cle) => donnees.get(cle) ?? null,
            setItem: (cle, valeur) => {
                donnees.set(cle, valeur);
            },
        };
    }

    it('relit ce qui a été mémorisé pour CE fichier', () => {
        const s = stockage();
        memoriserOnglets(s, 'fichier-A', [1, 3]);
        expect(lireOngletsMemorises(s, 'fichier-A')).toEqual([1, 3]);
        expect(lireOngletsMemorises(s, 'fichier-B')).toBeNull();
    });

    it('survit à un stockage absent, illisible ou qui lève', () => {
        expect(lireOngletsMemorises(null, 'x')).toBeNull();
        expect(() => memoriserOnglets(null, 'x', [1])).not.toThrow();

        const casse: StockageCle = {
            getItem: () => {
                throw new Error('SecurityError');
            },
            setItem: () => {
                throw new Error('QuotaExceededError');
            },
        };
        expect(lireOngletsMemorises(casse, 'x')).toBeNull();
        expect(() => memoriserOnglets(casse, 'x', [1])).not.toThrow();

        const s = stockage();
        s.donnees.set('retenue.import-fichier.onglets.x', '{pas du json');
        expect(lireOngletsMemorises(s, 'x')).toBeNull();
    });

    it('ne recoche que les onglets encore lisibles, et rien sans mémoire', () => {
        const onglets = [
            { gid: 1, titre: 'Janvier', reconnu: true, motif: null },
            { gid: 2, titre: 'Notes', reconnu: false, motif: 'Colonnes non reconnues' },
            { gid: 3, titre: 'Février', reconnu: true, motif: null },
        ];
        expect([...selectionInitiale(onglets, [1, 2, 99])]).toEqual([1]);
        expect(selectionInitiale(onglets, null).size).toBe(0);
    });
});
