import { Property } from "@superblocksteam/library-shared";
import { getErrorMeta, getLogger } from "../util/logger.js";
import { doesNodePathHaveBinding } from "./bindings.js";
import { isDimNode, isComputedNode, isFunctionNode, isSbTemplateNode, } from "./jsx-utils.js";
import { processJSXElement } from "./jsx.js";
import { parsingRegistry } from "./type-parsing-registry.js";
import { nodeToValue, getIdentiferName } from "./util.js";
export function parseNodeProperties(node, context) {
    const logger = getLogger();
    const expression = node.isJSXExpressionContainer()
        ? node.get("expression")
        : node;
    if (expression.isJSXElement()) {
        return Property.Jsx(processJSXElement(expression, undefined, context, false));
    }
    if (expression.isJSXFragment()) {
        return parseJSXFragment(expression, context);
    }
    if (isSbTemplateNode(expression)) {
        const templateString = parsingRegistry.TEMPLATE.toValue(expression);
        // when a computed prop fails to parse, we write the expression as a template literal
        // to avoid any runtime errors in the app. When sending the info to the editor, we
        // need to parse it back as a computed property so that is handled correctly in the ui.
        if (node.node.extra?.computedFallback) {
            return Property.Computed(templateString);
        }
        return templateString != null ? Property.Template(templateString) : null;
    }
    if (isComputedNode(expression)) {
        const computedBody = parsingRegistry.COMPUTED.toValue(expression);
        return Property.Computed(computedBody);
    }
    // Handle arrow functions or regular functions that return JSX (render props)
    // This must come before isFunctionNode to extract JSX from inside functions
    if (expression.isArrowFunctionExpression() ||
        expression.isFunctionExpression()) {
        const body = expression.get("body");
        if (!Array.isArray(body)) {
            if (body.isJSXElement()) {
                return Property.Jsx(processJSXElement(body, undefined, context, false));
            }
            // JSX in parentheses: (value) => (<div>{value}</div>)
            if (body.isJSXFragment()) {
                return parseJSXFragment(body, context);
            }
            // Block body with return statement: (value) => { return <div>{value}</div> }
            if (body.isBlockStatement()) {
                let foundJsx = null;
                body.traverse({
                    ReturnStatement(returnPath) {
                        const returnArg = returnPath.get("argument");
                        if (returnArg.isJSXElement()) {
                            foundJsx = Property.Jsx(processJSXElement(returnArg, undefined, context, false));
                            returnPath.stop();
                        }
                        else if (returnArg.isJSXFragment()) {
                            foundJsx = parseJSXFragment(returnArg, context);
                            returnPath.stop();
                        }
                    },
                });
                if (foundJsx) {
                    return foundJsx;
                }
            }
            // Template literal return: (value) => `Hello ${value}`
            if (body.isTemplateLiteral()) {
                const templateString = parsingRegistry.TEMPLATE.toValue(body);
                if (templateString) {
                    return Property.Template(templateString);
                }
            }
        }
    }
    if (isFunctionNode(expression)) {
        const functionSteps = parsingRegistry.FUNCTION.toValue(expression);
        return functionSteps ? Property.Function(functionSteps) : null;
    }
    if (isDimNode(expression)) {
        const dimension = parsingRegistry.DIMENSION.toValue(expression);
        return dimension ? Property.Dimension(dimension) : null;
    }
    if (expression.isStringLiteral() || expression.isNumericLiteral()) {
        return Property.Static(expression.node.value);
    }
    if (expression.isIdentifier() && context.jsxVariables?.length > 0) {
        const identifierName = getIdentiferName(expression.node);
        const jsxVariable = context.jsxVariables.find(({ binding }) => {
            const bindingName = getIdentiferName(binding.identifier);
            return bindingName === identifierName;
        });
        if (jsxVariable) {
            return Property.Jsx(processJSXElement(jsxVariable.jsxElement, undefined, context, false));
        }
    }
    if (doesNodePathHaveBinding(node, true)) {
        return Property.Expression(expression.toString());
    }
    if (expression.isCallExpression()) {
        return Property.Computed(expression.toString());
    }
    if (expression.isTemplateLiteral()) {
        const templateString = parsingRegistry.TEMPLATE.toValue(expression);
        if (templateString) {
            return Property.Template(templateString);
        }
    }
    if (expression.isObjectExpression()) {
        const parentObject = {};
        expression.get("properties").forEach((property) => {
            if (!property.isObjectProperty()) {
                return;
            }
            const key = getIdentiferName(property.get("key").node);
            if (!key) {
                logger.warn(`Could not get key for object property: ${property.toString()}`);
                return;
            }
            const value = property.get("value");
            const parsedValue = parseNodeProperties(value, context);
            if (parsedValue) {
                parentObject[key] = parsedValue;
            }
        });
        return Property.Static(parentObject);
    }
    if (expression.isMemberExpression()) {
        const value = nodeToValue(expression.node);
        return Property.Computed(value);
    }
    try {
        const value = nodeToValue(expression.node);
        if (value !== null || expression.node.type === "NullLiteral") {
            return Property.Static(value);
        }
    }
    catch (e) {
        logger.error(`Error parsing expression: ${expression}`, getErrorMeta(e));
    }
    // we assume that anything that is not a static value is js code
    return Property.Computed(expression.toString());
}
function parseJSXFragment(fragment, context) {
    const children = [];
    fragment.get("children").forEach((child) => {
        if (child.isJSXElement()) {
            const parsedElement = processJSXElement(child, undefined, context, false);
            if (parsedElement) {
                children.push(parsedElement);
            }
        }
        else if (child.isJSXExpressionContainer()) {
            const expression = child.get("expression");
            if (expression.isJSXElement()) {
                const parsedElement = processJSXElement(expression, undefined, context, false);
                if (parsedElement) {
                    children.push(parsedElement);
                }
            }
        }
        // Note: We're ignoring JSXText and other types for now,
        // but they could be handled here if needed
    });
    return Property.Jsx(children);
}
//# sourceMappingURL=properties.js.map