import clone from 'just-clone';
import extend from 'just-extend';

/**
 * Librairie de fonctions utilitaires de génération de définition de colonnes pour AG Grid.
 *
 * @module
 */

/**
 * Options de filtrage de définitions de colonnes
 * @typedef BuildColumnDefs_Selecteur
 * @property {?boolean} explicite - il doit exister un élément de comparaison (ex: 'tags' si 'tag' est utilisé)
 * @property {?string[]} tags - des valeurs de sélection arbitraires, il faut au moins une correspondance
 * @property {?import('../../../../jsdoc/types').Profil[]} profils - des profils de sélection, il faut au moins une correspondance
 *
 * Génère l'objet columnDefs pour l'affichage d'une liste.
 * @example
 * // Renvoie l'objet ColDef pour, dans l'ordre :
 * // - le groupe identité avec toutes ses colonne par défaut
 * // - le groupe "EIP actuel" avec la colonne "Filière" (avec propriétés spécifiques) puis la colonne "Domaine"
 * // - le groupe "EIP futur" avec toutes les colonne par défaut, avec propriétés spécifiques pour le groupe.
 * buildColumnDefs(
 *      [   "identite",
 *          {
 *              groupId:"EIPActuel",
 *              columns:
 *                  [
 *                      {
 *                          colId:"EIPActuel_filiere",
 *                          override: {...}
 *                      },
 *                      "EIPActuel_domaine"
 *                  ]
 *          },
 *          {
 *              groupId:"EIPFutur",
 *              override: {...}
 *          }
 *      ],
 *      administreCompleteColumnDefs,
 *      { tags: ['ITD], profils: ['PCP'] }
 *  )
 *
 * @param {(string|import('../../../../jsdoc/types').ColGroupDefConstruction)[]} groupsAndColumns - Liste des groupes de colonnes et des colonnes à générer
 * @param {import('../../../../jsdoc/types').AppColGroupDef[]} completeColumnDefs - Liste de toutes les définitions de groupes de colonnes et de leurs colonnes
 * @param {?BuildColumnDefs_Selecteur} [selecteur] - filtre de sélection, uniquement utilisé quand un groupe ne spécifie pas les colonnes
 * - sans filtre, toutes les colonnes qui n'ont pas 'notDisplayedByDefault' sont sélectionnées
 * - avec un filtre, tous les attributs doivent fonctionner
 * @returns {import('../../../../jsdoc/types').AppColGroupDef[]} - liste de définitions de groupes de colonnes
 */
