import { describe, expect, it } from 'vitest';
import { apercuCertificat, fmtDateFr, fmtMillimes, fmtTaux, fmtTnd, libellePaiement, moisEnAnneeMois } from './format';

/** Retire les séparateurs de milliers (espace fine insécable selon l'ICU). */
const digitsOnly = (s: string) => s.replace(/[^\d,]/g, '');

describe('fmtMillimes', () => {
    it('affiche toujours trois décimales', () => {
        expect(fmtMillimes(0)).toBe('0,000');
        expect(fmtMillimes(12)).toBe('12,000');
        expect(fmtMillimes(12.5)).toBe('12,500');
        expect(fmtMillimes(12.345)).toBe('12,345');
    });

    it('tronque au millime sans en ajouter', () => {
        // 4 décimales en entrée → arrondi à 3 à l'affichage
        expect(fmtMillimes(12.3456)).toBe('12,346');
    });

    it('utilise la virgule décimale française', () => {
        expect(fmtMillimes(1.5)).toContain(',');
        expect(fmtMillimes(1.5)).not.toContain('.');
    });

    it('groupe les milliers avec un séparateur non numérique', () => {
        const out = fmtMillimes(1234.5);
        // On ne fige pas le code point du séparateur (il varie selon l'ICU) :
        // on vérifie qu'il y en a bien un et que les chiffres sont intacts.
        expect(digitsOnly(out)).toBe('1234,500');
        expect(out).not.toBe('1234,500');
        expect(digitsOnly(fmtMillimes(1234567.891))).toBe('1234567,891');
    });

    it('gère les montants négatifs', () => {
        expect(fmtMillimes(-12.5)).toContain('12,500');
    });
});

describe('fmtTnd / fmtTaux', () => {
    it('suffixe la devise', () => {
        expect(fmtTnd(12.5)).toBe('12,500 TND');
    });

    it('affiche le taux à deux décimales avec le signe %', () => {
        expect(fmtTaux(10)).toBe('10,00 %');
        expect(fmtTaux(1.5)).toBe('1,50 %');
    });
});

describe('fmtDateFr', () => {
    it('convertit une date ISO en jj/mm/aaaa', () => {
        expect(fmtDateFr('2026-02-12')).toBe('12/02/2026');
    });

    it('accepte un datetime ISO', () => {
        expect(fmtDateFr('2026-02-12T10:30:00+01:00')).toBe('12/02/2026');
    });

    it('renvoie une chaîne vide pour une valeur absente', () => {
        expect(fmtDateFr(null)).toBe('');
        expect(fmtDateFr('')).toBe('');
    });
});

describe('libellePaiement', () => {
    it('produit « VT UIB LE 12/02/2026 »', () => {
        expect(
            libellePaiement({
                est_paye: true,
                date_paiement: '2026-02-12',
                mode_paiement: 'VIREMENT',
                banque: 'UIB',
            }),
        ).toBe('VT UIB LE 12/02/2026');
    });

    it('abrège les autres modes comme le serveur', () => {
        expect(libellePaiement({ est_paye: true, date_paiement: '2026-02-12', mode_paiement: 'CHÈQUE', banque: null })).toBe('CHQ LE 12/02/2026');
        expect(libellePaiement({ est_paye: true, date_paiement: '2026-02-12', mode_paiement: 'CASH', banque: null })).toBe('ESP LE 12/02/2026');
    });

    it("retombe sur le mode brut quand il n'a pas d'abréviation", () => {
        expect(libellePaiement({ est_paye: true, date_paiement: '2026-02-12', mode_paiement: 'AUTRE', banque: null })).toBe('AUTRE LE 12/02/2026');
    });

    it("renvoie null tant que la ligne n'est pas payée ou datée", () => {
        expect(libellePaiement({ est_paye: false, date_paiement: '2026-02-12', mode_paiement: 'VIREMENT', banque: 'UIB' })).toBeNull();
        expect(libellePaiement({ est_paye: true, date_paiement: null, mode_paiement: 'VIREMENT', banque: 'UIB' })).toBeNull();
    });
});

describe('moisEnAnneeMois', () => {
    it('découpe YYYY-MM', () => {
        expect(moisEnAnneeMois('2026-02')).toEqual({ annee: 2026, mois: 2 });
        expect(moisEnAnneeMois('2026-12')).toEqual({ annee: 2026, mois: 12 });
    });

    it('retombe sur le mois courant si le format est invalide', () => {
        const now = new Date();
        expect(moisEnAnneeMois("n'importe quoi")).toEqual({ annee: now.getFullYear(), mois: now.getMonth() + 1 });
    });
});

describe('apercuCertificat', () => {
    const base = { montantTtc: 0, montantHt: 0, tauxTva: 0, tauxRs: 0, seuilMinTtc: null };

    it('reproduit au millime le certificat réellement déposé le 2026-08-11', () => {
        // Dépôt IN260262 : HT 1 800,000 · TVA 19 % · RS 1 %.
        // Les montants attendus sont ceux que TEJ a ENREGISTRÉS, pas une hypothèse.
        expect(apercuCertificat({ ...base, montantHt: 1800, tauxTva: 19, tauxRs: 1 })).toEqual({
            ht: 1800,
            tva: 342,
            ttc: 2142,
            retenue: 21.42,
            net: 2120.58,
            sousSeuil: false,
        });
    });

    it('TRONQUE au millime, il n’arrondit pas', () => {
        // 23 928,394 × 19 % = 4 546,39486 → TEJ écrit 4 546,394, pas 4 546,395.
        // Cas réel : ligne SODIMAC de la base (certificat importé de TEJ).
        const apercu = apercuCertificat({ ...base, montantHt: 23928.394, tauxTva: 19, tauxRs: 1 });

        expect(apercu.tva).toBe(4546.394);
        expect(apercu.ttc).toBe(28474.788);
        // 28 474,788 × 1 % = 284,74788 → 284,747 et non 284,748.
        expect(apercu.retenue).toBe(284.747);
        expect(apercu.net).toBe(28190.041);
    });

    it('garde les deux identités que TEJ vérifie', () => {
        const apercu = apercuCertificat({ ...base, montantHt: 1038.1, tauxTva: 19, tauxRs: 1 });

        expect(apercu.ht + apercu.tva).toBeCloseTo(apercu.ttc, 3);
        expect(apercu.ttc - apercu.retenue).toBeCloseTo(apercu.net, 3);
    });

    it('sans TVA, le montant saisi EST le TTC — le cas des loyers', () => {
        const apercu = apercuCertificat({ ...base, montantTtc: 2126.433, tauxRs: 10 });

        expect(apercu.ht).toBe(2126.433);
        expect(apercu.tva).toBe(0);
        expect(apercu.ttc).toBe(2126.433);
        expect(apercu.retenue).toBe(212.643);
    });

    it('un HT sans taux de TVA ne décompose rien', () => {
        // Le serveur exige les DEUX pour décomposer : un HT seul laisserait
        // l'écran afficher un TTC que le serveur ne calculerait pas.
        const apercu = apercuCertificat({ ...base, montantTtc: 500, montantHt: 1800, tauxTva: 0, tauxRs: 1 });

        expect(apercu.ttc).toBe(500);
    });

    it('sous le seuil, la retenue est nulle et le net vaut le TTC', () => {
        const apercu = apercuCertificat({ ...base, montantTtc: 999.999, tauxRs: 1, seuilMinTtc: 1000 });

        expect(apercu.sousSeuil).toBe(true);
        expect(apercu.retenue).toBe(0);
        expect(apercu.net).toBe(999.999);
    });
});
