/**
 * 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 { extname } from 'node:path';
import { passthrough } from '@yeoman/transform';
import { Minimatch } from 'minimatch';
import { createJhiTransformTranslateReplacer, createJhiTransformTranslateStringifyReplacer, createJhiTranslateReplacer, escapeHtmlTranslationValue, escapeTsTranslationValue, } from "../../languages/support/index.js";
const PLACEHOLDER_REGEX = /(?:placeholder|title)=['|"](\{\{\s?['|"]([a-zA-Z0-9.\-_]+)['|"]\s?\|\s?translate\s?\}\})['|"]/.source;
const JHI_TRANSLATE_REGEX = /(\n?\s*[a-z][a-zA-Z]*Translate="[a-zA-Z0-9 +{}'_!?.]+")/.source;
const TRANSLATE_VALUES_REGEX = /(\n?\s*\[translateValues\]="\{(?:(?!\}").)*?\}")/.source;
const TRANSLATE_REGEX = [JHI_TRANSLATE_REGEX, TRANSLATE_VALUES_REGEX].join('|');
/**
 * Replace translation key with translation values
 *
 * @param {import('../generator-base.js')} generator
 * @param getWebappTranslation
 * @param {string} content
 * @param {string} regexSource regular expression to find keys
 * @returns {string}
 */
function replaceTranslationKeysWithText(getWebappTranslation, content, regexSource, { keyIndex = 1, replacementIndex = 1, escape, } = {}) {
    const regex = new RegExp(regexSource, 'g');
    const allMatches = content.matchAll(regex);
    for (const match of allMatches) {
        // match is now the next match, in array form and our key is at index 1, index 1 is replace target.
        const key = match[keyIndex];
        const target = match[replacementIndex];
        let translation = getWebappTranslation(key);
        if (escape) {
            translation = escape(translation, match);
        }
        content = content.replace(target, translation);
    }
    return content;
}
/**
 *
 * @param {import('../generator-base.js')} generator reference to the generator
 * @param {string} content html content
 * @param {string} jsKey
 * @returns string with jsKey value replaced
 */
function replaceJSTranslation(getWebappTranslation, content, jsKey) {
    return replaceTranslationKeysWithText(getWebappTranslation, content, String.raw `${jsKey}\s?:\s?['|"]([a-zA-Z0-9.\-_]+\.[a-zA-Z0-9.\-_]+)['|"]`, {
        escape: (translation, match) => translation.replaceAll(match[0].slice(-1), `\\${match[0].slice(-1)}`),
    });
}
/**
 *
 * @param {import('../generator-base.js')} generator reference to the generator
 * @param {string} content html content
 * @returns string with pageTitle replaced
 */
function replacePageTitles(getWebappTranslation, content) {
    return replaceJSTranslation(getWebappTranslation, content, 'title');
}
function replacePlaceholders(getWebappTranslation, content) {
    return replaceTranslationKeysWithText(getWebappTranslation, content, PLACEHOLDER_REGEX, { keyIndex: 2 });
}
/**
 * Replace error code translation key with translated message
 */
function replaceErrorMessage(getWebappTranslation, content) {
    return replaceJSTranslation(getWebappTranslation, content, 'errorMessage');
}
/**
 * Creates a `jhiTranslate` attribute with optional translateValues.
 * Or the translation value if translation is disabled.
 */
const tagTranslation = (getWebappTranslation, { enableTranslation, jhiPrefix }, { key, parsedInterpolate, prefix, suffix }) => {
    const translatedValueInterpolate = parsedInterpolate ? Object.fromEntries(Object.entries(parsedInterpolate).map(([key, value]) => [key, `{{ ${value} }}`])) : undefined;
    const translatedValue = escapeHtmlTranslationValue(getWebappTranslation(key, translatedValueInterpolate));
    if (enableTranslation) {
        const translateValuesAttr = parsedInterpolate ?
            ` [translateValues]="{ ${Object.entries(parsedInterpolate)
                .map(([key, value]) => `${key}: ${value}`)
                .join(', ')} }"`
            : '';
        return ` ${jhiPrefix}Translate="${key}"${translateValuesAttr}${prefix}${translatedValue}${suffix}`;
    }
    return `${prefix}${translatedValue}${suffix}`;
};
/**
 * Creates a `jhiTranslate` attribute with translateValues.
 * Or the translation value if translation is disabled.
 */
