/**
 * Copyright 2013-2026 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { passthrough } from '@yeoman/transform';
import { Minimatch } from 'minimatch';
function replaceTranslationAttributes({ content, getWebappTranslation, }) {
    return content.replaceAll(/:(?<tag>(?:placeholder|title|label))="(?<translate>t\$\([^"]+\))"/g, (_complete, ...args) => {
        const groups = args.pop();
        if (groups.translate.includes('+')) {
            return '';
        }
        const translated = replaceTranslations({ content: groups.translate, type: 'vue', getWebappTranslation });
        return `${groups.tag}="${translated}"`;
    });
}
export function removeDeclarations({ content }) {
    return content
        .replaceAll(/\nimport {\s*useI18n\s*} from 'vue-i18n';/g, '')
        .replaceAll(/\n\s*t\$,/g, '')
        .replaceAll(/\n\s*t\$:\s*useI18n\(\).t,/g, '');
}
export function replaceVueTranslations({ body, enableTranslation, getWebappTranslation, }) {
    body = body
        .replaceAll(/{{ *(?<translate>t\$\([^)]*\)) *}}/g, (complete, ...args) => {
        const groups = args.pop();
        if (!enableTranslation && groups.translate) {
            if (groups.translate.includes('+')) {
                this?.log.info(`Ignoring dynamic translation ${groups.translate}`);
            }
            else {
                const translated = replaceTranslations({
                    type: 'vue',
                    content: groups.translate,
                    getWebappTranslation,
                });
                if (translated !== groups.translate) {
                    return translated;
                }
                throw new Error(`${translated}, ${groups.translate}`);
            }
        }
        else if (groups.translate) {
            // Normalize white spaces
            return `{{ ${groups.translate} }}`;
        }
        return complete;
    })
        .replaceAll(/(?<tagWithAttributes>(?<beforeTranslateTag><(?<tagName>a|b-(?:badge|link|button|alert)|button|div|h[1-9]|input|label|p|router-link|small|span|t[hd])(?:[^>]*)) v-(?:text|html)="(?<translate>t\$\([^)]*\))"(?<afterTranslateTag>(?:(?!\/?>)[^/>])*>))(?<tagContent>(?:(?!<\/\k<tagName>(?:>|\s|\n)).|\n)*)/g, (_complete, ...args) => {
        const groups = args.pop();
        if (new RegExp(String.raw `<${groups.tagName}[\s>]`, 'g').test(groups.tagContent)) {
            throw new Error(`Nested tags identical to the translated tag are not supported: ${groups.tagWithAttributes}${groups.tagContent}`);
        }
        if (enableTranslation) {
            return groups.tagWithAttributes;
        }
        if (groups.translate) {
            if (groups.translate.includes('+')) {
                this?.log.info(`Ignoring dynamic translation ${groups.translate}`);
            }
            else {
                const translated = replaceTranslations({
                    type: 'vue',
                    content: groups.translate,
                    getWebappTranslation,
                });
                if (translated !== groups.translate) {
                    return `${groups.beforeTranslateTag}${groups.afterTranslateTag}${translated}`;
                }
                throw new Error(`${translated}, ${groups.translate}`);
            }
        }
        return `${groups.beforeTranslateTag}${groups.afterTranslateTag}${groups.tagContent}`;
    });
    if (enableTranslation) {
        return body;
    }
    return replaceTranslationAttributes({ content: body, getWebappTranslation });
}
export function replaceTranslations({ content, type, getWebappTranslation, }) {
    const regex = type === 'ts' ?
        /(?:this.)?(t\$|i18n.t)\((?<key>[^),]*)(?:,\s*{(?<data>[^)]*)})?\)\.toString\(\)/g
        : /t\$\((?<key>[^),]*)(?:,\s*{(?<data>[^)]*)})?\)/g;
    return content.replaceAll(regex, (_complete, ...args) => {
        const groups = args.pop();
        const key = groups.key.slice(1, -1).replaceAll(String.raw `\'`, "'");
        let data;
        if (groups.data) {
            const interpolateMatches = groups.data.matchAll(/(?<field>[^{\s:,}]+)(?:\s*:\s*(?<value>[^,}]+))?/g);
            data = {};
            let templateLiteral = false;
            for (const interpolateMatch of interpolateMatches) {
                let { field, value } = interpolateMatch.groups || {};
                if (/^'.*'$/.test(field) || /^".*"$/.test(field)) {
                    // unwrap field
                    field = field.slice(1, -1);
                }
                value ??= field;
                value = value.trim();
                if (/^\d+$/.test(value)) {
                    // convert integer
                    value = Number.parseInt(value, 10);
                }
                else if (/^'.*'$/.test(value) || /^".*"$/.test(value)) {
                    // extract string value
                    value = value.substring(1, value.length - 1);
                }
                else {
                    // wrap expression
                    if (type === 'vue') {
                        value = `{{ ${value} }}`;
                    }
                    else {
                        value = `\${${value}}`;
                    }
                    templateLiteral = true;
                }
                data[field] = value;
            }
            if (templateLiteral && type === 'ts') {
                return `\`${getWebappTranslation(key, data)}\``;
            }
        }
        if (type === 'vue') {
            return getWebappTranslation(key, data);
        }
        return `'${getWebappTranslation(key, data)}'`;
    });
}
const minimatch = new Minimatch('**/*.{vue,ts}');
export const isTranslatedVueFile = (file) => minimatch.match(file.path);
function translateVueFilesTransform({ enableTranslation, getWebappTranslation, }) {
    return passthrough(file => {
        let newContent;
        if (file.path.endsWith('.vue')) {
            newContent = replaceVueTranslations.call(this, { body: file.contents.toString(), enableTranslation, getWebappTranslation });
        }
        else if (!enableTranslation && file.path.endsWith('.ts')) {
            newContent = replaceTranslations({ type: 'ts', content: file.contents.toString(), getWebappTranslation });
            newContent = removeDeclarations({ content: newContent });
        }
        if (newContent) {
            if (!enableTranslation && /(?<![A-Za-z0-9])t\$/.test(newContent)) {
                throw new Error(`Could not translate ${file.path}:
${newContent}`);
            }
            file.contents = Buffer.from(newContent);
        }
    });
}
export default translateVueFilesTransform;