export function buildColumnDefs(groupsAndColumns, completeColumnDefs, selecteur) {
    // Initialisation de l'objet columnDefs à générer
    const columnDefs = [];

    // Harmonisation du paramétrage avant son traitement
    for (let groupIndex = 0; groupIndex < groupsAndColumns.length; groupIndex++) {
        let currentGroup = groupsAndColumns[groupIndex];

        // Si la définition du groupe de colonne est une chaîne (= id du groupe), on la remplace par l'objet correspondant
        if (typeof currentGroup === 'string' || currentGroup instanceof String) {
            currentGroup = { groupId: currentGroup, columns: null };
            groupsAndColumns[groupIndex] = currentGroup;
        }

        // Harmonisation du type du paramétrage des colonnes du groupe
        if (currentGroup.columns && currentGroup.columns.length) {
            currentGroup.columnsById = new Map();

            for (let colIndex = 0; colIndex < currentGroup.columns.length; colIndex++) {
                let col = currentGroup.columns[colIndex];
                // Si la définition de colonne est une chaîne (= id de la colonne), on la remplace par l'objet correspondant
                if (typeof col === 'string' || col instanceof String) {
                    col = { colId: col };
                    currentGroup.columns[colIndex] = col;
                }

                col.order = colIndex;

                // Mémorisation de la colonne à partir de son id
                currentGroup.columnsById.set(col.colId, col);
            }
        }
    }

    // Construction de la listes des définitions demandées
    for (const currentGroup of groupsAndColumns) {
        // Recherche du groupe correspondant dans la définition
        const completeDefForCurrentGroup = completeColumnDefs.find((cgd) => cgd.groupId === currentGroup.groupId);
        if (!completeDefForCurrentGroup) {
            console.warn('Groupe de colonne inconnu : groupId=' + currentGroup.groupId);
            continue;
        }

        // Copie en profondeur de la définition de groupe complète
        const currentColGroupDef = clone(completeDefForCurrentGroup);

        // Surcharge si nécessaire de la définition de groupe
        if (currentGroup.override) {
            extend(true, currentColGroupDef, currentGroup.override);
        }

        // Traitement de la liste des colonnes du groupe (si nécessaire)
        if (currentGroup.columns && currentGroup.columns.length) {
            // On ne conserve que les éventuelles colonnes qui sont demandées
            currentColGroupDef.children = currentColGroupDef.children.filter((colDef) => currentGroup.columnsById.has(colDef.colId));

            // On réordonne les colonnes dans l'ordre demandé
            currentColGroupDef.children.sort(function (colDefA, colDefB) {
                return currentGroup.columnsById.get(colDefA.colId).order - currentGroup.columnsById.get(colDefB.colId).order;
            });

            // Surcharge des définitions de colonnes qui le nécessitent
            for (let colDef of currentColGroupDef.children) {
                const col = currentGroup.columnsById.get(colDef.colId);
                if (col.override) {
                    extend(true, colDef, col.override);
                }
            }

            // On vérifie qu'on a bien trouvé toutes les colonnes demandées
            if (currentColGroupDef.children.length != currentGroup.columns.length) {
                console.warn(
                    'Au moins une colonne inconnue pour le groupe de colonne : groupId=' + currentGroup.groupId,
                    currentGroup.columns,
                    currentColGroupDef.children
                );
            }
        } else {
            /** @param {import('../../../../jsdoc/types').AppColDef} colDef */
            const filtre = (colDef) => {
                if (!selecteur) {
                    return !colDef.notDisplayedByDefault && !colDef.selecteur;
                }
                if (selecteur.tags) {
                    const tags = colDef.selecteur?.tags;
                    if ((!tags && selecteur.explicite) || (tags && !selecteur.tags.some(val => tags.includes(val)))) {
                        return false;
                    }
                }
                if (selecteur.profils) {
                    const profils = colDef.selecteur?.profils;
                    if ((!profils && selecteur.explicite) || (profils && !selecteur.profils.some(val => profils.includes(val)))) {
                        return false;
                    }
                }
                return true;
            };
            // Si on a demandé toutes les colonnes, on retire celle qui ne doivent pas apparaître par défaut
            currentColGroupDef.children = currentColGroupDef.children.filter(filtre);
        }

        columnDefs.push(currentColGroupDef);
    }

    return columnDefs;
}

/**
 * Ajoute une variante d'une définition de groupe de colonnes.
 *
 * @param {Array} colDefs - Liste des définitiosn de groupes de colonnes et colonnes
 * @param {string} sourceGroupId - Non du groupe non modifiable à copier
 * @param {string} newGroupId - Nom du groupe modifiable à créer
 * @param {Object} override - Données à compléter ou surcharger
 * @returns Nouveau groupe créé (par ailleurs automatiquement ajouté à colDefs)
 */
export function buildAndAddColGroupDefVariant(colDefs, sourceGroupId, newGroupId, override) {
    const sourceGroup = colDefs.find((cd) => cd.groupId === sourceGroupId);
    const newGroup = extend(true, {}, sourceGroup, override, { groupId: newGroupId });
    colDefs.push(newGroup);
    //console.log('buildAndAddColGroupDefVariant', sourceGroup, newGroup);
    return newGroup;
}

/**
 * Modification et ajoute une variante d'une définition de groupe de colonnes.
 *
 * @param {Array} colDefs - Liste des définitiosn de groupes de colonnes et colonnes
 * @param {string} sourceGroupId - Non du groupe non modifiable à copier
 * @param {string} colFix - Liste des colonnes qui doivent rester
 * @param {string} newGroupId - Nom du groupe modifiable à créer
 * @param {Object} override - Données à compléter ou surcharger
 * @returns Nouveau groupe créé (par ailleurs automatiquement ajouté à colDefs)
 */
 export function buildAndAddandDelColGroupDefVariant(colDefs, sourceGroupId, colFix, newGroupId, override) {
    const sourceGroup = extend(true, {}, colDefs.find((cd) => cd.groupId === sourceGroupId));
    sourceGroup.children = sourceGroup.children.filter(item => colFix.includes(item.colId));
    const newGroup = extend(true, {}, sourceGroup, override, { groupId: newGroupId });
    colDefs.push(newGroup);
    //console.log('buildAndAddColGroupDefVariant', sourceGroup, newGroup);
    return newGroup;
}
