// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
/* jshint node: true, browser: false */
/* eslint-env node */

/**
 * @copyright  2021 Andrew Nicols
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

module.exports = grunt => {

    /**
     * Generate the PHPCS configuration.
     *
     * @param {Object} thirdPartyPaths
     */
    const phpcsIgnore = (thirdPartyPaths) => {
        const {toXML} = require('jstoxml');

        const config = {
            _name: 'ruleset',
            _attrs: {
                name: "MoodleCore",
            },
            _content: [
                {
                    rule: {
                        _attrs: {
                            ref: './phpcs.xml.dist',
                        },
                    },
                },
            ],
        };

        thirdPartyPaths.forEach(library => {
            config._content.push({
                'exclude-pattern': library,
            });
        });

        grunt.file.write('phpcs.xml', toXML(config, {
            header: true,
            indent: '  ',
        }) + "\n");
    };

    /**
     * Extracts ignore entries from a local ignore file.
     *
     * @param {string} componentPath the file path to the component, relative to the code base directory
     * @param {string} ignoreFilePath the path to the ignore file
     * @return {array} array of ignore paths to be included in the global ignore files
     */
    const getEntriesFromLocalIgnoreFile = (componentPath, ignoreFilePath) => {
        const ignorePaths = [];
        if (grunt.file.exists(ignoreFilePath)) {
            const ignoreFile = grunt.file.read(ignoreFilePath);
            const entries = ignoreFile.split('\n');
            entries.forEach(entry => {
                entry = entry.trim();
                if (entry.length > 0 && !entry.startsWith('#') && !entry.startsWith('!')) {
                    while (entry.startsWith('/')) {
                        entry = entry.substring(1);
                    }
                    ignorePaths.push(componentPath + '/' + entry);
                }
            });
        }
        return ignorePaths;
    };

    /**
     * Generate ignore files (utilising thirdpartylibs.xml data)
     */
    const handler = function() {
        const path = require('path');
        const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));

        // An array of paths to third party directories.
        const thirdPartyPaths = ComponentList.getThirdPartyPaths();
        const localStylelintIgnorePaths = [];
        const localEslintIgnorePaths = [];
        ComponentList.getComponentPaths(process.cwd() + '/').forEach(componentPath => {
            const localEslintIgnorePath = process.cwd() + '/' + componentPath + '/.eslintignore';
            const localEslintIgnoreEntries = getEntriesFromLocalIgnoreFile(componentPath, localEslintIgnorePath);
            if (localEslintIgnoreEntries.length > 0) {
                localEslintIgnorePaths.push(...localEslintIgnoreEntries);
            }
            const localStylelintIgnorePath = process.cwd() + '/' + componentPath + '/.stylelintignore';
            const localStylelintIgnoreEntries = getEntriesFromLocalIgnoreFile(componentPath, localStylelintIgnorePath);
            if (localStylelintIgnoreEntries.length > 0) {
                localStylelintIgnorePaths.push(...localStylelintIgnoreEntries);
            }
        });

        // Generate .eslintignore.
        const eslintIgnores = [
            '# Generated by "grunt ignorefiles"',
            // Do not ignore the .grunt directory.
            '!/.grunt',

            // Ignore all yui/src meta directories and build directories.
            '*/**/yui/src/*/meta/',
            '*/**/build/',
        ].concat(thirdPartyPaths).concat(localEslintIgnorePaths);
        grunt.file.write('.eslintignore', eslintIgnores.join('\n') + '\n');

        // Generate .stylelintignore.
        const stylelintIgnores = [
            '# Generated by "grunt ignorefiles"',
            '**/yui/build/*',
            'public/theme/boost/style/moodle.css',
            'public/theme/classic/style/moodle.css',
            'jsdoc/styles/*.css',
            'public/admin/tool/componentlibrary/hugo/dist/css/docs.css',
        ].concat(thirdPartyPaths).concat(localStylelintIgnorePaths);
        grunt.file.write('.stylelintignore', stylelintIgnores.join('\n') + '\n');

        phpcsIgnore(thirdPartyPaths);
    };

    grunt.registerTask('ignorefiles', 'Generate ignore files for linters', handler);

    return handler;
};
