"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseExportIdentifiers = exports.parseInterfaceTypes = exports.resolvePlatformIndex = exports.resolvePlatformIndexFilename = exports.resolveRootInterface = exports.resolveRootIndex = exports.genProxyCode = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const shared_1 = require("@vue/shared");
const utils_1 = require("./utils");
const shared_2 = require("./shared");
const stacktrace_1 = require("./stacktrace");
const IOS_HOOK_CLASS = 'UTSiOSHookProxy';
const ANDROID_HOOK_CLASS = 'UTSAndroidHookProxy';
function isHookClass(name) {
    return name === ANDROID_HOOK_CLASS || name === IOS_HOOK_CLASS;
}
async function genProxyCode(module, options) {
    const { name, is_uni_modules, format, moduleName, moduleType } = options;
    options.inputDir = options.inputDir || process.env.UNI_INPUT_DIR;
    if (!options.meta) {
        options.meta = {
            exports: {},
            types: {},
            typeParams: [],
            components: [],
            customElements: [],
        };
    }
    options.types = await parseInterfaceTypes(module, options);
    options.meta.types = parseMetaTypes(options.types);
    options.meta.typeParams = parseTypeParams(options.types);
    if (options.androidPreprocessor) {
        // 内置 ext-api 需要分平台解析interface
        const androidTypes = await parseInterfaceTypes(module, options, options.androidPreprocessor);
        options.meta.android = {
            typeParams: parseTypeParams(androidTypes),
            types: parseMetaTypes(androidTypes),
        };
    }
    if (options.iosPreprocessor) {
        const iosTypes = await parseInterfaceTypes(module, options, options.iosPreprocessor);
        options.meta.ios = {
            typeParams: parseTypeParams(iosTypes),
            types: parseMetaTypes(iosTypes),
        };
    }
    const components = new Set();
    // 自动补充 VideoElement 导出
    if (options.androidComponents) {
        Object.keys(options.androidComponents).forEach((name) => {
            const className = (process.env.UNI_UTS_MODULE_PREFIX ? 'Uni' : '') +
                (0, shared_1.capitalize)((0, shared_1.camelize)(name)) +
                'Element';
            options.meta.types[className] = 'class';
            if (options.meta?.android?.types) {
                options.meta.android.types[className] = 'class';
            }
            components.add(name);
        });
    }
    if (options.iosComponents) {
        Object.keys(options.iosComponents).forEach((name) => {
            const className = (process.env.UNI_UTS_MODULE_PREFIX ? 'Uni' : '') +
                (0, shared_1.capitalize)((0, shared_1.camelize)(name)) +
                'Element';
            options.meta.types[className] = 'class';
            if (options.meta?.ios?.types) {
                options.meta.ios.types[className] = 'class';
            }
            components.add(name);
        });
    }
    options.meta.components = [...components];
    const decls = await parseModuleDecls(module, options);
    normalizeInterfaceKeepAlive(decls, options.types);
    return `
const { registerUTSInterface, initUTSProxyClass, initUTSProxyFunction, initUTSPackageName, initUTSIndexClassName, initUTSClassName } = uni
const name = '${name}'
const moduleName = '${moduleName || ''}'
const moduleType = '${moduleType || ''}'
const errMsg = \`${utils_1.ERR_MSG_PLACEHOLDER}\`
const is_uni_modules = ${is_uni_modules}
const pkg = /*#__PURE__*/ initUTSPackageName(name, is_uni_modules)
const cls = /*#__PURE__*/ initUTSIndexClassName(name, is_uni_modules)
${format === "cjs" /* FORMATS.CJS */
        ? `
const exports = { __esModule: true }
`
        : ''}
${genComponentsCode(format, options.androidComponents || {}, options.iosComponents || {})}${genCustomElementsCode(format, options.customElements || {})}

${genModuleCode(decls, format, options.pluginRelativeDir, options.meta)}
`;
}
exports.genProxyCode = genProxyCode;
// 查找实现该interface的class中是否有keepAlive方法，有则标记为keepAlive
function normalizeInterfaceKeepAlive(decls, types) {
    const classTypes = types.class;
    if (!classTypes) {
        return;
    }
    const classNames = Object.keys(classTypes);
    decls.forEach((decl) => {
        if (decl.type === 'InterfaceDeclaration') {
            classNames.find((n) => {
                const classMeta = classTypes[n];
                if (classMeta.interfaces && classMeta.interfaces.includes(decl.cls)) {
                    classMeta.keepAliveMethods.forEach((method) => {
                        const jsMethod = method + 'ByJs';
                        if (decl.options.methods[jsMethod]) {
                            decl.options.methods[jsMethod].keepAlive = true;
                        }
                    });
                }
            });
        }
    });
}
function parseMetaTypes(types) {
    let res = {
        uni: types.uni || [],
    };
    Object.keys(types.class).forEach((n) => {
        res[n] = 'class';
    });
    Object.keys(types.fn).forEach((n) => {
        res[n] = 'function';
    });
    Object.keys(types.interface).forEach((n) => {
        res[n] = 'interface';
    });
    Object.keys(types.alias).forEach((n) => {
        res[n] = 'typealias';
    });
    return res;
}
function parseTypeParams(types) {
    let res = [];
    Object.keys(types.class).forEach((n) => {
        if (types.class[n].typeParams) {
            res.push(n);
        }
    });
    return res;
}
function genComponentsCode(format = "es" /* FORMATS.ES */, androidComponents, iosComponents) {
    const codes = [];
    Object.keys(Object.assign({}, androidComponents, iosComponents)).forEach((name) => {
        if (format === "cjs" /* FORMATS.CJS */) {
            codes.push(`exports.${(0, shared_1.capitalize)((0, shared_1.camelize)(name))}Component = {}`);
        }
        else {
            codes.push(`export const ${(0, shared_1.capitalize)((0, shared_1.camelize)(name))}Component = {}`);
        }
    });
    return codes.join('\n');
}
function genCustomElementsCode(format = "es" /* FORMATS.ES */, customElements) {
    const codes = [];
    Object.keys(customElements).forEach((name) => {
        if (format === "cjs" /* FORMATS.CJS */) {
            codes.push(`exports.${(0, shared_1.capitalize)((0, shared_1.camelize)(name))}Element = {}`);
        }
        else {
            codes.push(`export const ${(0, shared_1.capitalize)((0, shared_1.camelize)(name))}Element = {}`);
        }
    });
    if (codes.length) {
        codes.unshift('\n');
    }
    return codes.join('\n');
}
function resolveRootIndex(module, options) {
    const filename = path_1.default.resolve(module, options.is_uni_modules ? 'utssdk' : '', `index${options.extname}`);
    return fs_1.default.existsSync(filename) ? filename : '';
}
exports.resolveRootIndex = resolveRootIndex;
function resolveRootInterface(module, options) {
    const filename = path_1.default.resolve(module, options.is_uni_modules ? 'utssdk' : '', `interface${options.extname}`);
    return fs_1.default.existsSync(filename) ? filename : '';
}
exports.resolveRootInterface = resolveRootInterface;
function resolvePlatformIndexFilename(platform, module, options) {
    return path_1.default.resolve(module, options.is_uni_modules ? 'utssdk' : '', platform, `index${options.extname}`);
}
exports.resolvePlatformIndexFilename = resolvePlatformIndexFilename;
function resolvePlatformIndex(platform, module, options) {
    const filename = resolvePlatformIndexFilename(platform, module, options);
    return fs_1.default.existsSync(filename) ? filename : '';
}
exports.resolvePlatformIndex = resolvePlatformIndex;
function exportDefaultCode(format) {
    return format === "es" /* FORMATS.ES */
        ? 'export default /*#__PURE__*/ '
        : 'exports.default = ';
}
function exportVarCode(format, kind) {
    if (format === "es" /* FORMATS.ES */) {
        return `export ${kind} `;
    }
    return `exports.`;
}
function isClassReturnOptions(value) {
    return ((0, shared_1.isPlainObject)(value) &&
        value.type === 'interface' &&
        (0, shared_1.isString)(value.options));
}
function genClassOptionsCode(options) {
    return JSON.stringify(options, (key, value) => {
        if (key === 'return' && isClassReturnOptions(value)) {
            return { type: 'interface', options: `${value.options}Options` };
        }
        return value;
    });
}
function genModuleCode(decls, format = "es" /* FORMATS.ES */, pluginRelativeDir, meta) {
    const codes = [];
    const exportDefault = exportDefaultCode(format);
    const exportConst = exportVarCode(format, 'const');
    decls.forEach((decl) => {
        if (decl.type === 'InterfaceDeclaration') {
            meta.exports[decl.cls] = {
                type: 'interface',
            };
            codes.push(`registerUTSInterface('${decl.cls}Options',Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, '${decl.cls}ByJsProxy', is_uni_modules) }, ${genClassOptionsCode(decl.options)} ))`);
        }
        else if (decl.type === 'Class') {
            meta.exports[decl.cls] = {
                type: decl.isVar ? 'var' : 'class',
            };
            if (decl.isDefault) {
                codes.push(`${exportDefault}initUTSProxyClass(Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, '${decl.cls}ByJs', is_uni_modules) }, ${genClassOptionsCode(decl.options)} ))`);
            }
            else {
                codes.push(`${exportConst}${decl.cls} = /*#__PURE__*/ initUTSProxyClass(Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, '${decl.cls}ByJs', is_uni_modules) }, ${genClassOptionsCode(decl.options)} ))`);
            }
        }
        else if (decl.type === 'FunctionDeclaration') {
            meta.exports[decl.method] = {
                type: decl.isVar ? 'var' : 'function',
                params: decl.params,
            };
            const returnOptions = decl.return
                ? { type: decl.return.type, options: decl.return.options + 'Options' }
                : '';
            if (decl.isDefault) {
                codes.push(`${exportDefault}initUTSProxyFunction(${decl.async}, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: '${decl.method}ByJs', keepAlive: ${decl.keepAlive}, params: ${JSON.stringify(decl.params)}, return: ${JSON.stringify(returnOptions)}})`);
            }
            else {
                codes.push(`${exportConst}${decl.method} = /*#__PURE__*/ initUTSProxyFunction(${decl.async}, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: '${decl.method}ByJs', keepAlive: ${decl.keepAlive}, params: ${JSON.stringify(decl.params)}, return: ${JSON.stringify(returnOptions)}})`);
            }
        }
        else if (decl.type === 'VariableDeclaration') {
            decl.declarations.forEach((d) => {
                meta.exports[d.id.value] = {
                    type: 'var',
                };
            });
            if (format === "es" /* FORMATS.ES */) {
                codes.push(`export ${decl.kind} ${decl.declarations
                    .map((d) => `${d.id.value} = ${genInitCode(d.init)}`)
                    .join(', ')}`);
            }
            else if (format === "cjs" /* FORMATS.CJS */) {
                codes.push(`${decl.kind} ${decl.declarations
                    .map((d) => `${d.id.value} = ${genInitCode(d.init)}`)
                    .join(', ')}`);
                const exportVar = exportVarCode(format, decl.kind);
                decl.declarations.forEach((d) => {
                    const name = d.id.value;
                    codes.push(`${exportVar}${name} = ${name}`);
                });
            }
        }
    });
    if (format === "cjs" /* FORMATS.CJS */) {
        codes.push(`uni.registerUTSPlugin('${(0, shared_2.normalizePath)(pluginRelativeDir)}', exports)`);
    }
    return codes.join(`\n`);
}
/**
 * 解析接口文件中定义的类型信息
 * @param module
 * @param options
 * @returns
 */
