import * as ts from "typescript";
const isDynamicImportCall = (node) => node.expression.kind === ts.SyntaxKind.ImportKeyword;
export const getDynamicImportLiteralSpecifier = (node) => {
    if (!isDynamicImportCall(node))
        return undefined;
    if (node.arguments.length !== 1)
        return undefined;
    const [arg] = node.arguments;
    return arg && ts.isStringLiteral(arg) ? arg.text : undefined;
};
export const isClosedWorldDynamicImportSpecifier = (specifier) => specifier.startsWith("./") || specifier.startsWith("../");
export const isSideEffectOnlyDynamicImport = (node) => {
    const parent = node.parent;
    return (ts.isAwaitExpression(parent) &&
        parent.expression === node &&
        ts.isExpressionStatement(parent.parent));
};
const normalizeFilePath = (filePath) => filePath.replace(/\\/g, "/");
const resolveDynamicImportTargetPath = (node, containingFile, compilerOptions) => {
    const specifier = getDynamicImportLiteralSpecifier(node);
    if (!specifier || !isClosedWorldDynamicImportSpecifier(specifier)) {
        return undefined;
    }
    const resolved = ts.resolveModuleName(specifier, containingFile, compilerOptions, ts.sys);
    const targetPath = resolved.resolvedModule?.resolvedFileName;
    if (!targetPath || targetPath.endsWith(".d.ts")) {
        return undefined;
    }
    return normalizeFilePath(targetPath);
};
const getModuleSymbol = (sourceFile, checker) => {
    const direct = checker.getSymbolAtLocation(sourceFile);
    if (direct)
        return direct;
    const withSymbol = sourceFile;
    return withSymbol.symbol;
};
const getSupportedValueExportDeclaration = (exportSymbol, checker, sourceFilesByPath) => {
    const actualSymbol = exportSymbol.flags & ts.SymbolFlags.Alias
        ? checker.getAliasedSymbol(exportSymbol)
        : exportSymbol;
    if ((actualSymbol.flags & ts.SymbolFlags.Value) === 0) {
        return null;
    }
    for (const declaration of actualSymbol.getDeclarations() ?? []) {
        if (ts.isFunctionDeclaration(declaration) &&
            declaration.name &&
            sourceFilesByPath.has(normalizeFilePath(declaration.getSourceFile().fileName))) {
            return {
                exportName: exportSymbol.getName(),
                ownerFilePath: normalizeFilePath(declaration.getSourceFile().fileName),
                memberName: declaration.name.text,
                memberKind: "function",
                declarationName: declaration.name,
            };
        }
        if (ts.isVariableDeclaration(declaration) &&
            ts.isIdentifier(declaration.name) &&
            sourceFilesByPath.has(normalizeFilePath(declaration.getSourceFile().fileName))) {
            return {
                exportName: exportSymbol.getName(),
                ownerFilePath: normalizeFilePath(declaration.getSourceFile().fileName),
                memberName: declaration.name.text,
                memberKind: "variable",
                declarationName: declaration.name,
            };
        }
    }
    return undefined;
};
export const resolveDynamicImportNamespace = (node, containingFile, ctx) => {
    const specifier = getDynamicImportLiteralSpecifier(node);
    if (!specifier) {
        return {
            ok: false,
            reason: "Dynamic import specifier must be a string literal.",
        };
    }
    if (!isClosedWorldDynamicImportSpecifier(specifier)) {
        return {
            ok: false,
            reason: "Dynamic import is only supported for closed-world local specifiers ('./' or '../').",
        };
    }
    const resolvedFilePath = resolveDynamicImportTargetPath(node, containingFile, ctx.compilerOptions);
    if (!resolvedFilePath) {
        return {
            ok: false,
            reason: "Dynamic import target could not be resolved to a local TypeScript source file.",
        };
    }
    const targetSourceFile = ctx.sourceFilesByPath.get(resolvedFilePath);
    if (!targetSourceFile) {
        return {
            ok: false,
            reason: "Dynamic import target is not part of the current closed-world source graph.",
        };
    }
    const moduleSymbol = getModuleSymbol(targetSourceFile, ctx.checker);
    if (!moduleSymbol) {
        return {
            ok: false,
            reason: "Dynamic import target does not expose a module symbol for deterministic export analysis.",
        };
    }
    const exportSymbols = [...ctx.checker.getExportsOfModule(moduleSymbol)].sort((left, right) => left.getName().localeCompare(right.getName()));
    const entries = [];
    for (const exportSymbol of exportSymbols) {
        const entry = getSupportedValueExportDeclaration(exportSymbol, ctx.checker, ctx.sourceFilesByPath);
        if (entry === null) {
            continue;
        }
        if (entry === undefined) {
            return {
                ok: false,
                reason: `Dynamic import namespace for '${specifier}' requires runtime exports to lower to named function/variable members. Unsupported export: '${exportSymbol.getName()}'.`,
            };
        }
        entries.push(entry);
    }
    return {
        ok: true,
        specifier,
        resolvedFilePath,
        entries,
    };
};
export const collectClosedWorldDynamicImportSites = (sourceFile) => {
    const sites = [];
    const visit = (node) => {
        if (ts.isCallExpression(node)) {
            const specifier = getDynamicImportLiteralSpecifier(node);
            if (specifier && isClosedWorldDynamicImportSpecifier(specifier)) {
                sites.push({ node, specifier });
            }
        }
        ts.forEachChild(node, visit);
    };
    visit(sourceFile);
    return sites;
};
//# sourceMappingURL=dynamic-import.js.map