"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDecoratorOrUndefinedByNames = getDecoratorOrUndefinedByNames;
exports.getTypeReferenceAsString = getTypeReferenceAsString;
exports.isPromiseOrObservable = isPromiseOrObservable;
exports.hasPropertyKey = hasPropertyKey;
exports.getOutputExtension = getOutputExtension;
exports.replaceImportPath = replaceImportPath;
exports.insertAt = insertAt;
exports.isDynamicallyAdded = isDynamicallyAdded;
exports.isAutoGeneratedEnumUnion = isAutoGeneratedEnumUnion;
exports.isAutoGeneratedTypeUnion = isAutoGeneratedTypeUnion;
exports.extractTypeArgumentIfArray = extractTypeArgumentIfArray;
exports.convertPath = convertPath;
exports.canReferenceNode = canReferenceNode;
const lodash_1 = require("lodash");
const path_1 = require("path");
const ts = require("typescript");
const ast_utils_1 = require("./ast-utils");
function getDecoratorOrUndefinedByNames(names, decorators, factory) {
    return (decorators || factory.createNodeArray()).find((item) => {
        try {
            const decoratorName = (0, ast_utils_1.getDecoratorName)(item);
            return names.includes(decoratorName);
        }
        catch (_a) {
            return false;
        }
    });
}
function getTypeReferenceAsString(type, typeChecker, arrayDepth = 0) {
    if ((0, ast_utils_1.isArray)(type)) {
        const arrayType = (0, ast_utils_1.getTypeArguments)(type)[0];
        const { typeName, arrayDepth: depth } = getTypeReferenceAsString(arrayType, typeChecker, arrayDepth + 1);
        if (!typeName) {
            return { typeName: undefined };
        }
        return {
            typeName: `${typeName}`,
            isArray: true,
            arrayDepth: depth
        };
    }
    if ((0, ast_utils_1.isBoolean)(type)) {
        return { typeName: Boolean.name, arrayDepth };
    }
    if ((0, ast_utils_1.isNumber)(type)) {
        return { typeName: Number.name, arrayDepth };
    }
    if ((0, ast_utils_1.isBigInt)(type)) {
        return { typeName: BigInt.name, arrayDepth };
    }
    if ((0, ast_utils_1.isString)(type) || (0, ast_utils_1.isStringLiteral)(type) || (0, ast_utils_1.isStringMapping)(type)) {
        return { typeName: String.name, arrayDepth };
    }
    if (isPromiseOrObservable((0, ast_utils_1.getText)(type, typeChecker))) {
        const typeArguments = (0, ast_utils_1.getTypeArguments)(type);
        const elementType = getTypeReferenceAsString((0, lodash_1.head)(typeArguments), typeChecker, arrayDepth);
        return elementType;
    }
    if (type.isClass()) {
        return { typeName: (0, ast_utils_1.getText)(type, typeChecker), arrayDepth };
    }
    try {
        const text = (0, ast_utils_1.getText)(type, typeChecker);
        if (text === Date.name) {
            return { typeName: text, arrayDepth };
        }
        if (isOptionalBoolean(text)) {
            return { typeName: Boolean.name, arrayDepth };
        }
        if (isAutoGeneratedTypeUnion(type) ||
            isAutoGeneratedEnumUnion(type, typeChecker)) {
            const types = type.types;
            return getTypeReferenceAsString(types[types.length - 1], typeChecker, arrayDepth);
        }
        if (text === 'any' ||
            text === 'unknown' ||
            text === 'object' ||
            (0, ast_utils_1.isInterface)(type) ||
            (type.isUnionOrIntersection() && !(0, ast_utils_1.isEnum)(type))) {
            return { typeName: 'Object', arrayDepth };
        }
        if ((0, ast_utils_1.isEnum)(type)) {
            return { typeName: undefined, arrayDepth };
        }
        if (type.aliasSymbol) {
            return { typeName: 'Object', arrayDepth };
        }
        if (typeChecker.getApparentType(type).getSymbol().getEscapedName() ===
            'String') {
            return { typeName: String.name, arrayDepth };
        }
        return { typeName: undefined };
    }
    catch (_a) {
        return { typeName: undefined };
    }
}
function isPromiseOrObservable(type) {
    return type.includes('Promise<') || type.includes('Observable<');
}
function hasPropertyKey(key, properties) {
    return properties
        .filter((item) => !isDynamicallyAdded(item))
        .some((item) => item.name.getText() === key);
}
function getOutputExtension(fileName) {
    if (fileName.endsWith('.mts')) {
        return '.mjs';
    }
    else if (fileName.endsWith('.cts')) {
        return '.cjs';
    }
    else {
        return '.js';
    }
}
function replaceImportPath(typeReference, fileName, options) {
    if (!typeReference.includes('import')) {
        return { typeReference, importPath: null };
    }
    if (options.esmCompatible) {
        typeReference = typeReference.replace(', { with: { "resolution-mode": "import" } }', '');
    }
    let importPath = /\(\"([^)]).+(\")/.exec(typeReference)[0];
    if (!importPath) {
        return { typeReference: undefined, importPath: null };
    }
    importPath = convertPath(importPath);
    importPath = importPath.slice(2, importPath.length - 1);
    try {
        if ((0, path_1.isAbsolute)(importPath)) {
            throw {};
        }
        require.resolve(importPath);
        if (!options.esmCompatible) {
            typeReference = typeReference.replace('import', 'require');
        }
        return {
            typeReference,
            importPath: null
        };
    }
    catch (_a) {
        const from = (options === null || options === void 0 ? void 0 : options.readonly)
            ? convertPath(options.pathToSource)
            : path_1.posix.dirname(convertPath(fileName));
        let relativePath = path_1.posix.relative(from, importPath);
        relativePath = relativePath[0] !== '.' ? './' + relativePath : relativePath;
        const nodeModulesText = 'node_modules';
        const nodeModulePos = relativePath.indexOf(nodeModulesText);
        if (nodeModulePos >= 0) {
            relativePath = relativePath.slice(nodeModulePos + nodeModulesText.length + 1);
            const typesText = '@types';
            const typesPos = relativePath.indexOf(typesText);
            if (typesPos >= 0) {
                relativePath = relativePath.slice(typesPos + typesText.length + 1);
            }
            const indexText = '/index';
            const indexPos = relativePath.indexOf(indexText);
            if (indexPos >= 0) {
                relativePath = relativePath.slice(0, indexPos);
            }
        }
        else if (options.esmCompatible) {
            const extension = getOutputExtension(fileName);
            relativePath += extension;
        }
        typeReference = typeReference.replace(importPath, relativePath);
        if (options.readonly) {
            const { typeName, typeImportStatement } = convertToAsyncImport(typeReference);
            return {
                typeReference: typeImportStatement,
                typeName,
                importPath: relativePath
            };
        }
        if (options.esmCompatible) {
            const { typeName, typeImportStatement } = convertToAsyncImport(typeReference);
            return {
                typeReference: `(${typeImportStatement}).${typeName}`,
                importPath: relativePath
            };
        }
        return {
            typeReference: typeReference.replace('import', 'require'),
            importPath: relativePath
        };
    }
}
function convertToAsyncImport(typeReference) {
    const regexp = /import\(.+\).([^\]]+)(\])?/;
    const match = regexp.exec(typeReference);
    if ((match === null || match === void 0 ? void 0 : match.length) >= 2) {
        const importPos = typeReference.indexOf(match[0]);
        typeReference = typeReference.replace(`.${match[1]}`, '');
        return {
            typeImportStatement: insertAt(typeReference, importPos, 'await '),
            typeName: match[1]
        };
    }
    return { typeImportStatement: typeReference };
}
function insertAt(string, index, substring) {
    return string.slice(0, index) + substring + string.slice(index);
}
function isDynamicallyAdded(identifier) {
    return identifier && !identifier.parent && identifier.pos === -1;
}
function isAutoGeneratedEnumUnion(type, typeChecker) {
    if (type.isUnionOrIntersection() && !(0, ast_utils_1.isEnum)(type)) {
        if (!type.types) {
            return undefined;
        }
        const undefinedTypeIndex = type.types.findIndex((type) => type.intrinsicName === 'undefined' || type.intrinsicName === 'null');
        if (undefinedTypeIndex < 0) {
            return undefined;
        }
        let parentType = undefined;
        const isParentSymbolEqual = type.types.every((item, index) => {
            if (index === undefinedTypeIndex) {
                return true;
            }
            if (!item.symbol) {
                return false;
            }
            if (!item.symbol.parent ||
                item.symbol.flags !== ts.SymbolFlags.EnumMember) {
                return false;
            }
            const symbolType = typeChecker.getDeclaredTypeOfSymbol(item.symbol.parent);
            if (symbolType === parentType || !parentType) {
                parentType = symbolType;
                return true;
            }
            return false;
        });
        if (isParentSymbolEqual) {
            return parentType;
        }
    }
    return undefined;
}
function isAutoGeneratedTypeUnion(type) {
    if (type.isUnionOrIntersection() && !(0, ast_utils_1.isEnum)(type)) {
        if (!type.types) {
            return false;
        }
        const undefinedTypeIndex = type.types.findIndex((type) => type.intrinsicName === 'undefined');
        if (type.types.length === 2 && undefinedTypeIndex >= 0) {
            return true;
        }
    }
    return false;
}
function extractTypeArgumentIfArray(type) {
    if ((0, ast_utils_1.isArray)(type)) {
        type = (0, ast_utils_1.getTypeArguments)(type)[0];
        if (!type) {
            return undefined;
        }
        return {
            type,
            isArray: true
        };
    }
    return {
        type,
        isArray: false
    };
}
function isOptionalBoolean(text) {
    return typeof text === 'string' && text === 'boolean | undefined';
}
function convertPath(windowsPath) {
    return windowsPath
        .replace(/^\\\\\?\\/, '')
        .replace(/\\/g, '/')
        .replace(/\/\/+/g, '/');
}
function canReferenceNode(node, options) {
    var _a;
    if (!options.readonly) {
        return true;
    }
    if (ts.isCallExpression(node) || ts.isIdentifier(node)) {
        return false;
    }
    if (ts.isNewExpression(node)) {
        if (((_a = node.expression) === null || _a === void 0 ? void 0 : _a.escapedText) === 'Date') {
            return true;
        }
        return false;
    }
    if (node.kind === ts.SyntaxKind.FalseKeyword ||
        node.kind === ts.SyntaxKind.TrueKeyword ||
        node.kind === ts.SyntaxKind.NullKeyword) {
        return true;
    }
    if (ts.isNumericLiteral(node) ||
        ts.isPrefixUnaryExpression(node) ||
        ts.isStringLiteral(node)) {
        return true;
    }
    return false;
}
