"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTemplateTagsAndAttrs = exports.getElementAttrs = exports.checkComponentNames = exports.checkEventsOfTag = exports.checkPropsOfTag = void 0;
const vue = require("@vue/language-core");
const embedded = require("@volar/language-core");
const reactivity_1 = require("@vue/reactivity");
const language_core_1 = require("@vue/language-core");
const shared_1 = require("@vue/shared");
function checkPropsOfTag(ts, tsLs, sourceFile, tag, vueCompilerOptions, requiredOnly = false) {
    const checker = tsLs.getProgram().getTypeChecker();
    const components = getComponentsType(ts, tsLs, sourceFile);
    if (!components)
        return [];
    const name = tag.split('.');
    let componentSymbol = components.componentsType.getProperty(name[0]);
    if (!componentSymbol && !vueCompilerOptions.nativeTags.includes(name[0])) {
        componentSymbol = components.componentsType.getProperty((0, shared_1.camelize)(name[0]))
            ?? components.componentsType.getProperty((0, shared_1.capitalize)((0, shared_1.camelize)(name[0])));
    }
    if (!componentSymbol)
        return [];
    let componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.componentsNode);
    for (let i = 1; i < name.length; i++) {
        componentSymbol = componentType.getProperty(name[i]);
        if (componentSymbol) {
            componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.componentsNode);
        }
        else {
            return [];
        }
    }
    const result = new Set();
    for (const sig of componentType.getCallSignatures()) {
        const propParam = sig.parameters[0];
        if (propParam) {
            const propsType = checker.getTypeOfSymbolAtLocation(propParam, components.componentsNode);
            const props = propsType.getProperties();
            for (const prop of props) {
                if (!requiredOnly || !(prop.flags & ts.SymbolFlags.Optional)) {
                    result.add(prop.name);
                }
            }
        }
    }
    for (const sig of componentType.getConstructSignatures()) {
        const instanceType = sig.getReturnType();
        const propsSymbol = instanceType.getProperty('$props');
        if (propsSymbol) {
            const propsType = checker.getTypeOfSymbolAtLocation(propsSymbol, components.componentsNode);
            const props = propsType.getProperties();
            for (const prop of props) {
                if (prop.flags & ts.SymbolFlags.Method) { // #2443
                    continue;
                }
                if (!requiredOnly || !(prop.flags & ts.SymbolFlags.Optional)) {
                    result.add(prop.name);
                }
            }
        }
    }
    return [...result];
}
exports.checkPropsOfTag = checkPropsOfTag;
function checkEventsOfTag(ts, tsLs, sourceFile, tag, vueCompilerOptions) {
    const checker = tsLs.getProgram().getTypeChecker();
    const components = getComponentsType(ts, tsLs, sourceFile);
    if (!components)
        return [];
    const name = tag.split('.');
    let componentSymbol = components.componentsType.getProperty(name[0]);
    if (!componentSymbol && !vueCompilerOptions.nativeTags.includes(name[0])) {
        componentSymbol = components.componentsType.getProperty((0, shared_1.camelize)(name[0]))
            ?? components.componentsType.getProperty((0, shared_1.capitalize)((0, shared_1.camelize)(name[0])));
    }
    if (!componentSymbol)
        return [];
    let componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.componentsNode);
    for (let i = 1; i < name.length; i++) {
        componentSymbol = componentType.getProperty(name[i]);
        if (componentSymbol) {
            componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.componentsNode);
        }
        else {
            return [];
        }
    }
    const result = new Set();
    // for (const sig of componentType.getCallSignatures()) {
    // 	const emitParam = sig.parameters[1];
    // 	if (emitParam) {
    // 		// TODO
    // 	}
    // }
    for (const sig of componentType.getConstructSignatures()) {
        const instanceType = sig.getReturnType();
        const emitSymbol = instanceType.getProperty('$emit');
        if (emitSymbol) {
            const emitType = checker.getTypeOfSymbolAtLocation(emitSymbol, components.componentsNode);
            for (const call of emitType.getCallSignatures()) {
                const eventNameParamSymbol = call.parameters[0];
                if (eventNameParamSymbol) {
                    const eventNameParamType = checker.getTypeOfSymbolAtLocation(eventNameParamSymbol, components.componentsNode);
                    if (eventNameParamType.isStringLiteral()) {
                        result.add(eventNameParamType.value);
                    }
                }
            }
        }
    }
    return [...result];
}
exports.checkEventsOfTag = checkEventsOfTag;
function checkComponentNames(ts, tsLs, sourceFile, vueCompilerOptions) {
    return getComponentsType(ts, tsLs, sourceFile)
        ?.componentsType
        ?.getProperties()
        .map(c => c.name)
        .filter(entry => entry.indexOf('$') === -1 && !entry.startsWith('_'))
        .filter(entry => !vueCompilerOptions.nativeTags.includes(entry))
        ?? [];
}
exports.checkComponentNames = checkComponentNames;
function getElementAttrs(ts, tsLs, tsLsHost, tagName) {
    const sharedTypesFileName = tsLsHost.getCurrentDirectory() + '/' + language_core_1.sharedTypes.baseName;
    let tsSourceFile;
    if (tsSourceFile = tsLs.getProgram()?.getSourceFile(sharedTypesFileName)) {
        const typeNode = tsSourceFile.statements.find((node) => ts.isTypeAliasDeclaration(node) && node.name.getText() === '__VLS_IntrinsicElements');
        const checker = tsLs.getProgram()?.getTypeChecker();
        if (checker && typeNode) {
            const type = checker.getTypeFromTypeNode(typeNode.type);
            const el = type.getProperty(tagName);
            if (el) {
                const attrs = checker.getTypeOfSymbolAtLocation(el, typeNode).getProperties();
                return attrs.map(c => c.name);
            }
        }
    }
    return [];
}
exports.getElementAttrs = getElementAttrs;
function getComponentsType(ts, tsLs, sourceFile) {
    if (!(sourceFile instanceof vue.VueFile)) {
        return;
    }
    let file;
    let tsSourceFile;
    embedded.forEachEmbeddedFile(sourceFile, embedded => {
        if (embedded.fileName === sourceFile.mainScriptName) {
            file = embedded;
        }
    });
    if (file && (tsSourceFile = tsLs.getProgram()?.getSourceFile(file.fileName))) {
        const componentsNode = getComponentsNode(ts, tsSourceFile);
        const checker = tsLs.getProgram()?.getTypeChecker();
        if (checker && componentsNode) {
            return {
                componentsNode,
                componentsType: checker.getTypeAtLocation(componentsNode),
            };
        }
    }
    function getComponentsNode(ts, sourceFile) {
        let componentsNode;
        walk(sourceFile);
        return componentsNode;
        function walk(node) {
            if (componentsNode) {
                return;
            }
            else if (ts.isVariableDeclaration(node) && node.name.getText() === '__VLS_components') {
                componentsNode = node;
            }
            else {
                node.forEachChild(walk);
            }
        }
    }
}
const map = new WeakMap();
function getTemplateTagsAndAttrs(sourceFile) {
    if (!map.has(sourceFile)) {
        const getter = (0, reactivity_1.computed)(() => {
            if (!(sourceFile instanceof vue.VueFile))
                return;
            const ast = sourceFile.compiledSFCTemplate?.ast;
            const tags = new Map();
            if (ast) {
                vue.walkElementNodes(ast, node => {
                    if (!tags.has(node.tag)) {
                        tags.set(node.tag, { offsets: [], attrs: new Map() });
                    }
                    const tag = tags.get(node.tag);
                    const startTagHtmlOffset = node.loc.start.offset + node.loc.source.indexOf(node.tag);
                    const endTagHtmlOffset = node.loc.start.offset + node.loc.source.lastIndexOf(node.tag);
                    tag.offsets.push(startTagHtmlOffset);
                    if (!node.isSelfClosing) {
                        tag.offsets.push(endTagHtmlOffset);
                    }
                    for (const prop of node.props) {
                        let name;
                        let offset;
                        if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                            && prop.arg?.type === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */
                            && prop.arg.isStatic) {
                            name = prop.arg.content;
                            offset = prop.arg.loc.start.offset;
                        }
                        else if (prop.type === 6 /* CompilerDOM.NodeTypes.ATTRIBUTE */) {
                            name = prop.name;
                            offset = prop.loc.start.offset;
                        }
                        if (name !== undefined && offset !== undefined) {
                            if (!tag.attrs.has(name)) {
                                tag.attrs.set(name, { offsets: [] });
                            }
                            tag.attrs.get(name).offsets.push(offset);
                        }
                    }
                });
            }
            return tags;
        });
        map.set(sourceFile, getter);
    }
    return map.get(sourceFile).value ?? new Map();
}
exports.getTemplateTagsAndAttrs = getTemplateTagsAndAttrs;
//# sourceMappingURL=helpers.js.map