import { getTagOrGroup } from '../../models/group.js';
import { DEFAULT_WEBHOOKS_TAG_NAME, GROUP_DEPTH } from '../../constants.js';
import { getValueFromMdParsedExtension, isOperationName, JsonPointer } from '../../utils/index.js';
import { getOperationsItems } from './operation.js';
import { addMarkdownItems } from './markdown.js';
/**
 * Returns array of OperationsGroup items for the tags of the group or for all tags
 * @param parser
 * @param tagsMap tags info returned from `getTagsWithOperations`
 * @param parent parent item
 * @param group group which this tag belongs to. if not provided gets all tags
 * @param options - normalized options
 */
export function getTagsItems(parser, tagsMap, parent, group, options) {
    let tagNames;
    if (group === undefined) {
        tagNames = Object.keys(tagsMap);
    }
    else {
        tagNames = group.tags;
    }
    if (!Array.isArray(tagNames)) {
        console.warn('Unexpected values of tags. Check tags or x-tagGroups in your definition.');
        return [];
    }
    const tags = tagNames.map((tagName) => {
        if (!tagsMap[tagName]) {
            console.warn(`Non-existing tag "${tagName}" is added to the group "${group?.name}"`);
            return null;
        }
        tagsMap[tagName].used = true;
        return tagsMap[tagName];
    });
    const res = [];
    for (const tag of tags) {
        if (!tag)
            continue;
        const item = getTagOrGroup('tag', tag, parent);
        item.depth = GROUP_DEPTH + 1;
        // don't put empty tag into content, instead put its operations
        if (tag.name === '') {
            const items = [
                ...addMarkdownItems(getValueFromMdParsedExtension(tag, 'description') || '', item, item.depth + 1),
                ...getOperationsItems(undefined, tag, item.depth + 1),
            ];
            res.push(...items);
            continue;
        }
        const relatedSchemas = getTagRelatedSchema({
            definition: parser.definition,
            tag,
            parent: item,
            schemaDefinitionsTagName: options.schemaDefinitionsTagName,
        });
        item.items = [
            ...relatedSchemas,
            ...addMarkdownItems(getValueFromMdParsedExtension(tag, 'description') || '', item, item.depth + 1),
            ...getOperationsItems(item, tag, item.depth + 1),
        ];
        res.push(item);
    }
    return res.filter(({ name, items }) => name !== DEFAULT_WEBHOOKS_TAG_NAME || items.length > 0);
}
/**
 * Returns array of OperationsGroup items for the tag groups (x-tagGroups vendor extension)
 * @param parser
 * @param parent
 * @param groups
 * @param tags value of `x-tagGroups` vendor extension
 * @param options - normalized options
 */
export function getTagGroupsItems(parser, parent, groups, tags, options) {
    const res = [];
    for (const group of groups) {
        const item = getTagOrGroup('group', group, parent);
        item.depth = GROUP_DEPTH;
        item.items = getTagsItems(parser, tags, item, group, options);
        res.push(item);
    }
    return res;
}
/**
 * Returns array of schemas with reference to tag, or schemas with reference to schemaDefinitionsTagName tag
 * @param tagsMap all tags
 * @param tag tag to find reference of schema
 * @param schemaDefinitionsTagName config
 */
function getTagRelatedSchema({ definition, tag, parent, schemaDefinitionsTagName, }) {
    const defaultTags = schemaDefinitionsTagName ? [schemaDefinitionsTagName] : [];
    return Object.entries(definition.components?.schemas || {})
        .map(([schemaName, schema]) => {
        const schemaTags = schema['x-tags'] || defaultTags;
        if (!schemaTags.includes(tag.name))
            return null;
        const item = getTagOrGroup('schema', {
            name: schemaName,
            'x-displayName': `${schema.title || schemaName}`,
            description: `{% schemaDefinition showWriteOnly="true" schemaRef="#/components/schemas/${schemaName}" /%}`,
            isSchema: true,
            level: 2,
        }, parent);
        item.depth = parent.depth + 1;
        return item;
    })
        .filter(Boolean);
}
/**
 * collects tags and maps each tag to list of operations belonging to this tag
 */
export function getTagsWithOperations(parser, explicitTags) {
    const { definition } = parser;
    const tags = {};
    const webhooks = definition['x-webhooks'] || definition.webhooks;
    for (const tag of explicitTags || []) {
        tags[tag.name] = { ...tag, operations: [] };
    }
    if (definition.paths) {
        getTags(parser, definition.paths, tags);
    }
    if (webhooks) {
        getTags(parser, webhooks, tags, true);
    }
    return tags;
}
function getTags(parser, paths, tags, isWebhook) {
    for (const pathName of Object.keys(paths || {})) {
        const path = paths[pathName];
        const operations = Object.keys(path).filter(isOperationName);
        for (const operationName of operations) {
            const operationInfo = path[operationName];
            if (path.$ref) {
                const { resolved: resolvedPaths } = parser.deref(path);
                getTags(parser, { [pathName]: resolvedPaths }, tags, isWebhook);
                continue;
            }
            let operationTags = operationInfo?.tags;
            if (!operationTags || !operationTags.length) {
                // empty tag for operations and default tag for webhooks
                operationTags = isWebhook ? [DEFAULT_WEBHOOKS_TAG_NAME] : [''];
            }
            for (const tagName of operationTags) {
                let tag = tags[tagName];
                if (tag === undefined) {
                    tag = {
                        name: tagName,
                        operations: [],
                    };
                    tags[tagName] = tag;
                }
                if (tag['x-traitTag']) {
                    continue;
                }
                tag.operations.push({
                    ...operationInfo,
                    pathName,
                    pointer: JsonPointer.compile(['paths', pathName, operationName]),
                    httpVerb: operationName,
                    pathParameters: path.parameters || [],
                    pathServers: path.servers,
                    isWebhook: !!isWebhook,
                });
            }
        }
    }
}
//# sourceMappingURL=tags.js.map