import chalk from 'chalk';
import { intersection } from 'lodash-es';
import { applicationOptions, applicationTypes, authenticationTypes, cacheTypes, databaseTypes, testFrameworkTypes, } from '../../lib/jhipster/index.js';
import { R2DBC_DB_OPTIONS, SQL_DB_OPTIONS } from '../server/support/database.js';
import { asPromptingTask } from '../base-application/support/index.js';
const { OptionNames } = applicationOptions;
const { GATEWAY, MONOLITH } = applicationTypes;
const { CAFFEINE, EHCACHE, HAZELCAST, INFINISPAN, MEMCACHED, REDIS } = cacheTypes;
const { OAUTH2 } = authenticationTypes;
const { CASSANDRA, H2_DISK, H2_MEMORY, MONGODB, NEO4J, SQL, COUCHBASE } = databaseTypes;
const { WEBSOCKET, SEARCH_ENGINE, ENABLE_SWAGGER_CODEGEN } = OptionNames;
const NO_DATABASE = databaseTypes.NO;
const NO_CACHE_PROVIDER = cacheTypes.NO;
const { GATLING, CUCUMBER } = testFrameworkTypes;
export const askForServerSideOpts = asPromptingTask(async function ({ control }) {
    if (control.existingProject && !this.options.askAnswered)
        return;
    const { applicationType, authenticationType, reactive } = this.jhipsterConfigWithDefaults;
    await this.prompt([
        {
            type: 'list',
            name: 'databaseType',
            message: `Which ${chalk.yellow('*type*')} of database would you like to use?`,
            choices: () => {
                const opts = [];
                if (!reactive) {
                    opts.push({
                        value: SQL,
                        name: 'SQL (H2, PostgreSQL, MySQL, MariaDB, Oracle, MSSQL)',
                    });
                }
                else {
                    opts.push({
                        value: SQL,
                        name: 'SQL (H2, PostgreSQL, MySQL, MariaDB, MSSQL)',
                    });
                }
                opts.push({
                    value: MONGODB,
                    name: 'MongoDB',
                });
                if (authenticationType !== OAUTH2) {
                    opts.push({
                        value: CASSANDRA,
                        name: 'Cassandra',
                    });
                }
                opts.push({
                    value: 'couchbase',
                    name: '[BETA] Couchbase',
                });
                opts.push({
                    value: NEO4J,
                    name: '[BETA] Neo4j',
                });
                opts.push({
                    value: NO_DATABASE,
                    name: 'No database',
                });
                return opts;
            },
            default: this.jhipsterConfigWithDefaults.databaseType,
        },
        {
            when: response => response.databaseType === SQL,
            type: 'list',
            name: 'prodDatabaseType',
            message: `Which ${chalk.yellow('*production*')} database would you like to use?`,
            choices: reactive ? R2DBC_DB_OPTIONS : SQL_DB_OPTIONS,
            default: this.jhipsterConfigWithDefaults.prodDatabaseType,
        },
        {
            when: response => response.databaseType === SQL,
            type: 'list',
            name: 'devDatabaseType',
            message: `Which ${chalk.yellow('*development*')} database would you like to use?`,
            choices: response => {
                const currentDatabase = SQL_DB_OPTIONS.find(it => it.value === response.prodDatabaseType);
                return [
                    {
                        ...currentDatabase,
                        name: `${currentDatabase.name} (requires Docker or manually configured database)`,
                    },
                ].concat([
                    { value: H2_DISK, name: `H2 with disk-based persistence` },
                    { value: H2_MEMORY, name: `H2 with in-memory persistence` },
                ]);
            },
            default: this.jhipsterConfigWithDefaults.devDatabaseType,
        },
        {
            when: !reactive,
            type: 'list',
            name: 'cacheProvider',
            message: 'Which cache do you want to use? (Spring cache abstraction)',
            choices: [
                {
                    value: EHCACHE,
                    name: 'Ehcache (local cache, for a single node)',
                },
                {
                    value: CAFFEINE,
                    name: 'Caffeine (local cache, for a single node)',
                },
                {
                    value: HAZELCAST,
                    name: 'Hazelcast (distributed cache for multiple nodes)',
                },
                {
                    value: INFINISPAN,
                    name: 'Infinispan (hybrid cache, for multiple nodes)',
                },
                {
                    value: MEMCACHED,
                    name: 'Memcached (distributed cache) - Warning, when using an SQL database, this will disable the Hibernate 2nd level cache!',
                },
                {
                    value: REDIS,
                    name: 'Redis (distributed cache)',
                },
                {
                    value: NO_CACHE_PROVIDER,
                    name: 'No cache - Warning, when using an SQL database, this will disable the Hibernate 2nd level cache!',
                },
            ],
            default: this.jhipsterConfigWithDefaults.cacheProvider,
        },
        {
            when: answers => ((answers.cacheProvider !== NO_CACHE_PROVIDER && answers.cacheProvider !== MEMCACHED) || applicationType === GATEWAY) &&
                answers.databaseType === SQL &&
                !reactive,
            type: 'confirm',
            name: 'enableHibernateCache',
            message: 'Do you want to use Hibernate 2nd level cache?',
            default: this.jhipsterConfigWithDefaults.enableHibernateCache,
        },
    ], this.config);
});
export const askForOptionalItems = asPromptingTask(async function askForOptionalItems({ control }) {
    if (control.existingProject && !this.options.askAnswered)
        return;
    const { applicationType, reactive, databaseType, serverSideOptions = [] } = this.jhipsterConfigWithDefaults;
    const choices = [];
    if ([SQL, MONGODB, NEO4J].includes(databaseType)) {
        choices.push({
            name: 'Elasticsearch as search engine',
            value: 'searchEngine:elasticsearch',
        });
    }
    if (databaseType === COUCHBASE) {
        choices.push({
            name: 'Couchbase FTS as search engine',
            value: 'searchEngine:couchbase',
        });
    }
    if (!reactive) {
        if (applicationType === MONOLITH || applicationType === GATEWAY) {
            choices.push({
                name: 'WebSockets using Spring Websocket',
                value: 'websocket:spring-websocket',
            });
        }
    }
    choices.push({
        name: 'Apache Kafka as asynchronous messages broker',
        value: 'messageBroker:kafka',
    });
    choices.push({
        name: 'Apache Pulsar as asynchronous messages broker',
        value: 'messageBroker:pulsar',
    });
    choices.push({
        name: 'API first development using OpenAPI-generator',
        value: 'enableSwaggerCodegen:true',
    });
    if (choices.length > 0) {
        const selectedChoices = [WEBSOCKET, SEARCH_ENGINE, 'messageBroker', ENABLE_SWAGGER_CODEGEN]
            .map(property => [property, this.jhipsterConfig[property]])
            .filter(([, value]) => value !== undefined)
            .map(([key, value]) => `${key}:${value}`)
            .filter(Boolean);
        choices.forEach(choice => {
            choice.checked = selectedChoices.includes(choice.value);
        });
        const answers = await this.prompt({
            type: 'checkbox',
            name: 'serverSideOptions',
            message: 'Which other technologies would you like to use?',
            choices,
            default: selectedChoices,
        });
        Object.assign(this.jhipsterConfig, Object.fromEntries(answers.serverSideOptions
            .map(it => it.split(':'))
            .map(([key, value]) => [key, ['true', 'false'].includes(value) ? value === 'true' : value])));
    }
});
export const askForServerTestOpts = asPromptingTask(async function ({ control }) {
    if (control.existingProject && this.options.askAnswered !== true)
        return;
    const testFrameworks = this.jhipsterConfigWithDefaults.testFrameworks ?? [];
    const answers = await this.prompt([
        {
            type: 'checkbox',
            name: 'serverTestFrameworks',
            message: 'Besides JUnit, which testing frameworks would you like to use?',
            choices: [
                { name: 'Gatling', value: GATLING, checked: testFrameworks.includes(GATLING) },
                { name: 'Cucumber', value: CUCUMBER, checked: testFrameworks.includes(CUCUMBER) },
            ],
            default: intersection([GATLING, CUCUMBER], this.jhipsterConfigWithDefaults.testFrameworks),
        },
    ]);
    this.jhipsterConfig.testFrameworks = [...new Set([...(this.jhipsterConfig.testFrameworks ?? []), ...answers.serverTestFrameworks])];
});
export async function askForOrmTool({ control }) {
    if (control.existingProject && !this.options.askAnswered)
        return;
    if (this.jhipsterConfig.databaseType !== 'sql' || this.jhipsterConfig.applicationType === 'gateway' || this.jhipsterConfig.reactive) {
        this.jhipsterConfig.ormTool = 'hibernate';
        return;
    }
    const choices = [
        {
            value: 'mybatis',
            name: 'Mybatis-Plus',
        },
        {
            value: 'hibernate',
            name: 'Hibernate',
        },
    ];
    const answers = await this.prompt([
        {
            type: 'list',
            name: 'ormTool',
            message: `Which ${chalk.yellow('OrmTools')} would you like to use for your application?`,
            choices,
            default: 'mybatis',
        },
    ]);
    this.jhipsterConfig.ormTool = answers.ormTool;
}
export async function askForUserIdType({ control }) {
    if (control.existingProject && !this.options.askAnswered)
        return;
    if (OAUTH2 === this.jhipsterConfig.authenticationType && CASSANDRA === this.jhipsterConfig.databaseType) {
        this.jhipsterConfig.userIdType = 'string';
        return;
    }
    const choices = [
        {
            value: 'long',
            name: 'Long',
        },
        {
            value: 'string',
            name: 'String',
        },
        {
            value: 'uuid',
            name: 'UUID',
        },
    ];
    const answers = await this.prompt([
        {
            type: 'list',
            name: 'userIdType',
            message: `Which ${chalk.yellow('*type*')} of user id type would you like to use?`,
            choices,
            default: 'long',
        },
    ]);
    this.jhipsterConfig.userIdType = answers.userIdType;
}
export async function askJobScheduler({ control }) {
    this.jhipsterConfig.jobScheduler = this.jhipsterConfig.jobScheduler = 'quartz';
}
export async function askUseLombok({ control }) {
    if (control.existingProject && !this.options.askAnswered) {
        return;
    }
    const answers = await this.prompt([
        {
            type: 'confirm',
            name: 'useLombok',
            message: `Would you like to use the ${chalk.yellow('Lombok')} for your application?`,
            default: true,
        },
    ]);
    this.jhipsterConfig.useLombok = answers.useLombok;
}
export async function askForIdmServicePath({ control }) {
    if ((control.existingProject && !this.options.askAnswered) ||
        this.jhipsterConfig.applicationType !== 'microservice' ||
        (this.jhipsterConfig.applicationType === 'microservice' && this.jhipsterConfig.builtInServices?.includes('idm'))) {
        if (this.jhipsterConfig.applicationType === 'microservice' && this.jhipsterConfig.builtInServices?.includes('idm')) {
            this.jhipsterConfig.skipUserManagement = false;
        }
        return;
    }
    const answers = await this.prompt([
        {
            type: 'input',
            name: 'idmServicePath',
            message: `Please input ${chalk.yellow('idm service')} project path ?`,
            default: '../system/',
            validate: (input) => (input.length === 0 ? 'Please enter your idm service path' : true),
        },
    ]);
    this.jhipsterConfig.skipUserManagement = true;
    this.jhipsterConfig.idmServicePath = answers.idmServicePath;
}
export async function askForSoftDelete({ control }) {
    if (control.existingProject && this.options.askAnswered !== true)
        return;
    await this.prompt({
        type: 'confirm',
        name: 'softDelete',
        message: `Would you like to use the ${chalk.yellow('SOFT_DELETE')} for your application?`,
        default: false,
    }, this.config);
}
export async function askFrontOrBack({ control }) {
    if (control.existingProject && this.options.askAnswered !== true)
        return;
    if (control.existingProject) {
        this.jhipsterConfig.frontOrBack = this.config.get('frontOrBack') || ['front', 'back'];
        return;
    }
    const config = this.jhipsterConfigWithDefaults;
    const choices = ['back', 'front'];
    choices.push(...(config.frontOrBack || []));
    if (config.applicationType === 'commonmodule' || config.applicationType === 'microservice') {
        choices.splice(choices.indexOf('front'), 1);
    }
    if (config.applicationType === 'monolith' && config.skipServer) {
        choices.splice(choices.indexOf('back'), 1);
    }
    if (config.applicationType === 'monolith' && config.skipClient) {
        choices.splice(choices.indexOf('front'), 1);
    }
    if (config.applicationType === 'gateway' && config.skipClient) {
        choices.splice(choices.indexOf('front'), 1);
    }
    if (config.applicationType === 'gateway' && config.skipServer) {
        choices.splice(choices.indexOf('back'), 1);
    }
    const choicesData = [
        {
            value: 'all',
            name: 'All',
        },
        {
            value: 'front',
            name: 'Front End',
        },
        {
            value: 'back',
            name: 'Back End',
        },
        {
            value: 'mobile',
            name: 'Mobile End',
        },
    ];
    const answer = await this.prompt([
        {
            when: () => choices.length > 1,
            type: 'checkbox',
            name: 'frontOrBack',
            message: `Which ${chalk.yellow('Front Or Back End')} would you like to use for your application?`,
            choices: choicesData.filter(choice => choices.includes(choice.value)),
            default: choices,
        },
    ]);
    if (choices.length > 1) {
        this.jhipsterConfig.frontOrBack = answer.frontOrBack;
    }
    else {
        this.jhipsterConfig.frontOrBack = choices;
    }
    if (!this.jhipsterConfig.frontOrBack.includes('front')) {
        this.jhipsterConfig.skipClient = true;
        this.jhipsterConfig.clientFramework = 'no';
    }
    if (!this.jhipsterConfig.frontOrBack.includes('mobile')) {
        this.jhipsterConfig.skipMobile = true;
        this.jhipsterConfig.mobileTheme = 'no';
    }
    if (!this.jhipsterConfig.frontOrBack.includes('back')) {
        this.jhipsterConfig.skipServer = true;
    }
}
export async function askUseMultiTenant({ control }) {
    if (control.existingProject && this.options.askAnswered !== true)
        return;
    const config = this.jhipsterConfigWithDefaults;
    if (config.applicationType === 'gateway') {
        this.jhipsterConfig.useMultiTenant = false;
        return;
    }
    const answers = await this.prompt([
        {
            type: 'confirm',
            name: 'useMultiTenant',
            message: `Would you like to use the ${chalk.yellow('Use Multi-Tenant')} for your application?`,
            default: false,
        },
    ]);
    this.jhipsterConfig.useMultiTenant = answers.useMultiTenant;
}
export async function askBuiltInServices({ control }) {
    if (control.existingProject && this.options.askAnswered !== true)
        return;
    const config = this.jhipsterConfigWithDefaults;
    if (config.applicationType === 'monolith' || config.applicationType === 'gateway') {
        return;
    }
    const choices = [];
    const defaultChoice = [];
    defaultChoice.push('idm');
    choices.push({
        name: 'User Center',
        value: 'idm',
    });
    defaultChoice.push('oss', 'log');
    choices.push({
        name: 'Object Storage Service',
        value: 'oss',
    });
    choices.push({
        name: 'Log Service',
        value: 'log',
    });
    choices.push({
        name: 'Report',
        value: 'report',
    });
    const answers = await this.prompt([
        {
            type: 'checkbox',
            name: 'builtInServices',
            message: 'Which other services would you like to use?',
            choices,
            default: defaultChoice,
        },
    ]);
    this.jhipsterConfig.builtInServices = answers.builtInServices;
}
export async function askForMobileTheme({ control }) {
    if (control.existingProject && !this.options.askAnswered)
        return;
    const config = this.jhipsterConfigWithDefaults;
    if (config.skipMobile && !config.frontOrBack.includes('mobile'))
        return;
    const mobileThemes = [...(config.mobileThemes || [])].map(theme => theme.value);
    const mobileThemeChoices = [...(config.mobileThemes || [])];
    config.mobileThemes?.splice(0);
    const answer = await this.prompt([
        {
            type: 'checkbox',
            name: 'mobileThemes',
            when: () => !this.jhipsterConfig.skipMobile && mobileThemeChoices.length > 0,
            message: 'Would you like to use a Mobile App framework?',
            choices: () => mobileThemeChoices,
            default: mobileThemes,
        },
    ]);
    this.jhipsterConfig.mobileThemes = answer.mobileThemes;
}
