import { arrayFirst } from "ts-extras";
import { ruleCreator } from "../_internal/rule-creator.js";
const getAssignmentTargetIdentifier = (node) => {
    if (node.left.type === "Identifier") {
        return node.left;
    }
    return undefined;
};
const getScopeVariable = (sourceCode, identifier) => {
    let scope = sourceCode.getScope(identifier);
    while (scope !== null) {
        const variable = scope.set.get(identifier.name);
        if (variable !== undefined) {
            return variable;
        }
        scope = scope.upper;
    }
    return undefined;
};
const isParameterVariable = (variable) => variable?.defs.some((definition) => definition.type === "Parameter") ??
    false;
const isInFirstFunctionExpressionStatement = (sourceCode, node) => {
    const ancestors = sourceCode.getAncestors(node);
    let enclosingFunction = null;
    for (let index = ancestors.length - 1; index >= 0; index -= 1) {
        const ancestor = ancestors[index];
        if (ancestor === undefined) {
            continue;
        }
        if (ancestor.type === "ArrowFunctionExpression" ||
            ancestor.type === "FunctionDeclaration" ||
            ancestor.type === "FunctionExpression") {
            enclosingFunction = ancestor;
            break;
        }
    }
    if (enclosingFunction?.body.type !== "BlockStatement") {
        return false;
    }
    const [firstStatement] = enclosingFunction.body.body;
    if (firstStatement?.type !== "ExpressionStatement") {
        return false;
    }
    return (arrayFirst(node.range) >= arrayFirst(firstStatement.range) &&
        node.range[1] <= firstStatement.range[1]);
};
/**
 * Disallow parameter reassignment, except in the first expression statement of
 * a function body.
 */
const rule = ruleCreator({
    create: (context) => {
        const { sourceCode } = context;
        const reportIfParameterReassignment = (identifier) => {
            if (isInFirstFunctionExpressionStatement(sourceCode, identifier)) {
                return;
            }
            const variable = getScopeVariable(sourceCode, identifier);
            if (!isParameterVariable(variable)) {
                return;
            }
            context.report({
                messageId: "forbidden",
                node: identifier,
            });
        };
        return {
            AssignmentExpression: (node) => {
                const identifier = getAssignmentTargetIdentifier(node);
                if (identifier === undefined) {
                    return;
                }
                reportIfParameterReassignment(identifier);
            },
            UpdateExpression: (node) => {
                if (node.argument.type !== "Identifier") {
                    return;
                }
                reportIfParameterReassignment(node.argument);
            },
        };
    },
    defaultOptions: [],
    meta: {
        deprecated: false,
        docs: {
            deprecated: false,
            description: "disallow parameter reassignment except in the first expression statement of a function body.",
            frozen: false,
            recommended: false,
            url: "https://nick2bad4u.github.io/eslint-plugin-etc-misc/docs/rules/no-param-reassign",
        },
        hasSuggestions: false,
        messages: {
            forbidden: "Do not reassign function parameters outside the first expression statement in the function body.",
        },
        schema: [],
        type: "problem",
    },
    name: "no-param-reassign",
});
export default rule;
//# sourceMappingURL=no-param-reassign.js.map