const validationTagTranslation = (getWebappTranslation, { enableTranslation, jhiPrefix }, { key, parsedInterpolate, prefix, suffix }) => {
    if (!parsedInterpolate || Object.keys(parsedInterpolate).length === 0) {
        throw new Error(`No interpolation values found for translation key ${key}, use __jhiTranslateTag__ instead.`);
    }
    const translatedValue = escapeHtmlTranslationValue(getWebappTranslation(key, parsedInterpolate));
    if (enableTranslation) {
        const translateValuesAttr = parsedInterpolate ?
            ` [translateValues]="{ ${Object.entries(parsedInterpolate)
                .map(([key, value]) => `${key}: '${value}'`)
                .join(', ')} }"`
            : '';
        return ` ${jhiPrefix}Translate="${key}"${translateValuesAttr}${prefix}${translatedValue}${suffix}`;
    }
    return `${prefix}${translatedValue}${suffix}`;
};
/**
 * Creates a `jhiTranslate` attribute with optional translateValues.
 * Or the translation value if translation is disabled.
 */
const tagPipeTranslation = (getWebappTranslation, { enableTranslation, jhiPrefix }, { key, parsedInterpolate, prefix, suffix }) => {
    if (!parsedInterpolate || Object.keys(parsedInterpolate).length === 0) {
        throw new Error(`No interpolation values found for translation key ${key}, use __jhiTranslateTag__ instead.`);
    }
    const translatedValueInterpolate = Object.fromEntries(Object.entries(parsedInterpolate).map(([key, value]) => [key, getWebappTranslation(value)]));
    const translatedValue = escapeHtmlTranslationValue(getWebappTranslation(key, translatedValueInterpolate));
    if (enableTranslation) {
        const translateValuesAttr = ` [translateValues]="{ ${Object.entries(parsedInterpolate)
            .map(([key, value]) => `${key}: ('${value}' | translate)`)
            .join(', ')} }"`;
        return ` ${jhiPrefix}Translate="${key}"${translateValuesAttr}${prefix}${translatedValue}${suffix}`;
    }
    return `${prefix}${translatedValue}${suffix}`;
};
/**
 * Creates a `jhiTranslate` attribute with optional translateValues.
 * Or the translation value if translation is disabled.
 */
const tagEnumTranslation = (getWebappTranslation, { enableTranslation, jhiPrefix }, { key, parsedInterpolate, prefix, suffix }) => {
    if (!parsedInterpolate?.value) {
        throw new Error(`Value is required for TagEnum ${key}.`);
    }
    const { value, fallback } = parsedInterpolate;
    const translatedValue = `{{ ${JSON.stringify(getWebappTranslation(key))}[${value}]${fallback ? ` || ${fallback}` : ''} }}`;
    if (enableTranslation) {
        return ` [${jhiPrefix}Translate]="'${key}.' + (${parsedInterpolate?.value})"${prefix}${translatedValue}${suffix}`;
    }
    return `${prefix}${translatedValue}${suffix}`;
};
/**
 * Creates a `translate` pipe.
 * Or the translation value if translation is disabled.
 */
const pipeTranslation = (getWebappTranslation, { enableTranslation }, { key, prefix, suffix }) => {
    if (enableTranslation) {
        return `${prefix}{{ '${key}' | translate }}${suffix}`;
    }
    return `${prefix}${escapeHtmlTranslationValue(getWebappTranslation(key))}${suffix}`;
};
/**
 * Get translation value.
 */
