import { arrayAt, objectEntries, objectHasOwn } from "ts-extras";
import { ruleCreator } from "../_internal/rule-creator.js";
import { createReplacementRuleInfo, withDeprecatedRuleLifecycle, } from "../_internal/rule-deprecation.js";
const isNode = (value) => typeof value === "object" && value !== null && objectHasOwn(value, "type");
const containsThisExpression = (root) => {
    let stack = [root];
    const enqueue = (value) => {
        if (isNode(value)) {
            stack = [...stack, value];
        }
    };
    while (stack.length > 0) {
        const node = arrayAt(stack, -1);
        stack = stack.slice(0, -1);
        if (node === undefined) {
            continue;
        }
        if (node.type === "ThisExpression") {
            return true;
        }
        for (const [key, child] of objectEntries(node)) {
            if (key === "loc" || key === "parent" || key === "range") {
                continue;
            }
            if (Array.isArray(child)) {
                for (const item of child) {
                    enqueue(item);
                }
            }
            else {
                enqueue(child);
            }
        }
    }
    return false;
};
const hasThisParameter = (node) => {
    const [firstParameter] = node.value.params;
    return (firstParameter?.type === "Identifier" && firstParameter.name === "this");
};
const usesThisExpression = (node) => node.value.body === null ? false : containsThisExpression(node.value.body);
/**
 * Require non-static class methods to reference `this`.
 */
const rule = ruleCreator({
    create: (context) => ({
        MethodDefinition: (node) => {
            if (node.kind !== "method" ||
                node.static ||
                node.value.body === null ||
                hasThisParameter(node) ||
                usesThisExpression(node)) {
                return;
            }
            context.report({
                messageId: "forbidden",
                node,
            });
        },
    }),
    defaultOptions: [],
    meta: {
        deprecated: true,
        docs: {
            deprecated: true,
            description: "require non-static class methods to reference `this`.",
            frozen: true,
            recommended: false,
            url: "https://nick2bad4u.github.io/eslint-plugin-etc-misc/docs/rules/typescript-class-methods-use-this",
        },
        hasSuggestions: false,
        messages: {
            forbidden: "Class method should use `this` or declare a `this` parameter.",
        },
        schema: [],
        type: "suggestion",
    },
    name: "typescript/class-methods-use-this",
});
/**
 * Wrapper rule with explicit lifecycle metadata and replacement mapping.
 */
const deprecatedRule = withDeprecatedRuleLifecycle(rule, {
    message: "Deprecated in favor of @typescript-eslint/class-methods-use-this.",
    replacedBy: [
        createReplacementRuleInfo({
            plugin: {
                name: "@typescript-eslint",
                url: "https://typescript-eslint.io/",
            },
            rule: {
                name: "class-methods-use-this",
                url: "https://typescript-eslint.io/rules/class-methods-use-this",
            },
        }),
    ],
    ruleId: "typescript/class-methods-use-this",
});
export default deprecatedRule;
//# sourceMappingURL=typescript-class-methods-use-this.js.map