import * as t from "@babel/types";
import { getIdentiferName, nodeToValue, extractNameFromBindAttribute, } from "./parsing/util.js";
import { refactorNameInBlocks } from "./refactor/blocks.js";
import { renameEntityInJavascript } from "./refactor/javascript.js";
import { traverse } from "./traverse.js";
import { getLogger } from "./util/logger.js";
const traverseFn = traverse.default || traverse;
export class RenameManager {
    constructor() { }
    async renameComponent({ widgetNode, newName, ast, renameInAst = true, }) {
        if (!widgetNode) {
            return;
        }
        const bindAttribute = widgetNode
            .get("attributes")
            .find((n) => n.isJSXAttribute() && getIdentiferName(n.node.name) === "bind");
        const nameAttribute = widgetNode
            .get("attributes")
            .find((n) => n.isJSXAttribute() && getIdentiferName(n.node.name) === "name");
        let oldName = null;
        if (bindAttribute) {
            oldName = extractNameFromBindAttribute(bindAttribute.node);
            if (oldName) {
                const bindValue = bindAttribute.node.value;
                if (bindValue && t.isJSXExpressionContainer(bindValue)) {
                    const expression = bindValue.expression;
                    // Handle simple case: bind={x} => bind={newName}
                    if (t.isIdentifier(expression)) {
                        bindAttribute.node.value = t.jsxExpressionContainer(t.identifier(newName));
                    }
                    // Handle nested case: bind={x.y} or bind={x.y.z}
                    else if (t.isMemberExpression(expression)) {
                        // Extract parent bindings for tracking
                        const parentParts = [];
                        let current = expression;
                        // Traverse the member expression to collect all parts
                        while (t.isMemberExpression(current)) {
                            if (t.isIdentifier(current.property)) {
                                parentParts.unshift(current.property.name);
                            }
                            current = current.object;
                        }
                        // Add the root identifier
                        if (t.isIdentifier(current)) {
                            parentParts.unshift(current.name);
                        }
                        // Create new expression with updated final property
                        const newExpression = t.cloneNode(expression, true);
                        // Find the final property and replace it
                        let finalMember = newExpression;
                        while (t.isMemberExpression(finalMember) &&
                            t.isMemberExpression(finalMember.property)) {
                            finalMember = finalMember.property;
                        }
                        // Replace the final property name
                        if (t.isMemberExpression(finalMember) &&
                            t.isIdentifier(finalMember.property)) {
                            finalMember.property = t.identifier(newName);
                        }
                        bindAttribute.node.value = t.jsxExpressionContainer(newExpression);
                    }
                }
            }
        }
        else if (!nameAttribute) {
            widgetNode.pushContainer("attributes", t.jsxAttribute(t.jsxIdentifier("bind"), t.jsxExpressionContainer(t.identifier(newName))));
            return;
        }
        else {
            oldName = nameAttribute.node.value
                ? nodeToValue(nameAttribute.node.value)
                : null;
            // switch to a bind attribute
            widgetNode.pushContainer("attributes", t.jsxAttribute(t.jsxIdentifier("bind"), t.jsxExpressionContainer(t.identifier(newName))));
            // remove the name attribute
            nameAttribute.remove();
        }
        if (!oldName) {
            getLogger().error("Existing widget name is not found");
            return;
        }
        if (renameInAst) {
            this.renameIdentifierInAST({
                oldName,
                newName,
                ast,
            });
        }
        return;
    }
    async renameEntityInApis({ oldName, newName, apis, parentBinding, }) {
        for (const { api } of apis) {
            const blocks = api.apiPb.blocks ?? [];
            await refactorNameInBlocks({
                blocks,
                oldName,
                newName,
                namespace: parentBinding,
            });
        }
    }
    renameIdentifierInAST({ oldName, newName, ast, parentBinding, }) {
        const elementVisitor = {
            TemplateLiteral(path) {
                const expressions = path.get("expressions");
                if (expressions.length > 0) {
                    renameEntityInJavascript({
                        oldName,
                        newName,
                        nodePath: path,
                        checkFunctionBinding: false,
                        parentBinding,
                    });
                }
            },
            Function(path) {
                renameEntityInJavascript({
                    oldName,
                    newName,
                    nodePath: path,
                    checkFunctionBinding: true,
                    parentBinding,
                });
            },
            ArrayExpression(path) {
                if (t.isCallExpression(path.parentPath.node)) {
                    renameEntityInJavascript({
                        oldName,
                        newName,
                        nodePath: path,
                        checkFunctionBinding: true,
                        parentBinding,
                    });
                }
            },
            CallExpression(path) {
                renameEntityInJavascript({
                    oldName,
                    newName,
                    nodePath: path,
                    checkFunctionBinding: true,
                    parentBinding,
                });
            },
        };
        traverseFn(ast, {
            JSXOpeningElement(path) {
                path.traverse(elementVisitor);
                path.skip();
            },
            CallExpression(path) {
                // we want to traverse into each StateVar/Timer/SbEvent
                if ((path.node.callee.type === "Identifier" &&
                    (path.node.callee.name === "StateVar" ||
                        path.node.callee.name === "Timer")) ||
                    (path.node.callee.type === "MemberExpression" &&
                        path.node.arguments.length > 0)) {
                    path.traverse(elementVisitor);
                    path.skip();
                }
            },
        });
    }
}
//# sourceMappingURL=rename-manager.js.map