const valueTranslation = (getWebappTranslation, _replacerOptions, { filePath, key, prefix, suffix }) => {
    let translationValue = getWebappTranslation(key);
    const fileExtension = extname(filePath);
    if (fileExtension === '.html') {
        translationValue = escapeHtmlTranslationValue(translationValue);
    }
    else if (fileExtension === '.ts') {
        translationValue = escapeTsTranslationValue(translationValue);
    }
    return `${prefix}${translationValue}${suffix}`;
};
/**
 * Creates a `translate` pipe.
 * Or the translation value if translation is disabled.
 */
const pipeEnumTranslation = (getWebappTranslation, { enableTranslation }, { key, parsedInterpolate, prefix, suffix }) => {
    if (!parsedInterpolate?.value) {
        throw new Error(`Value is required for TagEnum ${key}.`);
    }
    const { value, fallback } = parsedInterpolate;
    if (enableTranslation) {
        return `${prefix}{{ '${key}.' + ${value} | translate }}${suffix}`;
    }
    const translatedValue = `{{ ${JSON.stringify(getWebappTranslation(key))}[${value}]${fallback ? ` ?? ${fallback}` : ''} }}`;
    return `${prefix}${translatedValue}${suffix}`;
};
const replaceImplementations = {
    Tag: tagTranslation,
    TagPipe: tagPipeTranslation,
    TagEnum: tagEnumTranslation,
    ValidationTag: validationTagTranslation,
    Pipe: pipeTranslation,
    PipeEnum: pipeEnumTranslation,
    Value: valueTranslation,
};
/**
 * Replace and cleanup translations.
 *
 * @type {import('../generator-base.js').EditFileCallback}
 * @this {import('../generator-base.js')}
 */
export const createTranslationReplacer = (getWebappTranslation, opts) => {
    const htmlJhiTranslateReplacer = createJhiTransformTranslateReplacer(getWebappTranslation, { escapeHtml: true });
    const htmlJhiTranslateStringifyReplacer = createJhiTransformTranslateStringifyReplacer(getWebappTranslation);
    let translationReplacer;
    const enableTranslation = typeof opts === 'boolean' ? opts : opts.enableTranslation;
    if (typeof opts !== 'boolean') {
        translationReplacer = createJhiTranslateReplacer(optsReplacer => {
            const replacer = replaceImplementations[optsReplacer.type] ??
                (() => {
                    throw new Error(`Translation type not supported ${optsReplacer.type}`);
                });
            return replacer(getWebappTranslation, opts, optsReplacer);
        }, { prefixPattern: String.raw `>\s*`, suffixPattern: String.raw `\s*<` });
    }
    return function replaceAngularTranslations(content, filePath) {
        if (filePath.endsWith('.html') && !enableTranslation) {
            content = content.replace(new RegExp(TRANSLATE_REGEX, 'g'), '');
            content = replacePlaceholders(getWebappTranslation, content);
        }
        // Translate html files and inline templates.
        if (/(:?\.html|.ts)$/.test(filePath)) {
            content = htmlJhiTranslateReplacer(content);
            content = htmlJhiTranslateStringifyReplacer(content);
        }
        if (/(:?\.html|.ts)$/.test(filePath)) {
            content = translationReplacer ? translationReplacer?.(content, filePath) : content;
        }
        if (!enableTranslation) {
            if (/(:?routes?|module)\.ts$/.test(filePath)) {
                content = replacePageTitles(getWebappTranslation, content);
            }
            if (filePath.endsWith('error.route.ts')) {
                content = replaceErrorMessage(getWebappTranslation, content);
            }
        }
        return content;
    };
};
const minimatch = new Minimatch('**/*{.html,.ts}');
export const isTranslatedAngularFile = (file) => minimatch.match(file.path);
export const translateAngularFilesTransform = (getWebappTranslation, opts) => {
    const translate = createTranslationReplacer(getWebappTranslation, opts);
    return passthrough(file => {
        file.contents = Buffer.from(translate(file.contents.toString(), file.path));
    });
};
