import { parse } from "@babel/parser";
function isNode(value) {
    return !!value && typeof value === "object";
}
function getPropertyName(node) {
    if (!isNode(node)) {
        return undefined;
    }
    if (node.type === "Identifier" && typeof node.name === "string") {
        return node.name;
    }
    if (node.type === "StringLiteral" && typeof node.value === "string") {
        return node.value;
    }
    return undefined;
}
function getLiteralString(node) {
    if (!isNode(node)) {
        return undefined;
    }
    if (node.type === "StringLiteral" && typeof node.value === "string") {
        return node.value;
    }
    if (node.type === "TemplateLiteral" &&
        Array.isArray(node.expressions) &&
        node.expressions.length === 0 &&
        Array.isArray(node.quasis) &&
        node.quasis.length === 1) {
        const quasi = node.quasis[0];
        if (isNode(quasi) &&
            isNode(quasi.value) &&
            typeof quasi.value.cooked === "string") {
            return quasi.value.cooked;
        }
    }
    return undefined;
}
function getCalleeName(node) {
    if (!isNode(node)) {
        return undefined;
    }
    if (node.type === "Identifier" && typeof node.name === "string") {
        return node.name;
    }
    if (node.type === "MemberExpression") {
        return getPropertyName(node.property);
    }
    return undefined;
}
function unwrapExpression(node) {
    if (!isNode(node)) {
        return undefined;
    }
    if (node.type === "TSAsExpression" ||
        node.type === "TSTypeAssertion" ||
        node.type === "ParenthesizedExpression") {
        return unwrapExpression(node.expression);
    }
    return node;
}
function buildVariableInitializerMap(programBody) {
    const initializers = new Map();
    for (const statement of programBody) {
        if (!isNode(statement) ||
            statement.type !== "VariableDeclaration" ||
            !Array.isArray(statement.declarations)) {
            continue;
        }
        for (const declaration of statement.declarations) {
            if (!isNode(declaration)) {
                continue;
            }
            const identifier = declaration.id;
            if (!isNode(identifier) ||
                identifier.type !== "Identifier" ||
                typeof identifier.name !== "string") {
                continue;
            }
            initializers.set(identifier.name, declaration.init);
        }
    }
    return initializers;
}
function resolveApiConfigObject(expression, variableInitializers) {
    const resolvedExpression = unwrapExpression(expression);
    if (!resolvedExpression) {
        return undefined;
    }
    if (resolvedExpression.type === "Identifier" &&
        typeof resolvedExpression.name === "string") {
        return resolveApiConfigObject(variableInitializers.get(resolvedExpression.name), variableInitializers);
    }
    if (resolvedExpression.type !== "CallExpression" ||
        getCalleeName(resolvedExpression.callee) !== "api" ||
        !Array.isArray(resolvedExpression.arguments)) {
        return undefined;
    }
    const config = unwrapExpression(resolvedExpression.arguments[0]);
    if (!config || config.type !== "ObjectExpression") {
        return undefined;
    }
    return config;
}
function findExportedApiConfigObject(programBody) {
    const variableInitializers = buildVariableInitializerMap(programBody);
    for (const statement of programBody) {
        if (!isNode(statement)) {
            continue;
        }
        if (statement.type === "ExportDefaultDeclaration") {
            return resolveApiConfigObject(statement.declaration, variableInitializers);
        }
        if (statement.type === "ExportNamedDeclaration" &&
            Array.isArray(statement.specifiers)) {
            for (const specifier of statement.specifiers) {
                if (!isNode(specifier) ||
                    specifier.type !== "ExportSpecifier" ||
                    getPropertyName(specifier.exported) !== "default") {
                    continue;
                }
                return resolveApiConfigObject(specifier.local, variableInitializers);
            }
        }
    }
    return undefined;
}
function buildVariableReferenceMap(programBody, stringLiterals) {
    const refs = new Map();
    for (const statement of programBody) {
        if (!isNode(statement) ||
            statement.type !== "VariableDeclaration" ||
            !Array.isArray(statement.declarations)) {
            continue;
        }
        for (const declaration of statement.declarations) {
            if (!isNode(declaration)) {
                continue;
            }
            const identifier = declaration.id;
            const init = declaration.init;
            if (!isNode(identifier) ||
                identifier.type !== "Identifier" ||
                typeof identifier.name !== "string") {
                continue;
            }
            if (!isNode(init) ||
                init.type !== "CallExpression" ||
                !Array.isArray(init.arguments)) {
                continue;
            }
            const pluginId = getCalleeName(init.callee);
            const id = resolveStringArgument(init.arguments[0], stringLiterals);
            if (!pluginId || !id) {
                continue;
            }
            refs.set(identifier.name, { pluginId, id });
        }
    }
    return refs;
}
function buildStringLiteralMap(programBody) {
    const literals = new Map();
    for (const statement of programBody) {
        if (!isNode(statement) ||
            statement.type !== "VariableDeclaration" ||
            !Array.isArray(statement.declarations)) {
            continue;
        }
        for (const declaration of statement.declarations) {
            if (!isNode(declaration)) {
                continue;
            }
            const identifier = declaration.id;
            if (!isNode(identifier) ||
                identifier.type !== "Identifier" ||
                typeof identifier.name !== "string") {
                continue;
            }
            const value = getLiteralString(declaration.init);
            if (value !== undefined) {
                literals.set(identifier.name, value);
            }
        }
    }
    return literals;
}
function resolveStringArgument(node, stringLiterals) {
    const literal = getLiteralString(node);
    if (literal !== undefined) {
        return literal;
    }
    if (isNode(node) &&
        node.type === "Identifier" &&
        typeof node.name === "string") {
        return stringLiterals.get(node.name);
    }
    return undefined;
}
/**
 * Extract integration declarations from an SDK API source file.
 *
 * Anchors on the exported `api({ ... })` config object and extracts
 * integrations from its `integrations` property.
 *
 * Supports inline declarations:
 * `integrations: { db: postgres("uuid") }`
 *
 * Top-level variable references:
 * `const DB = postgres("uuid"); integrations: { db: DB }`
 *
 * And string variable indirection:
 * `const ID = "uuid"; integrations: { db: postgres(ID) }`
 */
