"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertTagName = convertTagName;
exports.convertAttrName = convertAttrName;
exports.getNameCasing = getNameCasing;
exports.detect = detect;
const vue = require("@vue/language-core");
const language_core_1 = require("@vue/language-core");
const alien_signals_1 = require("alien-signals");
const types_1 = require("../types");
async function convertTagName(context, uri, casing, tsPluginClient) {
    const sourceFile = context.language.scripts.get(uri);
    if (!sourceFile) {
        return;
    }
    const root = sourceFile?.generated?.root;
    if (!(root instanceof language_core_1.VueVirtualCode)) {
        return;
    }
    const { template } = root.sfc;
    if (!template) {
        return;
    }
    const document = context.documents.get(sourceFile.id, sourceFile.languageId, sourceFile.snapshot);
    const edits = [];
    const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
    const tags = getTemplateTagsAndAttrs(root);
    for (const [tagName, { offsets }] of tags) {
        const componentName = components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
        if (componentName) {
            for (const offset of offsets) {
                const start = document.positionAt(template.startTagEnd + offset);
                const end = document.positionAt(template.startTagEnd + offset + tagName.length);
                const range = { start, end };
                if (casing === types_1.TagNameCasing.Kebab && tagName !== (0, language_core_1.hyphenateTag)(componentName)) {
                    edits.push({ range, newText: (0, language_core_1.hyphenateTag)(componentName) });
                }
                if (casing === types_1.TagNameCasing.Pascal && tagName !== componentName) {
                    edits.push({ range, newText: componentName });
                }
            }
        }
    }
    return edits;
}
async function convertAttrName(context, uri, casing, tsPluginClient) {
    const sourceFile = context.language.scripts.get(uri);
    if (!sourceFile) {
        return;
    }
    const root = sourceFile?.generated?.root;
    if (!(root instanceof language_core_1.VueVirtualCode)) {
        return;
    }
    const { template } = root.sfc;
    if (!template) {
        return;
    }
    const document = context.documents.get(uri, sourceFile.languageId, sourceFile.snapshot);
    const edits = [];
    const components = await tsPluginClient?.getComponentNames(root.fileName) ?? [];
    const tags = getTemplateTagsAndAttrs(root);
    for (const [tagName, { attrs }] of tags) {
        const componentName = components.find(component => component === tagName || (0, language_core_1.hyphenateTag)(component) === tagName);
        if (componentName) {
            const props = (await tsPluginClient?.getComponentProps(root.fileName, componentName) ?? []).map(prop => prop.name);
            for (const [attrName, { offsets }] of attrs) {
                const propName = props.find(prop => prop === attrName || (0, language_core_1.hyphenateAttr)(prop) === attrName);
                if (propName) {
                    for (const offset of offsets) {
                        const start = document.positionAt(template.startTagEnd + offset);
                        const end = document.positionAt(template.startTagEnd + offset + attrName.length);
                        const range = { start, end };
                        if (casing === types_1.AttrNameCasing.Kebab && attrName !== (0, language_core_1.hyphenateAttr)(propName)) {
                            edits.push({ range, newText: (0, language_core_1.hyphenateAttr)(propName) });
                        }
                        if (casing === types_1.AttrNameCasing.Camel && attrName !== propName) {
                            edits.push({ range, newText: propName });
                        }
                    }
                }
            }
        }
    }
    return edits;
}
async function getNameCasing(context, uri) {
    const detected = await detect(context, uri);
    const [attr, tag] = await Promise.all([
        context.env.getConfiguration?.('vue.complete.casing.props', uri.toString()),
        context.env.getConfiguration?.('vue.complete.casing.tags', uri.toString()),
    ]);
    const tagNameCasing = detected.tag.length === 1 && (tag === 'autoPascal' || tag === 'autoKebab') ? detected.tag[0] : (tag === 'autoKebab' || tag === 'kebab') ? types_1.TagNameCasing.Kebab : types_1.TagNameCasing.Pascal;
    const attrNameCasing = detected.attr.length === 1 && (attr === 'autoCamel' || attr === 'autoKebab') ? detected.attr[0] : (attr === 'autoCamel' || attr === 'camel') ? types_1.AttrNameCasing.Camel : types_1.AttrNameCasing.Kebab;
    return {
        tag: tagNameCasing,
        attr: attrNameCasing,
    };
}
async function detect(context, uri) {
    const rootFile = context.language.scripts.get(uri)?.generated?.root;
    if (!(rootFile instanceof language_core_1.VueVirtualCode)) {
        return {
            tag: [],
            attr: [],
        };
    }
    return {
        tag: await getTagNameCase(rootFile),
        attr: getAttrNameCase(rootFile),
    };
    function getAttrNameCase(file) {
        const tags = getTemplateTagsAndAttrs(file);
        const result = [];
        for (const [_, { attrs }] of tags) {
            for (const [tagName] of attrs) {
                // attrName
                if (tagName !== (0, language_core_1.hyphenateTag)(tagName)) {
                    result.push(types_1.AttrNameCasing.Camel);
                    break;
                }
            }
            for (const [tagName] of attrs) {
                // attr-name
                if (tagName.includes('-')) {
                    result.push(types_1.AttrNameCasing.Kebab);
                    break;
                }
            }
        }
        return result;
    }
    function getTagNameCase(file) {
        const result = new Set();
        if (file.sfc.template?.ast) {
            for (const element of vue.forEachElementNode(file.sfc.template.ast)) {
                if (element.tagType === 1) {
                    if (element.tag !== (0, language_core_1.hyphenateTag)(element.tag)) {
                        // TagName
                        result.add(types_1.TagNameCasing.Pascal);
                    }
                    else {
                        // Tagname -> tagname
                        // TagName -> tag-name
                        result.add(types_1.TagNameCasing.Kebab);
                    }
                }
            }
        }
        return [...result];
    }
}
const map = new WeakMap();
function getTemplateTagsAndAttrs(sourceFile) {
    if (!map.has(sourceFile)) {
        const getter = (0, alien_signals_1.computed)(() => {
            if (!(sourceFile instanceof vue.VueVirtualCode)) {
                return;
            }
            const ast = sourceFile.sfc.template?.ast;
            const tags = new Map();
            if (ast) {
                for (const node of vue.forEachElementNode(ast)) {
                    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
                            && prop.arg?.type === 4
                            && prop.arg.isStatic) {
                            name = prop.arg.content;
                            offset = prop.arg.loc.start.offset;
                        }
                        else if (prop.type === 6) {
                            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)() ?? new Map();
}
//# sourceMappingURL=nameCasing.js.map