async function parseInterfaceTypes(module, options, preprocessor) {
    const interfaceFilename = resolveRootInterface(module, options);
    if (!interfaceFilename) {
        return {
            interface: {},
            class: {},
            fn: {},
            alias: {},
            uni: [],
        };
    }
    // 懒加载 uts 编译器
    // eslint-disable-next-line no-restricted-globals
    const { parse } = require('@dcloudio/uts');
    let ast = null;
    try {
        const code = fs_1.default.readFileSync(interfaceFilename, 'utf8');
        ast = await parse(preprocessor ? await preprocessor(code, interfaceFilename) : code, {
            filename: (0, utils_1.relative)(interfaceFilename, options.inputDir),
            noColor: !(0, utils_1.isColorSupported)(),
        });
    }
    catch (e) {
        console.error((0, stacktrace_1.parseUTSSyntaxError)(e, options.inputDir));
    }
    return parseAstTypes(ast, true);
}
exports.parseInterfaceTypes = parseInterfaceTypes;
function parseAstTypes(ast, isInterface) {
    const interfaceTypes = {};
    const classTypes = {};
    const fnTypes = {};
    const aliasTypes = {};
    const uniMethods = [];
    const exportNamed = [];
    if (ast) {
        if (isInterface) {
            ast.body.filter((node) => {
                if (node.type === 'ExportNamedDeclaration') {
                    node.specifiers.forEach((s) => {
                        if (s.type === 'ExportSpecifier') {
                            if (s.exported) {
                                if (s.exported.type === 'Identifier') {
                                    exportNamed.push(s.exported.value);
                                }
                            }
                            else {
                                exportNamed.push(s.orig.value);
                            }
                        }
                    });
                }
            });
        }
        ast.body.filter((node) => {
            if (node.type === 'ExportDeclaration') {
                if (node.declaration.type === 'TsTypeAliasDeclaration') {
                    parseTypes(node.declaration, classTypes, fnTypes, aliasTypes);
                }
                else if (node.declaration.type === 'TsInterfaceDeclaration') {
                    interfaceTypes[node.declaration.id.value] = {
                        returned: false,
                        decl: node.declaration,
                    };
                    if (node.declaration.id.value === 'Uni') {
                        node.declaration.body.body.forEach((item) => {
                            if (item.type === 'TsMethodSignature' &&
                                item.key.type === 'Identifier') {
                                uniMethods.push(item.key.value);
                            }
                            else if (item.type === 'TsPropertySignature' &&
                                item.key.type === 'Identifier') {
                                uniMethods.push(item.key.value);
                            }
                        });
                    }
                }
                else if (node.declaration.type === 'ClassDeclaration') {
                    classTypes[node.declaration.identifier.value] = {
                        interfaces: parseImplements(node.declaration),
                        keepAliveMethods: parseKeepAliveMethods(node.declaration),
                    };
                }
            }
            else if (node.type === 'TsTypeAliasDeclaration') {
                if (!isInterface || exportNamed.includes(node.id.value)) {
                    parseTypes(node, classTypes, fnTypes, aliasTypes);
                }
            }
            else if (node.type === 'TsInterfaceDeclaration') {
                interfaceTypes[node.id.value] = {
                    returned: false,
                    decl: node,
                };
            }
            else if (node.type === 'ClassDeclaration') {
                classTypes[node.identifier.value] = {
                    interfaces: parseImplements(node),
                    keepAliveMethods: parseKeepAliveMethods(node),
                };
            }
        });
    }
    return {
        interface: interfaceTypes,
        class: classTypes,
        fn: fnTypes,
        alias: aliasTypes,
        uni: uniMethods,
    };
}
function parseImplements(node) {
    const interfaces = [];
    node.implements.forEach((implement) => {
        if (implement.expression.type === 'Identifier') {
            interfaces.push(implement.expression.value);
        }
    });
    return interfaces;
}
function parseKeepAliveMethods(node) {
    const keepAliveMethods = [];
    node.body.forEach((method) => {
        if (method.type === 'ClassMethod' && method.key.type === 'Identifier') {
            if (parseKeepAlive(method.function)) {
                keepAliveMethods.push(method.key.value);
            }
        }
    });
    return keepAliveMethods;
}
function parseTypes(decl, classTypes, fnTypes, aliasTypes) {
    switch (decl.typeAnnotation.type) {
        // export type ShowLoading = ()=>void
        case 'TsFunctionType':
            const params = createParams(decl.typeAnnotation.params);
            if (params.length) {
                fnTypes[decl.id.value] = params;
            }
            else {
                fnTypes[decl.id.value] = [];
            }
            break;
        // export type ShowLoadingOptions = {}
        // export type RequestMethod = 'GET' | 'POST'
        case 'TsTypeLiteral':
        case 'TsUnionType':
            classTypes[decl.id.value] = {
                typeParams: !!decl.typeParams,
                interfaces: [],
                keepAliveMethods: [],
            };
            break;
        default:
            aliasTypes[decl.id.value] = {};
    }
}
function createParams(tsParams) {
    const params = [];
    tsParams.forEach((pat) => {
        if (pat.type === 'Identifier') {
            params.push({
                type: 'Parameter',
                pat,
                span: {},
            });
        }
    });
    return params;
}
async function parseModuleDecls(module, options) {
    // 优先合并 ios + android，如果没有，查找根目录 index.uts
    const iosDecls = (await parseFile(resolvePlatformIndex('app-ios', module, options), options, options.iosPreprocessor)).filter((decl) => {
        if (decl.type === 'Class') {
            if (decl.isHook) {
                options.iOSHookClass = options.namespace + (0, shared_1.capitalize)(decl.cls);
                return false;
            }
        }
        return true;
    });
    const androidDecls = (await parseFile(resolvePlatformIndex('app-android', module, options), options, options.androidPreprocessor)).filter((decl) => {
        if (decl.type === 'Class') {
            if (decl.isHook) {
                options.androidHookClass =
                    (0, utils_1.parseKotlinPackageWithPluginId)(options.id, options.is_uni_modules) +
                        '.' +
                        decl.cls;
                return false;
            }
        }
        return true;
    });
    // 优先使用 app-ios，因为 app-ios 平台函数类型需要正确的参数列表
    const decls = mergeDecls(androidDecls, iosDecls);
    // 如果没有平台特有，查找 root index.uts
    if (!decls.length) {
        return await parseFile(resolveRootIndex(module, options), options);
    }
    return decls;
}
function mergeRecord(from, to) {
    Object.keys(from).forEach((key) => {
        if (!(0, shared_1.hasOwn)(to, key)) {
            to[key] = from[key];
        }
    });
}
function mergeArray(from, to) {
    from.forEach((item) => {
        if (!to.includes(item)) {
            to.push(item);
        }
    });
}
function mergeDecls(from, to) {
    from.forEach((item) => {
        if (item.type === 'InterfaceDeclaration') {
            const decl = to.find((toItem) => toItem.type === 'InterfaceDeclaration' && toItem.cls === item.cls);
            if (!decl) {
                to.push(item);
            }
            else {
                mergeRecord(item.options.methods, decl.options.methods);
                mergeArray(item.options.props, decl.options.props);
            }
        }
        else if (item.type === 'Class') {
            const decl = to.find((toItem) => toItem.type === 'Class' &&
                toItem.cls === item.cls &&
                toItem.isDefault === item.isDefault);
            if (!decl) {
                to.push(item);
            }
            else {
                mergeRecord(item.options.methods, decl.options.methods);
                mergeRecord(item.options.staticMethods, decl.options.staticMethods);
                mergeArray(item.options.props, decl.options.props);
                mergeArray(item.options.staticProps, decl.options.staticProps);
            }
        }
        else if (item.type === 'FunctionDeclaration') {
            if (!to.find((toItem) => toItem.type === 'FunctionDeclaration' &&
                toItem.method === item.method &&
                toItem.isDefault === item.isDefault)) {
                to.push(item);
            }
        }
        else if (item.type === 'VariableDeclaration' &&
            item.declarations.length === 1) {
            if (!to.find((toItem) => {
                if (toItem.type === 'VariableDeclaration' &&
                    toItem.declarations.length === 1) {
                    const toDecl = toItem.declarations[0].id;
                    const decl = item.declarations[0].id;
                    return (toDecl.type === 'Identifier' &&
                        decl.type === 'Identifier' &&
                        toDecl.value === decl.value);
                }
                return false;
            })) {
                to.push(item);
            }
        }
    });
    return to;
}
async function parseFile(filename, options, preprocessor) {
    if (filename) {
        // 暂时不从uvue目录读取了，就读取原始文件
        // filename = resolveUVueFileName(filename)
        if (fs_1.default.existsSync(filename)) {
            const code = fs_1.default.readFileSync(filename, 'utf8');
            return parseCode(preprocessor ? await preprocessor(code, filename) : code, options.namespace, options.types, filename, options.inputDir);
        }
    }
    return [];
}
async function parseCode(code, namespace, types, filename, inputDir) {
    // 懒加载 uts 编译器
    // eslint-disable-next-line no-restricted-globals
    const { parse } = require('@dcloudio/uts');
    try {
        const ast = await parse(code, {
            filename: (0, utils_1.relative)(filename, inputDir),
            noColor: !(0, utils_1.isColorSupported)(),
        });
        return parseAst(ast, (0, utils_1.createResolveTypeReferenceName)(namespace, ast, types.class), types);
    }
    catch (e) {
        // console.error(parseUTSSyntaxError(e, inputDir))
    }
    return [];
}
function mergeAstTypes(to, from) {
    if (Object.keys(from.class).length) {
        for (const name in from.class) {
            if (!(0, shared_1.hasOwn)(to.class, name)) {
                to.class[name] = from.class[name];
            }
        }
    }
    if (Object.keys(from.fn).length) {
        for (const name in from.fn) {
            if (!(0, shared_1.hasOwn)(to.fn, name)) {
                to.fn[name] = from.fn[name];
            }
        }
    }
    if (Object.keys(from.interface).length) {
        for (const name in from.interface) {
            if (!(0, shared_1.hasOwn)(to.interface, name)) {
                to.interface[name] = from.interface[name];
            }
        }
    }
}
function parseAst(ast, resolveTypeReferenceName, types) {
    const decls = [];
    mergeAstTypes(types, parseAstTypes(ast, false));
    ast.body.forEach((item) => {
        if (item.type === 'ExportDeclaration') {
            const decl = item.declaration;
            switch (decl.type) {
                case 'FunctionDeclaration':
                    decls.push(genFunctionDeclaration(types, decl, resolveTypeReferenceName, false));
                    break;
                case 'ClassDeclaration':
                    decls.push(genClassDeclaration(types, decl, resolveTypeReferenceName, false));
                    break;
                case 'VariableDeclaration':
                    const varDecl = genVariableDeclaration(types, decl, resolveTypeReferenceName);
                    if (varDecl) {
                        decls.push(varDecl);
                    }
                    break;
            }
        }
        else if (item.type === 'ExportDefaultDeclaration') {
            const decl = item.decl;
            if (decl.type === 'ClassExpression') {
                if (decl.identifier) {
                    // export default class test{}
                    decls.push(genClassDeclaration(types, decl, resolveTypeReferenceName, true));
                }
            }
            else if (decl.type === 'FunctionExpression') {
                if (decl.identifier) {
                    decls.push(genFunctionDeclaration(types, decl, resolveTypeReferenceName, true));
                }
            }
        }
    });
    const interfaces = [];
    Object.keys(types.interface).forEach((name) => {
        const options = types.interface[name];
        if (options.returned) {
            interfaces.push(genInterfaceDeclaration(types, options.decl, resolveTypeReferenceName));
        }
    });
    return [...interfaces, ...decls];
}
function isReturnPromise(anno) {
    if (!anno) {
        return false;
    }
    const { typeAnnotation } = anno;
    return (typeAnnotation.type === 'TsTypeReference' &&
        typeAnnotation.typeName.type === 'Identifier' &&
        typeAnnotation.typeName.value === 'Promise');
}
function genProxyFunction(method, async, params, ret = '', isDefault = false, isVar = false, keepAlive = false) {
    return {
        type: 'FunctionDeclaration',
        method,
        async,
        keepAlive,
        params,
        return: ret ? { type: 'interface', options: ret } : undefined,
        isDefault,
        isVar,
    };
}
function genProxyClass(cls, options, isDefault = false, isVar = false, isHook = false, interfaces = []) {
    return { type: 'Class', cls, options, isDefault, isVar, isHook, interfaces };
}
function resolveIdentifierDefaultValue(ident) {
    if (ident.type === 'NullLiteral') {
        return 'UTSNull';
    }
    else if (ident.type === 'StringLiteral' ||
        ident.type === 'NumericLiteral' ||
        ident.type === 'BooleanLiteral') {
        return ident.value;
    }
    return null;
}
function resolveType(types, typeAnnotation, resolveTypeReferenceName) {
    if (typeAnnotation.type === 'TsKeywordType') {
        return typeAnnotation.kind;
    }
    else if (typeAnnotation.type === 'TsFunctionType') {
        return 'UTSCallback';
    }
    else if (typeAnnotation.type === 'TsTypeReference' &&
        typeAnnotation.typeName.type === 'Identifier') {
        // Array<string>
        if (typeAnnotation.typeName.value === 'Array' &&
            typeAnnotation.typeParams &&
            typeAnnotation.typeParams.params.length === 1) {
            return resolveType(types, typeAnnotation.typeParams.params[0], resolveTypeReferenceName);
        }
        if ((0, shared_1.hasOwn)(types.fn, typeAnnotation.typeName.value)) {
            return 'UTSCallback';
        }
        return resolveTypeReferenceName(typeAnnotation.typeName.value);
    }
    else if (typeAnnotation.type === 'TsParenthesizedType') {
        return resolveType(types, typeAnnotation.typeAnnotation, resolveTypeReferenceName);
    }
    else if (typeAnnotation.type === 'TsUnionType') {
        const isNullable = typeAnnotation.types.length === 2 &&
            typeAnnotation.types.some((type) => type.type === 'TsKeywordType' && type.kind === 'null');
        if (isNullable) {
            for (const type of typeAnnotation.types) {
                if (type.type === 'TsKeywordType') {
                    continue;
                }
                return resolveType(types, type, resolveTypeReferenceName);
            }
        }
    }
    else if (typeAnnotation.type === 'TsArrayType') {
        return resolveType(types, typeAnnotation.elemType, resolveTypeReferenceName);
    }
    return '';
}
function resolveIdentifierType(types, ident, resolveTypeReferenceName) {
    if (ident.typeAnnotation) {
        return resolveType(types, ident.typeAnnotation.typeAnnotation, resolveTypeReferenceName);
    }
    return '';
}
// function request<T>(options : RequestOptions<T>, _t : T.Type) : RequestTask
function isTDotType(pat) {
    const typeAnn = pat.typeAnnotation?.typeAnnotation;
    return (typeAnn?.type === 'TsTypeReference' &&
        typeAnn.typeName.type === 'TsQualifiedName' &&
        typeAnn.typeName.right.value === 'Type');
}
function resolveFunctionParams(types, params, resolveTypeReferenceName) {
    const result = [];
    params.forEach((param) => {
        let pat = param.type === 'Parameter' ? param.pat : param.param;
        if (pat.type === 'Identifier') {
            if (!isTDotType(pat)) {
                // ignore T.Type
                const param = {
                    name: pat.value,
                    type: resolveIdentifierType(types, pat, resolveTypeReferenceName),
                };
                // A | null
                if (pat.typeAnnotation?.typeAnnotation.type ===
                    'TsUnionType') {
                    param.default = 'UTSNull';
                }
                result.push(param);
            }
        }
        else if (pat.type === 'AssignmentPattern') {
            if (pat.left.type === 'Identifier') {
                const param = {
                    name: pat.left.value,
                    type: resolveIdentifierType(types, pat.left, resolveTypeReferenceName),
                };
                const defaultValue = resolveIdentifierDefaultValue(pat.right);
                if (defaultValue !== null) {
                    param.default = defaultValue;
                }
                result.push(param);
            }
        }
        else {
            result.push({ name: '', type: '' });
        }
    });
    return result;
}
function parseReturnInterface(types, returnType) {
    switch (returnType.type) {
        case 'TsTypeReference':
            if (returnType.typeName.type === 'Identifier') {
                if ((0, shared_1.hasOwn)(types.interface, returnType.typeName.value)) {
                    types.interface[returnType.typeName.value].returned = true;
                    return returnType.typeName.value;
                }
            }
            break;
        case 'TsUnionType':
            for (const type of returnType.types) {
                if (type.type === 'TsKeywordType') {
                    continue;
                }
                return parseReturnInterface(types, type);
            }
            break;
        case 'TsParenthesizedType':
            return parseReturnInterface(types, returnType.typeAnnotation);
    }
    return '';
}
function genFunctionDeclaration(types, decl, resolveTypeReferenceName, isDefault = false, isVar = false) {
    return genProxyFunction(decl.identifier.value, decl.async || isReturnPromise(decl.returnType), resolveFunctionParams(types, decl.params, resolveTypeReferenceName), decl.returnType
        ? parseReturnInterface(types, decl.returnType.typeAnnotation)
        : '', isDefault, isVar, parseKeepAlive(decl));
}
function parseKeepAlive(decl) {
    if (!decl.decorators || !decl.decorators.length) {
        return false;
    }
    return decl.decorators.some((decorator) => {
        if (decorator.expression.type === 'MemberExpression' &&
            decorator.expression.property.type === 'Identifier' &&
            decorator.expression.property.value === 'keepAlive') {
            return true;
        }
        return false;
    });
}
function parseInterfaceBody(types, decl) {
    const elements = decl.body.body.slice();
    decl.extends.forEach((extend) => {
        if (extend.expression.type === 'Identifier' &&
            types.interface[extend.expression.value]) {
            elements.push(...parseInterfaceBody(types, types.interface[extend.expression.value].decl));
        }
    });
    return elements;
}
function genInterfaceDeclaration(types, decl, resolveTypeReferenceName) {
    const cls = decl.id.value;
    const methods = {};
    const props = [];
    const setters = {};
    const elements = parseInterfaceBody(types, decl);
    elements.forEach((item) => {
        if (item.type === 'TsMethodSignature') {
            if (item.key.type === 'Identifier') {
                let returnOptions;
                if (item.typeAnn) {
                    let returnInterface = parseReturnInterface(types, item.typeAnn.typeAnnotation);
                    if (returnInterface) {
                        returnOptions = {
                            type: 'interface',
                            options: returnInterface,
                        };
                    }
                }
                const name = item.key.value;
                const value = {
                    async: isReturnPromise(item.typeAnn),
                    keepAlive: false,
                    params: resolveFunctionParams(types, tsParamsToParams(item.params), resolveTypeReferenceName),
                    return: returnOptions,
                };
                methods[name + 'ByJs'] = value;
            }
        }
        else if (item.type === 'TsPropertySignature') {
            if (item.key.type === 'Identifier') {
                props.push(item.key.value);
                if (item.typeAnnotation) {
                    const params = resolveFunctionParams(types, tsParamsToParams([
                        createBindingIdentifier(item.key.value, item.typeAnnotation),
                    ]), resolveTypeReferenceName);
                    if (params.length) {
                        setters[item.key.value] = params[0];
                    }
                }
            }
        }
    });
    return {
        type: 'InterfaceDeclaration',
        cls,
        options: {
            methods,
            props,
            setters,
        },
    };
}
function tsParamsToParams(tsParams) {
    const params = [];
    tsParams.forEach((p) => {
        if (p.type === 'Identifier') {
            params.push({
                type: 'Parameter',
                pat: p,
                span: {},
            });
        }
    });
    return params;
}
function genClassDeclaration(types, decl, resolveTypeReferenceName, isDefault = false) {
    const cls = decl.identifier.value;
    const constructor = { params: [] };
    const methods = {};
    const staticMethods = {};
    const props = [];
    const staticProps = [];
    const setters = {};
    const staticSetters = {};
    const isHook = decl.implements.some((implement) => implement.expression.type === 'Identifier' &&
        isHookClass(implement.expression.value));
    const interfaces = parseImplements(decl);
    decl.body.forEach((item) => {
        if (item.type === 'Constructor') {
            constructor.params = resolveFunctionParams(types, item.params, resolveTypeReferenceName);
        }
        else if (item.type === 'ClassMethod') {
            if (item.key.type === 'Identifier') {
                if (item.kind === 'getter' || item.kind === 'setter') {
                    const curProps = item.isStatic ? staticProps : props;
                    if (!curProps.includes(item.key.value)) {
                        curProps.push(item.key.value);
                    }
                    if (item.kind === 'setter') {
                        const params = resolveFunctionParams(types, item.function.params, resolveTypeReferenceName);
                        if (params.length) {
                            ;
                            (item.isStatic ? staticSetters : setters)[item.key.value] =
                                params[0];
                        }
                    }
                }
                else {
                    let returnOptions;
                    if (item.function.returnType) {
                        let returnInterface = parseReturnInterface(types, item.function.returnType.typeAnnotation);
                        if (returnInterface) {
                            returnOptions = {
                                type: 'interface',
                                options: returnInterface,
                            };
                        }
                    }
                    const name = item.key.value;
                    const value = {
                        async: item.function.async || isReturnPromise(item.function.returnType),
                        keepAlive: parseKeepAlive(item.function),
                        params: resolveFunctionParams(types, item.function.params, resolveTypeReferenceName),
                        return: returnOptions,
                    };
                    if (item.isStatic) {
                        staticMethods[name + 'ByJs'] = value;
                    }
                    else {
                        methods[name + 'ByJs'] = value;
                    }
                }
            }
        }
        else if (item.type === 'ClassProperty') {
            if (item.key.type === 'Identifier') {
                if (item.isStatic) {
                    staticProps.push(item.key.value);
                }
                else {
                    props.push(item.key.value);
                }
                if (item.typeAnnotation) {
                    const params = resolveFunctionParams(types, tsParamsToParams([
                        createBindingIdentifier(item.key.value, item.typeAnnotation),
                    ]), resolveTypeReferenceName);
                    if (params.length) {
                        ;
                        (item.isStatic ? staticSetters : setters)[item.key.value] =
                            params[0];
                    }
                }
            }
        }
    });
    return genProxyClass(cls, {
        constructor,
        methods,
        staticMethods,
        props,
        staticProps,
        setters,
        staticSetters,
    }, isDefault, false, isHook, interfaces);
}
function genInitCode(expr) {
    switch (expr.type) {
        case 'BooleanLiteral':
            return expr.value + '';
        case 'NumericLiteral':
            return expr.value + '';
        case 'StringLiteral':
            return expr.value;
    }
    return '';
}
function genVariableDeclaration(types, decl, resolveTypeReferenceName) {
    // 目前仅支持 const 的 boolean,number,string
    const lits = ['BooleanLiteral', 'NumericLiteral', 'StringLiteral'];
    if (decl.kind === 'const' &&
        !decl.declarations.find((d) => {
            if (d.id.type !== 'Identifier') {
                return true;
            }
            if (!d.init) {
                return true;
            }
            const type = d.init.type;
            if (!lits.includes(type)) {
                return true;
            }
            return false;
        })) {
        return decl;
    }
    if (decl.declarations.length === 1) {
        // 识别是否是定义的 function,如：export const showToast:ShowToast = ()=>{}
        const { id, init } = decl.declarations[0];
        if (id.type === 'Identifier' &&
            init &&
            (init.type === 'ArrowFunctionExpression' ||
                init.type === 'FunctionExpression')) {
            // 根据类型信息查找参数列表
            let params;
            const typeAnn = id.typeAnnotation;
            if (typeAnn && typeAnn.typeAnnotation.type === 'TsTypeReference') {
                const { typeName } = typeAnn.typeAnnotation;
                if (typeName.type === 'Identifier') {
                    const value = types.fn[typeName.value];
                    if ((0, shared_1.isArray)(value)) {
                        params = value;
                    }
                }
            }
            return genFunctionDeclaration(types, createFunctionDeclaration(id.value, init, params), resolveTypeReferenceName, false, true);
        }
    }
}
function createBindingIdentifier(name, typeAnnotation) {
    return {
        type: 'Identifier',
        value: name,
        optional: false,
        span: {},
        typeAnnotation,
    };
}
function createIdentifier(name) {
    return {
        type: 'Identifier',
        value: name,
        optional: false,
        span: {},
    };
}
function createFunctionDeclaration(name, func, params) {
    if (!params) {
        if (func.type === 'FunctionExpression') {
            params = func.params;
        }
        else if (func.type === 'ArrowFunctionExpression') {
            params = [];
            func.params.forEach((p) => {
                if (p.type === 'Identifier') {
                    params.push({
                        type: 'Parameter',
                        pat: p,
                        span: {},
                    });
                }
            });
        }
    }
    return {
        type: 'FunctionDeclaration',
        identifier: createIdentifier(name),
        declare: false,
        params: params,
        generator: false,
        async: func.async,
        typeParameters: func.typeParameters,
        returnType: func.returnType,
        span: {},
    };
}
async function parseExportIdentifiers(fileName) {
    const ids = [];
    if (!fs_1.default.existsSync(fileName)) {
        return ids;
    }
    const { parse } = require('@dcloudio/uts');
    let ast = null;
    try {
        ast = await parse(fs_1.default.readFileSync(fileName, 'utf8'), {
            filename: fileName,
            noColor: true,
        });
    }
    catch (e) { }
    if (!ast) {
        return ids;
    }
    ast.body.forEach((item) => {
        if (item.type === 'ExportDeclaration') {
            switch (item.declaration.type) {
                case 'FunctionDeclaration':
                    ids.push(item.declaration.identifier.value);
                    break;
                case 'ClassDeclaration':
                    ids.push(item.declaration.identifier.value);
                    break;
                case 'VariableDeclaration':
                    item.declaration.declarations.forEach((d) => {
                        if (d.id.type === 'Identifier') {
                            ids.push(d.id.value);
                        }
                    });
                    break;
                case 'TsInterfaceDeclaration':
                    ids.push(item.declaration.id.value);
                    break;
                case 'TsTypeAliasDeclaration':
                    ids.push(item.declaration.id.value);
                    break;
                case 'TsEnumDeclaration':
                    ids.push(item.declaration.id.value);
                    break;
                case 'TsModuleDeclaration':
                    ids.push(item.declaration.id.value);
                    break;
            }
        }
    });
    return ids;
}
exports.parseExportIdentifiers = parseExportIdentifiers;
