//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
/**
 * @import { CSSRuleDefinition } from "../types.js"
 * @typedef {"notLogicalProperty" | "notLogicalValue" | "notLogicalUnit" | "replaceWithLogicalProperty" | "replaceWithLogicalValue" | "replaceWithLogicalUnit"} PreferLogicalPropertiesMessageIds
 * @typedef {[{
 *     allowProperties?: string[],
 *     allowUnits?: string[]
 * }]} PreferLogicalPropertiesOptions
 * @typedef {CSSRuleDefinition<{ RuleOptions: PreferLogicalPropertiesOptions, MessageIds: PreferLogicalPropertiesMessageIds }>} PreferLogicalPropertiesRuleDefinition
 */
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
const propertiesReplacements = new Map([
    ["bottom", "inset-block-end"],
    ["border-bottom", "border-block-end"],
    ["border-bottom-color", "border-block-end-color"],
    ["border-bottom-left-radius", "border-end-start-radius"],
    ["border-bottom-right-radius", "border-end-end-radius"],
    ["border-bottom-style", "border-block-end-style"],
    ["border-bottom-width", "border-block-end-width"],
    ["border-left", "border-inline-start"],
    ["border-left-color", "border-inline-start-color"],
    ["border-left-style", "border-inline-start-style"],
    ["border-left-width", "border-inline-start-width"],
    ["border-right", "border-inline-end"],
    ["border-right-color", "border-inline-end-color"],
    ["border-right-style", "border-inline-end-style"],
    ["border-right-width", "border-inline-end-width"],
    ["border-top", "border-block-start"],
    ["border-top-color", "border-block-start-color"],
    ["border-top-left-radius", "border-start-start-radius"],
    ["border-top-right-radius", "border-start-end-radius"],
    ["border-top-style", "border-block-start-style"],
    ["border-top-width", "border-block-start-width"],
    ["contain-intrinsic-height", "contain-intrinsic-block-size"],
    ["contain-intrinsic-width", "contain-intrinsic-inline-size"],
    ["height", "block-size"],
    ["left", "inset-inline-start"],
    ["margin-bottom", "margin-block-end"],
    ["margin-left", "margin-inline-start"],
    ["margin-right", "margin-inline-end"],
    ["margin-top", "margin-block-start"],
    ["max-height", "max-block-size"],
    ["max-width", "max-inline-size"],
    ["min-height", "min-block-size"],
    ["min-width", "min-inline-size"],
    ["overflow-x", "overflow-inline"],
    ["overflow-y", "overflow-block"],
    ["overscroll-behavior-x", "overscroll-behavior-inline"],
    ["overscroll-behavior-y", "overscroll-behavior-block"],
    ["padding-bottom", "padding-block-end"],
    ["padding-left", "padding-inline-start"],
    ["padding-right", "padding-inline-end"],
    ["padding-top", "padding-block-start"],
    ["right", "inset-inline-end"],
    ["scroll-margin-bottom", "scroll-margin-block-end"],
    ["scroll-margin-left", "scroll-margin-inline-start"],
    ["scroll-margin-right", "scroll-margin-inline-end"],
    ["scroll-margin-top", "scroll-margin-block-start"],
    ["scroll-padding-bottom", "scroll-padding-block-end"],
    ["scroll-padding-left", "scroll-padding-inline-start"],
    ["scroll-padding-right", "scroll-padding-inline-end"],
    ["scroll-padding-top", "scroll-padding-block-start"],
    ["top", "inset-block-start"],
    ["width", "inline-size"],
]);
const propertyValuesReplacements = new Map([
    [
        "text-align",
        {
            left: "start",
            right: "end",
        },
    ],
    [
        "resize",
        {
            horizontal: "inline",
            vertical: "block",
        },
    ],
    [
        "caption-side",
        {
            left: "inline-start",
            right: "inline-end",
        },
    ],
    [
        "box-orient",
        {
            horizontal: "inline-axis",
            vertical: "block-axis",
        },
    ],
    [
        "float",
        {
            left: "inline-start",
            right: "inline-end",
        },
    ],
    [
        "clear",
        {
            left: "inline-start",
            right: "inline-end",
        },
    ],
]);
const unitReplacements = new Map([
    ["cqh", "cqb"],
    ["cqw", "cqi"],
    ["dvh", "dvb"],
    ["dvw", "dvi"],
    ["lvh", "lvb"],
    ["lvw", "lvi"],
    ["svh", "svb"],
    ["svw", "svi"],
    ["vh", "vb"],
    ["vw", "vi"],
]);
//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------
export default /** @satisfies {PreferLogicalPropertiesRuleDefinition} */ ({
    meta: {
        type: "problem",
        hasSuggestions: true,
        docs: {
            description: "Enforce the use of logical properties",
            url: "https://github.com/eslint/css/blob/main/docs/rules/prefer-logical-properties.md",
        },
        schema: [
            {
                type: "object",
                properties: {
                    allowProperties: {
                        type: "array",
                        items: {
                            enum: Array.from(propertiesReplacements.keys()),
                        },
                        uniqueItems: true,
                    },
                    allowUnits: {
                        type: "array",
                        items: {
                            enum: Array.from(unitReplacements.keys()),
                        },
                        uniqueItems: true,
                    },
                },
                additionalProperties: false,
            },
        ],
        defaultOptions: [
            {
                allowProperties: [],
                allowUnits: [],
            },
        ],
        messages: {
            notLogicalProperty: "Expected logical property '{{replacement}}' instead of '{{property}}'.",
            notLogicalValue: "Expected logical value '{{replacement}}' instead of '{{value}}'.",
            notLogicalUnit: "Expected logical unit '{{replacement}}' instead of '{{unit}}'.",
            replaceWithLogicalProperty: "Replace '{{property}}' with logical property '{{replacement}}'.",
            replaceWithLogicalValue: "Replace '{{value}}' with logical value '{{replacement}}'.",
            replaceWithLogicalUnit: "Replace '{{unit}}' with logical unit '{{replacement}}'.",
        },
    },
    create(context) {
        const [{ allowProperties, allowUnits }] = context.options;
        return {
            Declaration(node) {
                const parent = context.sourceCode.getParent(node);
                if (parent.type === "SupportsDeclaration") {
                    return;
                }
                const propertyReplacement = propertiesReplacements.get(node.property);
                if (propertyReplacement &&
                    !allowProperties.includes(node.property)) {
                    context.report({
                        loc: node.loc,
                        messageId: "notLogicalProperty",
                        data: {
                            property: node.property,
                            replacement: propertyReplacement,
                        },
                        suggest: [
                            {
                                messageId: "replaceWithLogicalProperty",
                                data: {
                                    property: node.property,
                                    replacement: propertyReplacement,
                                },
                                fix(fixer) {
                                    return fixer.replaceTextRange([
                                        node.loc.start.offset,
                                        node.loc.start.offset +
                                            node.property.length,
                                    ], propertyReplacement);
                                },
                            },
                        ],
                    });
                }
                const valueReplacements = propertyValuesReplacements.get(node.property);
                if (valueReplacements &&
                    node.value.type === "Value" &&
                    node.value.children[0].type === "Identifier") {
                    const identifier = node.value.children[0];
                    const nodeValue = identifier.name;
                    const valueReplacement = valueReplacements[nodeValue];
                    if (valueReplacement) {
                        context.report({
                            loc: identifier.loc,
                            messageId: "notLogicalValue",
                            data: {
                                value: nodeValue,
                                replacement: valueReplacement,
                            },
                            suggest: [
                                {
                                    messageId: "replaceWithLogicalValue",
                                    data: {
                                        value: nodeValue,
                                        replacement: valueReplacement,
                                    },
                                    fix(fixer) {
                                        return fixer.replaceText(identifier, valueReplacement);
                                    },
                                },
                            ],
                        });
                    }
                }
            },
            Dimension(node) {
                const unitReplacement = unitReplacements.get(node.unit);
                if (unitReplacement && !allowUnits.includes(node.unit)) {
                    context.report({
                        loc: node.loc,
                        messageId: "notLogicalUnit",
                        data: {
                            unit: node.unit,
                            replacement: unitReplacement,
                        },
                        suggest: [
                            {
                                messageId: "replaceWithLogicalUnit",
                                data: {
                                    unit: node.unit,
                                    replacement: unitReplacement,
                                },
                                fix(fixer) {
                                    return fixer.replaceText(node, node.value + unitReplacement);
                                },
                            },
                        ],
                    });
                }
            },
        };
    },
});
