import type { CompteursApercu, LigneApercu, OngletApercu, StatutLigne } from './types';

/**
 * L'aperçu de l'import : quelles lignes se montrent, lesquelles se cochent, et
 * ce qui part à la confirmation. Logique pure, testée dans `preview-filtres.test.ts`.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * QUATRE FILTRES POUR CINQ STATUTS
 * ═════════════════════════════════════════════════════════════════════════════
 * `avertissement` n'a pas de pastille à lui : c'est une ligne À IMPORTER qui
 * porte une remarque (fiche incomplète, retenue du fichier différente…). La
 * séparer ferait croire qu'elle ne sera pas créée. Elle se range donc sous
 * « À importer », se coche par défaut, et dit sa remarque sur sa ligne.
 *
 * « Ignorées » est masqué par défaut : ce sont les lignes jaunes déjà déclarées,
 * les totaux, les lignes vides — le gros du fichier, et rien à décider.
 *
 * ═════════════════════════════════════════════════════════════════════════════
 * ON RETIENT CE QUI EST DÉCOCHÉ, PAS CE QUI EST COCHÉ
 * ═════════════════════════════════════════════════════════════════════════════
 * Tout se coche par défaut. « Relire » ou un changement de date relancent
 * l'aperçu : retenir les DÉCOCHÉES garde le choix de l'utilisateur sur les
 * lignes qu'il a vues, et laisse cochée toute ligne apparue entre-temps.
 */

export type FiltreApercu = 'a_importer' | 'bloquee' | 'deja_faite' | 'ignoree';

/** Ordre d'affichage des pastilles. */
export const FILTRES_APERCU: readonly FiltreApercu[] = ['a_importer', 'bloquee', 'deja_faite', 'ignoree'];

/** La pastille sous laquelle se range un statut. */
export function filtreDuStatut(statut: StatutLigne): FiltreApercu {
    return statut === 'avertissement' ? 'a_importer' : statut;
}

/** Les compteurs du serveur, rangés par pastille. */
export function compteursParFiltre(compteurs: CompteursApercu): Record<FiltreApercu, number> {
    return {
        a_importer: compteurs.a_importer + compteurs.avertissement,
        bloquee: compteurs.bloquee,
        deja_faite: compteurs.deja_faite,
        ignoree: compteurs.ignoree,
    };
}

/** Pastilles actives à l'ouverture : tout sauf « Ignorées ». */
export function filtresParDefaut(): Set<FiltreApercu> {
    return new Set<FiltreApercu>(['a_importer', 'bloquee', 'deja_faite']);
}

/** Active ou désactive une pastille — un nouvel ensemble, jamais muté. */
export function basculerFiltre(actifs: ReadonlySet<FiltreApercu>, filtre: FiltreApercu): Set<FiltreApercu> {
    const suivant = new Set(actifs);
    if (suivant.has(filtre)) suivant.delete(filtre);
    else suivant.add(filtre);

    return suivant;
}

/** Les lignes d'un onglet que les pastilles actives laissent voir, dans l'ordre du fichier. */
export function lignesFiltrees(lignes: readonly LigneApercu[], actifs: ReadonlySet<FiltreApercu>): LigneApercu[] {
    return lignes.filter((ligne) => actifs.has(filtreDuStatut(ligne.statut)));
}

/** Seule une ligne à importer (avertissement compris) a une case à cocher. */
export function estSelectionnable(ligne: Pick<LigneApercu, 'statut'>): boolean {
    return ligne.statut === 'a_importer' || ligne.statut === 'avertissement';
}

/** Les clés cochées, sur tous les onglets — celles qui seront CRÉÉES. */
export function clesSelectionnees(onglets: readonly OngletApercu[], decochees: ReadonlySet<string>): string[] {
    return onglets.flatMap((onglet) => onglet.lignes.filter((l) => estSelectionnable(l) && !decochees.has(l.cle)).map((l) => l.cle));
}

/**
 * Les lignes déjà faites dans le manager, à LIER au fichier (pour leur
 * couleur) : celles qui portent une `ligne_manager`. Aucune case : les lier ne
 * crée rien, et c'est ce qui permet au fichier de se colorer.
 */
export function clesALier(onglets: readonly OngletApercu[]): string[] {
    return onglets.flatMap((onglet) => onglet.lignes.filter((l) => l.statut === 'deja_faite' && l.ligne_manager !== null).map((l) => l.cle));
}

/** Le corps `cles` de la confirmation : les lignes cochées, puis celles à lier. */
export function clesAConfirmer(onglets: readonly OngletApercu[], decochees: ReadonlySet<string>): string[] {
    return [...new Set([...clesSelectionnees(onglets, decochees), ...clesALier(onglets)])];
}

/**
 * La retenue écrite dans le fichier diffère-t-elle de celle que le manager
 * déclarera ? Comparée au MILLIME — la règle de l'écran — pour qu'un 0,1 + 0,2
 * flottant ne lève pas de fausse alerte.
 */
export function retenueDiffere(ligne: Pick<LigneApercu, 'retenue_fichier' | 'retenue_calculee'>): boolean {
    if (ligne.retenue_fichier === null || ligne.retenue_calculee === null) return false;

    return Math.round(ligne.retenue_fichier * 1000) !== Math.round(ligne.retenue_calculee * 1000);
}

/** Un `Y-m-d` complet et réel (« 2026-02-30 » ne l'est pas). */
export function estDateValide(valeur: string): boolean {
    const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(valeur);
    if (!m) return false;

    const date = new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])));

    return date.getUTCFullYear() === Number(m[1]) && date.getUTCMonth() === Number(m[2]) - 1 && date.getUTCDate() === Number(m[3]);
}
