/**
 * @fileoverview Rule to prevent unused definitions in Markdown.
 * @author 루밀LuMir(lumirlumir)
 */
//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------
import { normalizeIdentifier } from "micromark-util-normalize-identifier";
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
/**
 * @import { Definition, FootnoteDefinition } from "mdast";
 * @import { MarkdownRuleDefinition } from "../types.js";
 * @typedef {"unusedDefinition" | "unusedFootnoteDefinition"} NoUnusedDefinitionsMessageIds
 * @typedef {[{ allowDefinitions?: string[], allowFootnoteDefinitions?: string[], checkFootnoteDefinitions?: boolean }]} NoUnusedDefinitionsOptions
 * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoUnusedDefinitionsOptions, MessageIds: NoUnusedDefinitionsMessageIds }>} NoUnusedDefinitionsRuleDefinition
 */
//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------
export default /** @satisfies {NoUnusedDefinitionsRuleDefinition} */ ({
    meta: {
        type: "problem",
        docs: {
            recommended: true,
            description: "Disallow unused definitions",
            url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-unused-definitions.md",
        },
        messages: {
            unusedDefinition: "Unexpected unused definition `{{ identifier }}` (label: `{{ label }}`) found.",
            unusedFootnoteDefinition: "Unexpected unused 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;
        /** @type {Set<string>} Set to track used identifiers */
        const usedIdentifiers = new Set();
        /** @type {Set<string>} Set to track used footnote identifiers */
        const usedFootnoteIdentifiers = new Set();
        /** @type {Set<Definition>} */
        const definitions = new Set();
        /** @type {Set<FootnoteDefinition>} */
        const footnoteDefinitions = new Set();
        return {
            imageReference(node) {
                usedIdentifiers.add(node.identifier);
            },
            linkReference(node) {
                usedIdentifiers.add(node.identifier);
            },
            footnoteReference(node) {
                usedFootnoteIdentifiers.add(node.identifier);
            },
            definition(node) {
                if (allowDefinitions.has(node.identifier)) {
                    return;
                }
                definitions.add(node);
            },
            footnoteDefinition(node) {
                if (!checkFootnoteDefinitions ||
                    allowFootnoteDefinitions.has(node.identifier)) {
                    return;
                }
                footnoteDefinitions.add(node);
            },
            "root:exit"() {
                for (const definition of definitions) {
                    if (!usedIdentifiers.has(definition.identifier)) {
                        context.report({
                            node: definition,
                            messageId: "unusedDefinition",
                            data: {
                                identifier: definition.identifier,
                                label: definition.label.trim(),
                            },
                        });
                    }
                }
                for (const footnoteDefinition of footnoteDefinitions) {
                    if (!usedFootnoteIdentifiers.has(footnoteDefinition.identifier)) {
                        context.report({
                            node: footnoteDefinition,
                            messageId: "unusedFootnoteDefinition",
                            data: {
                                identifier: footnoteDefinition.identifier,
                                label: footnoteDefinition.label,
                            },
                        });
                    }
                }
            },
        };
    },
});
