/**
 * @fileoverview Enforce the use of relative units for font size.
 * @author Tanuj Kanti
 */
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
/**
 * @import { CSSRuleDefinition } from "../types.js"
 * @typedef {"allowedFontUnits"} RelativeFontUnitsMessageIds
 * @typedef {[{allowUnits?: string[]}]} RelativeFontUnitsOptions
 * @typedef {CSSRuleDefinition<{ RuleOptions: RelativeFontUnitsOptions, MessageIds: RelativeFontUnitsMessageIds}>} RelativeFontUnitsRuleDefinition
 */
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
const relativeFontUnits = [
    "%",
    "cap",
    "ch",
    "em",
    "ex",
    "ic",
    "lh",
    "rcap",
    "rch",
    "rem",
    "rex",
    "ric",
    "rlh",
];
const disallowedFontSizeKeywords = new Set([
    "xx-small",
    "x-small",
    "small",
    "medium",
    "large",
    "x-large",
    "xx-large",
    "xxx-large",
    "math",
]);
//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------
export default /** @satisfies {RelativeFontUnitsRuleDefinition} */ ({
    meta: {
        type: "suggestion",
        docs: {
            description: "Enforce the use of relative font units",
            recommended: false,
            url: "https://github.com/eslint/css/blob/main/docs/rules/relative-font-units.md",
        },
        schema: [
            {
                type: "object",
                properties: {
                    allowUnits: {
                        type: "array",
                        items: {
                            enum: relativeFontUnits,
                        },
                        uniqueItems: true,
                    },
                },
                additionalProperties: false,
            },
        ],
        defaultOptions: [
            {
                allowUnits: ["rem"],
            },
        ],
        messages: {
            allowedFontUnits: "Use only allowed relative units for 'font-size' - {{allowedFontUnits}}.",
        },
    },
    create(context) {
        const [{ allowUnits: allowedFontUnits }] = context.options;
        return {
            Declaration(node) {
                if (node.property === "font-size") {
                    if (node.value.type === "Value" &&
                        node.value.children.length > 0) {
                        const value = node.value.children[0];
                        if ((value.type === "Dimension" &&
                            !allowedFontUnits.includes(value.unit.toLowerCase())) ||
                            (value.type === "Identifier" &&
                                disallowedFontSizeKeywords.has(value.name.toLowerCase())) ||
                            (value.type === "Percentage" &&
                                !allowedFontUnits.includes("%"))) {
                            context.report({
                                loc: value.loc,
                                messageId: "allowedFontUnits",
                                data: {
                                    allowedFontUnits: allowedFontUnits.join(", "),
                                },
                            });
                        }
                    }
                }
                if (node.property === "font") {
                    if (node.value.type === "Value" &&
                        node.value.children.length > 0) {
                        const value = node.value;
                        const dimensionNode = value.children.find(child => child.type === "Dimension");
                        const identifierNode = value.children.find(child => child.type === "Identifier" &&
                            disallowedFontSizeKeywords.has(child.name.toLowerCase()));
                        const percentageNode = value.children.find((child, index) => {
                            const isPercentage = child.type === "Percentage";
                            const previousNode = value.children[index - 1];
                            const previousNodeIsSlashOperator = previousNode &&
                                previousNode.type === "Operator" &&
                                previousNode.value === "/";
                            return (isPercentage && !previousNodeIsSlashOperator);
                        });
                        let location;
                        let shouldReport = false;
                        const conditions = [
                            {
                                check: !allowedFontUnits.includes("%") &&
                                    percentageNode,
                                loc: percentageNode?.loc,
                            },
                            {
                                check: identifierNode,
                                loc: identifierNode?.loc,
                            },
                            {
                                check: dimensionNode &&
                                    !allowedFontUnits.includes(dimensionNode.unit.toLowerCase()),
                                loc: dimensionNode?.loc,
                            },
                        ];
                        for (const condition of conditions) {
                            if (condition.check) {
                                shouldReport = true;
                                location = condition.loc;
                                break;
                            }
                        }
                        if (shouldReport) {
                            context.report({
                                loc: location,
                                messageId: "allowedFontUnits",
                                data: {
                                    allowedFontUnits: allowedFontUnits.join(", "),
                                },
                            });
                        }
                    }
                }
            },
        };
    },
});
