/**
 * @fileoverview Rule to disallow `!important` flags.
 * @author thecalamiity
 * @author Yann Bertrand
 */
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
/**
 * @import { CSSRuleDefinition } from "../types.js"
 * @typedef {"unexpectedImportant" | "removeImportant"} NoImportantMessageIds
 * @typedef {CSSRuleDefinition<{ RuleOptions: [], MessageIds: NoImportantMessageIds }>} NoImportantRuleDefinition
 */
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
const importantPattern = /!\s*important/iu;
const commentPattern = /\/\*[\s\S]*?\*\//gu;
const trailingWhitespacePattern = /\s*$/u;
//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------
export default /** @satisfies {NoImportantRuleDefinition} */ ({
    meta: {
        type: "problem",
        hasSuggestions: true,
        docs: {
            description: "Disallow !important flags",
            recommended: true,
            url: "https://github.com/eslint/css/blob/main/docs/rules/no-important.md",
        },
        messages: {
            unexpectedImportant: "Unexpected !important flag found.",
            removeImportant: "Remove !important flag.",
        },
    },
    create(context) {
        const { sourceCode } = context;
        return {
            Declaration(node) {
                if (node.important) {
                    const declarationText = sourceCode.getText(node);
                    const textWithoutComments = declarationText.replace(commentPattern, 
                    /* eslint-disable-next-line require-unicode-regexp -- we want to replace each code unit with a space */
                    match => match.replace(/[^\r\n\f]/g, " "));
                    const importantMatch = importantPattern.exec(textWithoutComments);
                    const importantStartOffset = importantMatch.index;
                    const importantEndOffset = importantStartOffset + importantMatch[0].length;
                    const nodeStartOffset = node.loc.start.offset;
                    context.report({
                        loc: {
                            start: sourceCode.getLocFromIndex(nodeStartOffset + importantStartOffset),
                            end: sourceCode.getLocFromIndex(nodeStartOffset + importantEndOffset),
                        },
                        messageId: "unexpectedImportant",
                        suggest: [
                            {
                                messageId: "removeImportant",
                                fix(fixer) {
                                    // Find any trailing whitespace before the `!important`
                                    const whitespaceEndOffset = declarationText
                                        .slice(0, importantStartOffset)
                                        .search(trailingWhitespacePattern);
                                    return fixer.removeRange([
                                        nodeStartOffset + whitespaceEndOffset,
                                        nodeStartOffset + importantEndOffset,
                                    ]);
                                },
                            },
                        ],
                    });
                }
            },
        };
    },
});
