"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LazyModuleDocumentation = exports.parseRawRouting = exports.processRawOptions = exports.processRawDocumentation = exports.processDocumentationFragments = void 0;
const _ = __importStar(require("lodash"));
const fs = __importStar(require("fs"));
const yaml_1 = require("yaml");
const misc_1 = require("./misc");
const DOCUMENTATION = "DOCUMENTATION";
function processDocumentationFragments(module, docFragments) {
    module.fragments = [];
    const mainDocumentationFragment = module.rawDocumentationFragments.get(DOCUMENTATION);
    if (mainDocumentationFragment &&
        (0, misc_1.hasOwnProperty)(mainDocumentationFragment, "extends_documentation_fragment")) {
        const docFragmentNames = mainDocumentationFragment.extends_documentation_fragment instanceof Array
            ? mainDocumentationFragment.extends_documentation_fragment
            : [mainDocumentationFragment.extends_documentation_fragment];
        const resultContents = {};
        for (const docFragmentName of docFragmentNames) {
            const fragmentNameArray = docFragmentName.split(".");
            let fragmentPartName;
            if (fragmentNameArray.length === 2 || fragmentNameArray.length === 4) {
                fragmentPartName = fragmentNameArray.pop()?.toUpperCase();
            }
            else {
                fragmentPartName = DOCUMENTATION;
            }
            const docFragmentCatalogueName = fragmentNameArray.join(".");
            const docFragment = docFragments.get(docFragmentCatalogueName) ||
                docFragments.get(`ansible.builtin.${docFragmentCatalogueName}`);
            if (docFragment &&
                docFragment.rawDocumentationFragments.has(fragmentPartName)) {
                module.fragments.push(docFragment); // currently used only as indicator
                _.mergeWith(resultContents, docFragment.rawDocumentationFragments.get(fragmentPartName), docFragmentMergeCustomizer);
            }
        }
        _.mergeWith(resultContents, mainDocumentationFragment, docFragmentMergeCustomizer);
        module.rawDocumentationFragments.set(DOCUMENTATION, resultContents);
    }
}
exports.processDocumentationFragments = processDocumentationFragments;
function docFragmentMergeCustomizer(objValue, srcValue, key) {
    if (["notes", "requirements", "seealso"].includes(key) &&
        _.isArray(objValue)) {
        return objValue.concat(srcValue);
    }
}
function processRawDocumentation(moduleDocParts) {
    // currently processing only the main documentation
    const rawDoc = moduleDocParts.get(DOCUMENTATION);
    if (rawDoc && typeof rawDoc.module === "string") {
        const moduleDoc = {
            module: rawDoc.module,
            options: processRawOptions(rawDoc.options),
            deprecated: !!rawDoc.deprecated,
        };
        if (isIDescription(rawDoc.short_description))
            moduleDoc.shortDescription = rawDoc.short_description;
        if (isIDescription(rawDoc.description))
            moduleDoc.description = rawDoc.description;
        if (typeof rawDoc.version_added === "string")
            moduleDoc.versionAdded = rawDoc.version_added;
        if (isIDescription(rawDoc.author))
            moduleDoc.author = rawDoc.author;
        if (isIDescription(rawDoc.requirements))
            moduleDoc.requirements = rawDoc.requirements;
        if (typeof rawDoc.seealso === "object")
            moduleDoc.seealso = rawDoc.seealso;
        if (isIDescription(rawDoc.notes))
            moduleDoc.notes = rawDoc.notes;
        return moduleDoc;
    }
}
exports.processRawDocumentation = processRawDocumentation;
function processRawOptions(rawOptions) {
    const options = new Map();
    if ((0, misc_1.isObject)(rawOptions)) {
        for (const [optionName, rawOption] of Object.entries(rawOptions)) {
            if ((0, misc_1.isObject)(rawOption)) {
                const optionDoc = {
                    name: optionName,
                    required: !!rawOption.required,
                    default: rawOption.default,
                    suboptions: processRawOptions(rawOption.suboptions),
                };
                if (isIDescription(rawOption.description))
                    optionDoc.description = rawOption.description;
                if (rawOption.choices instanceof Array)
                    optionDoc.choices = rawOption.choices;
                if (typeof rawOption.type === "string")
                    optionDoc.type = rawOption.type;
                if (typeof rawOption.elements === "string")
                    optionDoc.elements = rawOption.elements;
                if (rawOption.aliases instanceof Array)
                    optionDoc.aliases = rawOption.aliases;
                if (typeof rawOption.version_added === "string")
                    optionDoc.versionAdded = rawOption.version_added;
                options.set(optionName, optionDoc);
                if (optionDoc.aliases) {
                    for (const alias of optionDoc.aliases) {
                        options.set(alias, optionDoc);
                    }
                }
            }
        }
    }
    return options;
}
exports.processRawOptions = processRawOptions;
function isIDescription(obj) {
    return (obj instanceof Array || // won't check that all elements are string
        typeof obj === "string");
}
function parseRawRouting(rawDoc) {
    const routesByType = new Map();
    if ((0, misc_1.hasOwnProperty)(rawDoc, "plugin_routing") &&
        (0, misc_1.isObject)(rawDoc.plugin_routing)) {
        for (const [pluginType, rawRoutesByName] of Object.entries(rawDoc.plugin_routing)) {
            if (pluginType === "modules" && (0, misc_1.isObject)(rawRoutesByName)) {
                routesByType.set(pluginType, parseRawRoutesByName(rawRoutesByName));
            }
        }
    }
    return routesByType;
}
exports.parseRawRouting = parseRawRouting;
function parseRawRoutesByName(rawRoutesByName) {
    const routesByName = new Map();
    for (const [moduleName, rawRoute] of Object.entries(rawRoutesByName)) {
        if ((0, misc_1.isObject)(rawRoute))
            routesByName.set(moduleName, parseRawRoute(rawRoute));
    }
    return routesByName;
}
function parseRawRoute(rawRoute) {
    const route = {};
    if ((0, misc_1.isObject)(rawRoute.deprecation)) {
        route.deprecation = parseRawDeprecationOrTombstone(rawRoute.deprecation);
    }
    if ((0, misc_1.isObject)(rawRoute.tombstone)) {
        route.tombstone = parseRawDeprecationOrTombstone(rawRoute.tombstone);
    }
    if (typeof rawRoute.redirect === "string") {
        route.redirect = rawRoute.redirect;
    }
    return route;
}
function parseRawDeprecationOrTombstone(rawInfo) {
    let warningText;
    let removalDate;
    let removalVersion;
    if (typeof rawInfo.warning_text === "string") {
        warningText = rawInfo.warning_text;
    }
    if (typeof rawInfo.removal_date === "string") {
        removalDate = rawInfo.removal_date;
    }
    if (typeof rawInfo.removal_version === "string") {
        removalVersion = rawInfo.removal_version;
    }
    return {
        warningText: warningText,
        removalDate: removalDate,
        removalVersion: removalVersion,
    };
}
class LazyModuleDocumentation {
    static docsRegex = /(?<pre>[ \t]*(?<name>[A-Z0-9_]+)\s*=\s*r?(?<quotes>'''|""")(?:\n---)?\n?)(?<doc>.*?)\k<quotes>/gs;
    source;
    sourceLineRange = [0, 0];
    fqcn;
    namespace;
    collection;
    name;
    errors = [];
    _contents;
    constructor(source, fqcn, namespace, collection, name) {
        this.source = source;
        this.fqcn = fqcn;
        this.namespace = namespace;
        this.collection = collection;
        this.name = name;
    }
    get rawDocumentationFragments() {
        if (!this._contents) {
            this._contents = new Map();
            const contents = fs.readFileSync(this.source, { encoding: "utf8" });
            let m;
            while ((m = LazyModuleDocumentation.docsRegex.exec(contents)) !== null) {
                if (m && m.groups && m.groups.name && m.groups.doc && m.groups.pre) {
                    if (m.groups.name === DOCUMENTATION) {
                        // determine documentation start/end lines for definition provider
                        let startLine = contents.substr(0, m.index).match(/\n/g)?.length || 0;
                        startLine += m.groups.pre.match(/\n/g)?.length || 0;
                        const endLine = startLine + (m.groups.doc.match(/\n/g)?.length || 0);
                        this.sourceLineRange = [startLine, endLine];
                    }
                    const document = (0, yaml_1.parseDocument)(m.groups.doc);
                    // There's about 20 modules (out of ~3200) in Ansible 2.9 libs that contain YAML syntax errors
                    // Still, document.toJSON() works on them
                    this._contents.set(m.groups.name, document.toJSON());
                    this.errors = document.errors;
                }
            }
        }
        return this._contents;
    }
    set rawDocumentationFragments(value) {
        this._contents = value;
    }
}
exports.LazyModuleDocumentation = LazyModuleDocumentation;
