"use strict";
/*
 * Copyright 2020 NEM
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReportService = void 0;
const fs_1 = require("fs");
const _ = require("lodash");
const path_1 = require("path");
const BootstrapUtils_1 = require("./BootstrapUtils");
const ConfigLoader_1 = require("./ConfigLoader");
class ReportService {
    constructor(logger, params) {
        this.logger = logger;
        this.params = params;
        this.configLoader = new ConfigLoader_1.ConfigLoader(logger);
    }
    createReportFromFile(resourceContent, descriptions) {
        const sections = [];
        const lines = resourceContent
            .trim()
            .split(/\s*[\r\n]+\s*/g)
            .map((l) => l.trim())
            .filter((l) => l && l.indexOf('#') != 0);
        lines.forEach((l) => {
            const isHeader = /\[(.*?)\]/.exec(l);
            if (isHeader && isHeader.length && isHeader[1]) {
                sections.push({
                    header: isHeader[1],
                    lines: [],
                });
            }
            else {
                const isProperty = /(.*)=(.*)/.exec(l);
                if (isProperty && isProperty.length && isProperty[1]) {
                    const propertyName = isProperty[1].trim();
                    const descriptor = descriptions[propertyName];
                    const hidden = (descriptor && descriptor.hidden) || false;
                    const value = isProperty[2].trim();
                    sections[sections.length - 1].lines.push({
                        property: propertyName,
                        value: hidden ? value.replace(/./g, '*') : value,
                        hidden: hidden,
                        description: (descriptor && descriptor.description) || '',
                        type: (descriptor && descriptor.type) || '',
                    });
                }
            }
        });
        return sections;
    }
    async createReportsPerNode(presetData) {
        const workingDir = process.cwd();
        const target = path_1.join(workingDir, this.params.target);
        const descriptions = await BootstrapUtils_1.BootstrapUtils.loadYaml(path_1.join(BootstrapUtils_1.BootstrapUtils.ROOT_FOLDER, 'presets', 'descriptions.yml'), false);
        const promises = (presetData.nodes || []).map(async (n) => {
            const resourcesFolder = path_1.join(BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(target, false, n.name), 'server-config', 'resources');
            const files = await fs_1.promises.readdir(resourcesFolder);
            const reportFiles = files
                .filter((fileName) => fileName.indexOf('.properties') > -1)
                .map((fileName) => {
                const resourceContent = fs_1.readFileSync(path_1.join(resourcesFolder, fileName), 'utf-8');
                const sections = this.createReportFromFile(resourceContent, descriptions);
                const reportFile = {
                    fileName: fileName,
                    sections,
                };
                return reportFile;
            });
            const reportNode = {
                name: n.name,
                files: reportFiles,
            };
            return reportNode;
        });
        return Promise.all(promises);
    }
    /**
     *  It generates a .rst config report file per node.
     * @param passedPresetData the preset data,
     */
    async run(passedPresetData) {
        const presetData = passedPresetData !== null && passedPresetData !== void 0 ? passedPresetData : this.configLoader.loadExistingPresetData(this.params.target, false);
        const reportFolder = path_1.join(this.params.target, 'reports');
        BootstrapUtils_1.BootstrapUtils.deleteFolder(this.logger, reportFolder);
        const reportNodes = await this.createReportsPerNode(presetData);
        const missingProperties = _.flatMap(reportNodes, (n) => _.flatMap(n.files, (f) => _.flatMap(f.sections, (s) => s.lines.filter((s) => !s.type && !s.description && !s.property.startsWith('starting-at-height')).map((s) => s.property))));
        const missingDescriptions = _.uniq(missingProperties);
        if (missingDescriptions.length) {
            const missingDescriptionsObject = _.mapValues(_.keyBy(missingProperties, (k) => k), () => ({
                type: '',
                description: '',
            }));
            this.logger.debug('Missing yaml properties: ' + BootstrapUtils_1.BootstrapUtils.toYaml(missingDescriptionsObject));
        }
        // const missingDescriptions = reportNodes.map(node -> node.files)
        await BootstrapUtils_1.BootstrapUtils.mkdir(reportFolder);
        const promises = _.flatMap(reportNodes, (n) => {
            return [this.toRstReport(reportFolder, n), this.toCsvReport(reportFolder, n)];
        });
        return Promise.all(promises);
    }
    async toRstReport(reportFolder, n) {
        const reportFile = path_1.join(reportFolder, `${n.name}-config.rst`);
        const reportContent = `Symbol Bootstrap Version: ${BootstrapUtils_1.BootstrapUtils.VERSION}\n` +
            n.files
                .map((fileReport) => {
                const hasDescriptionSection = fileReport.sections.find((s) => s.lines.find((l) => l.description || l.type));
                const header = hasDescriptionSection ? '"Property", "Value", "Type", "Description"' : '"Property", "Value"';
                const csvBody = fileReport.sections
                    .map((s) => {
                    const hasDescriptionSection = s.lines.find((l) => l.description || l.type);
                    return ((hasDescriptionSection ? `**${s.header}**; ; ;\n` : `**${s.header}**;\n`) +
                        s.lines
                            .map((l) => {
                            if (hasDescriptionSection)
                                return `${l.property}; ${l.value}; ${l.type}; ${l.description}`.trim() + '\n';
                            else {
                                return `${l.property}; ${l.value}`.trim() + '\n';
                            }
                        })
                            .join(''));
                })
                    .join('');
                return `
${fileReport.fileName}
${fileReport.fileName.replace(/./g, '=')}
.. csv-table::
    :header: ${header}
    :delim: ;

${csvBody.trim().replace(/^/gm, '    ')}`;
            })
                .join('\n');
        await BootstrapUtils_1.BootstrapUtils.writeTextFile(reportFile, reportContent);
        this.logger.info(`Report file ${reportFile} created`);
        return reportFile;
    }
    async toCsvReport(reportFolder, n) {
        const reportFile = path_1.join(reportFolder, `${n.name}-config.csv`);
        const reportContent = `symbol-bootstrap-version; ${BootstrapUtils_1.BootstrapUtils.VERSION}\n\n` +
            n.files
                .map((fileReport) => {
                const csvBody = fileReport.sections
                    .map((s) => {
                    const hasDescriptionSection = s.lines.find((l) => l.description || l.type);
                    return (`${s.header}\n` +
                        s.lines
                            .map((l) => {
                            if (hasDescriptionSection)
                                return `${l.property}; ${l.value}; ${l.type}; ${l.description}`.trim() + '\n';
                            else {
                                return `${l.property}; ${l.value}`.trim() + '\n';
                            }
                        })
                            .join(''));
                })
                    .join('\n');
                return `${fileReport.fileName}
${csvBody.trim()}`;
            })
                .join('\n\n\n');
        await BootstrapUtils_1.BootstrapUtils.writeTextFile(reportFile, reportContent);
        this.logger.info(`Report file ${reportFile} created`);
        return reportFile;
    }
}
exports.ReportService = ReportService;
ReportService.defaultParams = {
    target: BootstrapUtils_1.BootstrapUtils.defaultTargetFolder,
};
