/**
 * 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 { escapeRegExp } from 'lodash-es';
const addJavaImportToContent = (content, identifier, { staticImport } = {}) => {
    const importStatement = `import ${staticImport ? 'static ' : ''}${identifier};`;
    return new RegExp(escapeRegExp(importStatement)).test(content) ?
        content
        : content.replace(/(package [\w.]+;\n\n?)/, `$1${importStatement}\n`);
};
export function addJavaImport(contentOrIdentifier, identifierOrType, type) {
    if (typeof identifierOrType === 'string') {
        return addJavaImportToContent(contentOrIdentifier, identifierOrType, type);
    }
    return (content) => addJavaImportToContent(content, contentOrIdentifier, identifierOrType);
}
export function parseJavaAnnotation(annotation) {
    if (annotation === undefined) {
        return {};
    }
    const params = [];
    let current = '';
    let depth = 0;
    // Iterate to find top-level commas
    for (const char of annotation) {
        if (char === '(' || char === '{' || char === '[')
            depth++;
        if (char === ')' || char === '}' || char === ']')
            depth--;
        if (char === ',' && depth === 0) {
            params.push(current.trim());
            current = '';
        }
        else {
            current += char;
        }
    }
    params.push(current.trim());
    // Convert array of strings into a key-value object
    return params.reduce((acc, param) => {
        const eqIdx = param.indexOf('=');
        if (eqIdx === -1) {
            acc.value = param.trim();
            return acc;
        }
        const key = param.slice(0, eqIdx).trim();
        let value = param.slice(eqIdx + 1).trim();
        if (value.startsWith('{')) {
            // Handle Java arrays by stripping braces and splitting
            value = value
                .slice(1, -1)
                .split(/,\s*/)
                .map(v => v.replace(/^"|"$/g, ''));
        }
        else {
            value = value.replace(/^"|"$/g, ''); // Simple strings
        }
        acc[key] = value;
        return acc;
    }, {});
}
const serializeJavaAnnotationParameters = (params) => {
    if (!params) {
        return '';
    }
    const entries = Object.entries(params).map(([key, value]) => [key, Array.isArray(value) ? `{${value.join(', ')}}` : value]);
    if (entries.length === 0) {
        return '';
    }
    else if (entries.length === 1 && entries[0][0] === 'value') {
        return entries[0][1];
    }
    return entries.map(([key, value]) => `${key} = ${value}`).join(', ');
};
const addJavaAnnotationToContent = (content, annotationDef) => {
    const { package: packageName, annotation, parameters } = annotationDef;
    if (packageName) {
        content = addJavaImport(content, `${packageName}.${annotation}`);
    }
    const annotationWithParametersMatches = content.match(new RegExp(String.raw `@${annotation}\((?<oldParameters>[^)]*)\)`));
    let annotationToAdd;
    if (parameters) {
        const oldParameters = annotationWithParametersMatches?.groups?.oldParameters?.trim?.();
        const parsedParameters = parseJavaAnnotation(oldParameters);
        const returnedParameters = parameters(parsedParameters, {
            addKeyValue: (key, value) => {
                const existing = parsedParameters[key];
                if (existing) {
                    if (Array.isArray(existing)) {
                        parsedParameters[key] = Array.isArray(value) ? [...existing, ...value] : [...existing, value];
                    }
                    else {
                        parsedParameters[key] = Array.isArray(value) ? [existing, ...value] : [existing, value];
                    }
                }
                else {
                    parsedParameters[key] = value;
                }
            },
            setKeyValue: (key, value) => {
                const existing = parsedParameters[key];
                if (existing) {
                    if (Array.isArray(existing)) {
                        parsedParameters[key] = Array.isArray(value) ? [...existing, ...value] : [...existing, value];
                    }
                    else {
                        parsedParameters[key] = Array.isArray(value) ? [existing, ...value] : [existing, value];
                    }
                }
                else {
                    parsedParameters[key] = value;
                }
            },
        });
        const serializedParam = typeof returnedParameters === 'string' ? returnedParameters : (serializeJavaAnnotationParameters(returnedParameters ?? parsedParameters));
        annotationToAdd = serializedParam ? `${annotation}(${serializedParam})` : annotation;
    }
    else {
        annotationToAdd = annotation;
    }
    if (annotationWithParametersMatches) {
        content = content.replace(new RegExp(String.raw `@${annotation}\((?<oldParameters>[^)]*)\)`), `@${annotationToAdd}`);
    }
    else if (!new RegExp(escapeRegExp(`\n@${annotationToAdd}\n`)).test(content)) {
        // add the annotation before class or interface
        content = content.replace(/\n([a-w ]*(class|@?interface|enum) )/, `\n@${annotationToAdd}\n$1`);
    }
    return content;
};
export function addJavaAnnotation(contentOrAnnotation, annotation) {
    if (typeof contentOrAnnotation === 'string') {
        return addJavaAnnotationToContent(contentOrAnnotation, annotation);
    }
    return (content) => addJavaAnnotationToContent(content, contentOrAnnotation);
}
