/**
 * @fileoverview Rule to prevent empty definitions in Markdown.
 * @author Pixel998
 */
//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------
import { normalizeIdentifier } from "micromark-util-normalize-identifier";
import { htmlCommentPattern } from "../util.js";
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
/**
 * @import { MarkdownRuleDefinition } from "../types.js";
 * @typedef {"emptyDefinition" | "emptyFootnoteDefinition"} NoEmptyDefinitionsMessageIds
 * @typedef {[{ allowDefinitions?: string[], allowFootnoteDefinitions?: string[], checkFootnoteDefinitions?: boolean }]} NoEmptyDefinitionsOptions
 * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoEmptyDefinitionsOptions, MessageIds: NoEmptyDefinitionsMessageIds }>} NoEmptyDefinitionsRuleDefinition
 */
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
/**
 * Checks if a string contains only HTML comments.
 * @param {string} value The input string to check.
 * @returns {boolean} True if the string contains only HTML comments, false otherwise.
 */
function isOnlyComments(value) {
    const withoutComments = value.replace(htmlCommentPattern, "");
    return withoutComments.trim().length === 0;
}
//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------
export default /** @satisfies {NoEmptyDefinitionsRuleDefinition} */ ({
    meta: {
        type: "problem",
        docs: {
            recommended: true,
            description: "Disallow empty definitions",
            url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-empty-definitions.md",
        },
        messages: {
            emptyDefinition: "Unexpected empty definition `{{ identifier }}` (label: `{{ label }}`) found.",
            emptyFootnoteDefinition: "Unexpected empty footnote definition `{{ identifier }}` (label: `{{ label }}`) found.",
        },
        schema: [
            {
                type: "object",
                properties: {
                    allowDefinitions: {
                        type: "array",
                        items: {
                            type: "string",
                        },
                        uniqueItems: true,
                    },
                    allowFootnoteDefinitions: {
                        type: "array",
                        items: {
                            type: "string",
                        },
                        uniqueItems: true,
                    },
                    checkFootnoteDefinitions: {
                        type: "boolean",
                    },
                },
                additionalProperties: false,
            },
        ],
        defaultOptions: [
            {
                allowDefinitions: ["//"],
                allowFootnoteDefinitions: [],
                checkFootnoteDefinitions: true,
            },
        ],
    },
    create(context) {
        const allowDefinitions = new Set(context.options[0].allowDefinitions.map(identifier => normalizeIdentifier(identifier).toLowerCase()));
        const allowFootnoteDefinitions = new Set(context.options[0].allowFootnoteDefinitions.map(identifier => normalizeIdentifier(identifier).toLowerCase()));
        const [{ checkFootnoteDefinitions }] = context.options;
        return {
            definition(node) {
                if ((!node.url || node.url === "#") &&
                    !allowDefinitions.has(node.identifier)) {
                    context.report({
                        loc: node.position,
                        messageId: "emptyDefinition",
                        data: {
                            identifier: node.identifier,
                            label: node.label.trim(),
                        },
                    });
                }
            },
            footnoteDefinition(node) {
                if (checkFootnoteDefinitions &&
                    !allowFootnoteDefinitions.has(node.identifier) &&
                    (node.children.length === 0 ||
                        node.children.every(child => child.type === "html" &&
                            isOnlyComments(child.value)))) {
                    context.report({
                        loc: node.position,
                        messageId: "emptyFootnoteDefinition",
                        data: {
                            identifier: node.identifier,
                            label: node.label,
                        },
                    });
                }
            },
        };
    },
});
