/**
 * 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 assert from 'node:assert';
import { databaseTypes } from "../../../../lib/jhipster/index.js";
import { isReservedTableName } from "../../../../lib/jhipster/reserved-keywords.js";
import BaseApplicationGenerator from "../../../base-application/index.js";
import cleanupTask from "./cleanup.js";
import writeEntitiesTask, { cleanupEntitiesTask } from "./entity-files.js";
import writeTask from "./files.js";
import { getDatabaseTypeMavenDefinition, getH2MavenDefinition, getTestcontainerSupport, javaSqlDatabaseArtifacts, } from "./internal/dependencies.js";
import { getDBCExtraOption, getJdbcUrl, getR2dbcUrl } from "./support/index.js";
const { SQL } = databaseTypes;
export default class SqlGenerator extends BaseApplicationGenerator {
    async beforeQueue() {
        if (!this.fromBlueprint) {
            await this.composeWithBlueprints();
        }
        if (!this.delegateToBlueprint) {
            await this.dependsOnBootstrap('spring-boot');
            await this.dependsOnJHipster('jhipster:java:domain');
        }
    }
    get composing() {
        return this.asComposingTaskGroup({
            async composing() {
                await this.composeWithJHipster('jhipster:spring-boot:liquibase');
            },
        });
    }
    get [BaseApplicationGenerator.COMPOSING]() {
        return this.delegateTasksToBlueprint(() => this.composing);
    }
    get preparing() {
        return this.asPreparingTaskGroup({
            async preparing({ application, applicationDefaults }) {
                application.devDatabaseExtraOptions = getDBCExtraOption(application.devDatabaseType);
                application.prodDatabaseExtraOptions = getDBCExtraOption(application.prodDatabaseType);
                const { prodDatabaseType, springBoot4, dockerContainers } = application;
                applicationDefaults(getTestcontainerSupport({ databaseType: prodDatabaseType, springBoot4, dockerContainers }));
                applicationDefaults({
                    implementsDynamicSourceTestcontainersSupport: data => Boolean(!application.devDatabaseTypeH2Any && data.testcontainerClass && data.springBoot4),
                    useTestcontainersV1: data => Boolean(data.testcontainerClassPackage?.endsWith('.containers')),
                });
            },
        });
    }
    get [BaseApplicationGenerator.PREPARING]() {
        return this.delegateTasksToBlueprint(() => this.preparing);
    }
    get preparingEachEntity() {
        return this.asPreparingEachEntityTaskGroup({
            prepareEntity({ entity }) {
                entity.relationships.forEach(relationship => {
                    if (relationship.persistableRelationship === undefined && relationship.relationshipType === 'many-to-many') {
                        relationship.persistableRelationship = true;
                    }
                });
                const entityAny = entity;
                if (isReservedTableName(entity.entityInstance, entity.prodDatabaseType ?? entity.databaseType) && entityAny.jhiPrefix) {
                    entity.entityJpqlInstance = `${entityAny.jhiPrefix}${entity.entityClass}`;
                }
                else {
                    entity.entityJpqlInstance = entity.entityInstance;
                }
            },
        });
    }
    get [BaseApplicationGenerator.PREPARING_EACH_ENTITY]() {
        return this.delegateTasksToBlueprint(() => this.preparingEachEntity);
    }
    get preparingEachEntityRelationship() {
        return this.asPreparingEachEntityRelationshipTaskGroup({
            prepareRelationship({ application, relationship }) {
                if (application.reactive) {
                    relationship.relationshipSqlSafeName =
                        isReservedTableName(relationship.relationshipName, SQL) ? `e_${relationship.relationshipName}` : relationship.relationshipName;
                }
            },
        });
    }
    get [BaseApplicationGenerator.PREPARING_EACH_ENTITY_RELATIONSHIP]() {
        return this.delegateTasksToBlueprint(() => this.preparingEachEntityRelationship);
    }
    get writing() {
        return this.asWritingTaskGroup({
            async cleanup({ application, control }) {
                await control.cleanupFiles({
                    '9.0.0-beta.1': [
                        [
                            !application.devDatabaseTypeH2Any && application.springBoot4,
                            `${application.srcTestJava}config/EmbeddedSQL.java`,
                            `${application.srcTestJava}config/SqlTestContainer.java`,
                            `${application.srcTestJava}config/SqlTestContainersSpringContextCustomizerFactory.java`,
                            `${application.srcTestResources}META-INF/spring.factories`,
                        ],
                        [application.prodDatabaseTypeMysql, `${application.srcTestJava}config/MysqlTestContainer.java`],
                        [application.prodDatabaseTypeMariadb, `${application.srcTestJava}config/MariadbTestContainer.java`],
                        [application.prodDatabaseTypeMssql, `${application.srcTestJava}config/MsSqlTestContainer.java`],
                        [application.prodDatabaseTypePostgresql, `${application.srcTestJava}config/PostgreSqlTestContainer.java`],
                    ],
                });
            },
            cleanupTask,
            writeTask,
        });
    }
    get [BaseApplicationGenerator.WRITING]() {
        return this.delegateTasksToBlueprint(() => this.writing);
    }
    get writingEntities() {
        return this.asWritingEntitiesTaskGroup({
            cleanupEntitiesTask,
            writeEntitiesTask,
        });
    }
    get [BaseApplicationGenerator.WRITING_ENTITIES]() {
        return this.delegateTasksToBlueprint(() => this.writingEntities);
    }
    get postWriting() {
        return this.asPostWritingTaskGroup({
            addTestSpringFactory({ source, application }) {
                if (application.implementsDynamicSourceTestcontainersSupport)
                    return;
                source.addTestSpringFactory?.({
                    key: 'org.springframework.test.context.ContextCustomizerFactory',
                    value: `${application.packageName}.config.SqlTestContainersSpringContextCustomizerFactory`,
                });
            },
            addLog({ source }) {
                source.addTestLog?.({
                    name: 'org.hibernate.orm.incubating',
                    level: 'ERROR',
                });
            },
            addDependencies({ application, source }) {
                const { reactive, javaDependencies, packageFolder } = application;
                assert.ok(javaDependencies, 'javaDependencies is required');
                assert.ok(packageFolder, 'packageFolder is required');
                const { prodDatabaseType, devDatabaseTypeH2Any } = application;
                const dbDefinitions = getDatabaseTypeMavenDefinition(prodDatabaseType, {
                    inProfile: devDatabaseTypeH2Any ? 'prod' : undefined,
                    javaDependencies,
                });
                const h2Definitions = devDatabaseTypeH2Any ?
                    getH2MavenDefinition({
                        implementsTestcontainersSupport: Boolean(application.testcontainerClass),
                        packageFolder,
                    })
                    : undefined;
                if (application.springBoot4) {
                    source.addSpringBootModule('spring-boot-h2console');
                    // TODO move spring-boot-h2console to dev profile
                    // source.addJavaDependencies!([{ groupId: 'org.springframework.boot', artifactId: 'spring-boot-h2console' }], { dev: true });
                }
                source.addSpringBootModule?.(`spring-boot-starter-data-${reactive ? 'r2dbc' : 'jpa'}`);
                if (application.implementsDynamicSourceTestcontainersSupport) {
                    source.addSpringBootModule?.('spring-boot-testcontainers');
                }
                if (!application.reactive) {
                    source.addIntegrationTestAnnotation?.({
                        annotation: 'SpringBootTest',
                        parameters: (_, cb) => cb.addKeyValue('classes', `${application.packageName}.config.JacksonHibernateConfiguration.class`),
                    });
                }
                source.addJavaDefinitions?.({
                    condition: reactive,
                    dependencies: [
                        {
                            groupId: 'commons-beanutils',
                            artifactId: 'commons-beanutils',
                            version: javaDependencies['commons-beanutils'],
                            exclusions: [{ groupId: 'commons-logging', artifactId: 'commons-logging' }],
                        },
                        { groupId: 'jakarta.persistence', artifactId: 'jakarta.persistence-api' },
                    ],
                    mavenDefinition: dbDefinitions.r2dbc,
                }, {
                    condition: !reactive,
                    dependencies: [
                        {
                            groupId: application.springBoot4 ? 'tools.jackson.datatype' : 'com.fasterxml.jackson.datatype',
                            artifactId: application.springBoot4 ? 'jackson-datatype-hibernate7' : 'jackson-datatype-hibernate6',
                        },
                        { groupId: 'org.hibernate.orm', artifactId: 'hibernate-core' },
                        { groupId: 'org.hibernate.validator', artifactId: 'hibernate-validator' },
                        { groupId: 'org.springframework.security', artifactId: 'spring-security-data' },
                        {
                            scope: 'annotationProcessor',
                            groupId: 'org.hibernate.orm',
                            artifactId: application.springBoot4 ? 'hibernate-processor' : 'hibernate-jpamodelgen',
                        },
                    ],
                    mavenDefinition: {
                        dependencies: [
                            {
                                inProfile: 'IDE',
                                groupId: 'org.hibernate.orm',
                                artifactId: application.springBoot4 ? 'hibernate-processor' : 'hibernate-jpamodelgen',
                            },
                        ],
                    },
                }, {
                    dependencies: [
                        {
                            groupId: application.springBoot4 ? 'tools.jackson.module' : 'com.fasterxml.jackson.module',
                            artifactId: 'jackson-module-jaxb-annotations',
                        },
                        { groupId: 'com.zaxxer', artifactId: 'HikariCP' },
                        { scope: 'annotationProcessor', groupId: 'org.glassfish.jaxb', artifactId: 'jaxb-runtime' },
                        { scope: 'test', groupId: 'org.testcontainers', artifactId: 'testcontainers-jdbc' },
                        { scope: 'test', groupId: 'org.testcontainers', artifactId: 'testcontainers-junit-jupiter' },
                        { scope: 'test', groupId: 'org.testcontainers', artifactId: 'testcontainers' },
                    ],
                    mavenDefinition: dbDefinitions.jdbc,
                }, {
                    condition: devDatabaseTypeH2Any,
                    mavenDefinition: h2Definitions?.jdbc,
                }, {
                    condition: devDatabaseTypeH2Any && reactive,
                    mavenDefinition: h2Definitions?.r2dbc,
                });
                if (application.buildToolGradle) {
                    const artifacts = javaSqlDatabaseArtifacts[prodDatabaseType];
                    source.addJavaDefinition({
                        dependencies: [
                            artifacts.jdbc,
                            artifacts.testContainer,
                            ...(reactive && 'r2dbc' in artifacts && artifacts.r2dbc ? [artifacts.r2dbc] : []),
                        ],
                    }, { gradleFile: devDatabaseTypeH2Any ? 'gradle/profile_prod.gradle' : 'build.gradle' });
                    if (devDatabaseTypeH2Any) {
                        source.addJavaDefinition({ dependencies: [javaSqlDatabaseArtifacts.h2.jdbc, ...(reactive ? [javaSqlDatabaseArtifacts.h2.r2dbc] : [])] }, { gradleFile: 'gradle/profile_dev.gradle' });
                    }
                }
            },
            integrationTest({ application, source }) {
                if (application.implementsDynamicSourceTestcontainersSupport) {
                    source.editJavaFile(`${application.javaPackageTestDir}IntegrationTest.java`, {
                        imports: [`${application.packageName}.config.DatabaseTestcontainer`],
                        annotations: [
                            {
                                package: 'org.springframework.boot.testcontainers.context',
                                annotation: 'ImportTestcontainers',
                                parameters: (_, cb) => cb.addKeyValue('value', 'DatabaseTestcontainer.class'),
                            },
                        ],
                    });
                }
                else {
                    source.editJavaFile(`${application.javaPackageTestDir}IntegrationTest.java`, {
                        annotations: [{ package: `${application.packageName}.config`, annotation: 'EmbeddedSQL' }],
                    });
                }
            },
            nativeHints({ application, source }) {
                if (!application.graalvmSupport)
                    return;
                if (!application.reactive) {
                    // Latest hibernate-core version supported by Reachability Repository is 6.5.0.Final
                    // Hints may be dropped if newer version is supported
                    // https://github.com/oracle/graalvm-reachability-metadata/blob/master/metadata/org.hibernate.orm/hibernate-core/index.json
                    source.addNativeHint({
                        publicConstructors: ['org.hibernate.binder.internal.BatchSizeBinder.class'],
                    });
                }
                source.addNativeHint({
                    publicMethods: ['com.zaxxer.hikari.HikariDataSource.class'],
                });
            },
            async nativeMavenBuildTool({ application, source }) {
                if (!application.buildToolMaven || !application.graalvmSupport)
                    return;
                source.addMavenProperty({ property: 'spring.h2.console.enabled', value: 'true' });
            },
            async nativeGradleBuildTool({ application, source }) {
                if (!application.buildToolGradle || !application.graalvmSupport)
                    return;
                const { reactive, javaManagedProperties } = application;
                if (!reactive) {
                    source.addGradleDependencyCatalogVersion({ name: 'hibernate', version: javaManagedProperties['hibernate.version'] });
                    source.addGradleDependencyCatalogPlugin({
                        addToBuild: true,
                        pluginName: 'hibernate',
                        id: 'org.hibernate.orm',
                        'version.ref': 'hibernate',
                    });
                }
            },
        });
    }
    get [BaseApplicationGenerator.POST_WRITING]() {
        return this.delegateTasksToBlueprint(() => this.postWriting);
    }
    get postWritingEntities() {
        return this.asPostWritingEntitiesTaskGroup({
            async jsonFilter({ application, entities, source }) {
                if (application.reactive || !application.graalvmSupport)
                    return;
                for (const entity of entities.filter(({ builtIn, builtInUser, embedded }) => builtInUser || (!builtIn && !embedded))) {
                    source.editJavaFile(`${application.srcMainJava}/${entity.entityAbsoluteFolder}/domain/${entity.entityClass}.java`, {
                        annotations: [
                            {
                                package: 'com.fasterxml.jackson.annotation',
                                annotation: 'JsonFilter',
                                parameters: () => '"lazyPropertyFilter"',
                            },
                        ],
                    });
                }
            },
        });
    }
    get [BaseApplicationGenerator.POST_WRITING_ENTITIES]() {
        return this.delegateTasksToBlueprint(() => this.postWritingEntities);
    }
    /**
     * @private
     * Returns the JDBC URL for a databaseType
     */
    getJDBCUrl(...args) {
        return getJdbcUrl(...args);
    }
    /**
     * @private
     * Returns the R2DBC URL for a databaseType
     */
    getR2DBCUrl(...args) {
        return getR2dbcUrl(...args);
    }
}