export function extractIntegrationsFromSource(source) {
    let ast;
    try {
        ast = parse(source, {
            sourceType: "module",
            plugins: ["typescript"],
        });
    }
    catch {
        return [];
    }
    const stringLiterals = buildStringLiteralMap(ast.program.body);
    const variableRefs = buildVariableReferenceMap(ast.program.body, stringLiterals);
    const apiConfig = findExportedApiConfigObject(ast.program.body);
    if (!apiConfig) {
        return [];
    }
    const results = [];
    const apiConfigProperties = Array.isArray(apiConfig.properties)
        ? apiConfig.properties
        : [];
    const integrationsProperty = apiConfigProperties.find((property) => isNode(property) &&
        property.type === "ObjectProperty" &&
        getPropertyName(property.key) === "integrations");
    if (!isNode(integrationsProperty) ||
        !isNode(integrationsProperty.value) ||
        integrationsProperty.value.type !== "ObjectExpression") {
        return [];
    }
    const properties = Array.isArray(integrationsProperty.value.properties)
        ? integrationsProperty.value.properties
        : [];
    for (const property of properties) {
        if (!isNode(property) || property.type !== "ObjectProperty") {
            continue;
        }
        const key = getPropertyName(property.key);
        if (!key) {
            continue;
        }
        if (isNode(property.value) &&
            property.value.type === "CallExpression" &&
            Array.isArray(property.value.arguments)) {
            const pluginId = getCalleeName(property.value.callee);
            const id = resolveStringArgument(property.value.arguments[0], stringLiterals);
            if (pluginId && id) {
                results.push({ key, pluginId, id });
            }
            continue;
        }
        if (isNode(property.value) &&
            property.value.type === "Identifier" &&
            typeof property.value.name === "string") {
            const resolved = variableRefs.get(property.value.name);
            if (resolved) {
                results.push({ key, ...resolved });
            }
        }
    }
    return results;
}
export function extractIntegrationIdsFromSource(source) {
    return extractIntegrationsFromSource(source).map((integration) => integration.id);
}
//# sourceMappingURL=extract-api-integrations.js.map