import chalk from 'chalk';
import { includes, intersection } from 'lodash-es';
import { applicationOptions, applicationTypes, authenticationTypes, databaseTypes, cacheTypes, testFrameworkTypes, } from '../../jdl/jhipster/index.js';
import { MESSAGE_BROKER } from '../server/options/index.js';
import { R2DBC_DB_OPTIONS, SQL_DB_OPTIONS } from '../server/support/database.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 { CACHE_PROVIDER, DATABASE_TYPE, DEV_DATABASE_TYPE, PROD_DATABASE_TYPE, SERVICE_DISCOVERY_TYPE, WEBSOCKET, SEARCH_ENGINE, ENABLE_SWAGGER_CODEGEN, } = OptionNames;
const NO_DATABASE = databaseTypes.NO;
const NO_CACHE_PROVIDER = cacheTypes.NO;
const { GATLING, CUCUMBER } = testFrameworkTypes;
const getOptionFromArray = (array, option) => {
    let optionValue = false;
    array.forEach(value => {
        if (includes(value, option)) {
            optionValue = value.split(':')[1];
        }
    });
    optionValue = optionValue === 'true' ? true : optionValue;
    return optionValue;
};
export async function askForServerSideOpts({ control }) {
    if (control.existingProject && !this.options.askAnswered)
        return;
    const { applicationType } = this.jhipsterConfigWithDefaults;
    const prompts = [
        {
            type: 'list',
            name: DATABASE_TYPE,
            message: `Which ${chalk.yellow('*type*')} of database would you like to use?`,
            choices: answers => {
                const opts = [];
                if (!answers.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 (answers.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: PROD_DATABASE_TYPE,
            message: `Which ${chalk.yellow('*production*')} database would you like to use?`,
            choices: answers => (answers.reactive ? R2DBC_DB_OPTIONS : SQL_DB_OPTIONS),
            default: this.jhipsterConfigWithDefaults.prodDatabaseType,
        },
        {
            when: response => response.databaseType === SQL,
            type: 'list',
            name: DEV_DATABASE_TYPE,
            message: `Which ${chalk.yellow('*development*')} database would you like to use?`,
            choices: response => [SQL_DB_OPTIONS.find(it => it.value === response.prodDatabaseType)].concat([
                {
                    value: H2_DISK,
                    name: 'H2 with disk-based persistence',
                },
                {
                    value: H2_MEMORY,
                    name: 'H2 with in-memory persistence',
                },
            ]),
            default: this.jhipsterConfigWithDefaults.devDatabaseType,
        },
        {
            when: answers => !answers.reactive,
            type: 'list',
            name: CACHE_PROVIDER,
            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, supports rate-limiting for gateway applications)',
                },
                {
                    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 &&
                !answers.reactive,
            type: 'confirm',
            name: 'enableHibernateCache',
            message: 'Do you want to use Hibernate 2nd level cache?',
            default: this.jhipsterConfigWithDefaults.enableHibernateCache,
        },
    ];
    await this.prompt(prompts, this.config);
}
export async function askForOptionalItems({ control }) {
    if (control.existingProject && !this.options.askAnswered)
        return;
    const { applicationType, reactive, databaseType, serverSideOptions = [] } = this.jhipsterConfigWithDefaults;
    const choices = [];
    const defaultChoice = [...serverSideOptions];
    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',
    });
    const PROMPTS = {
        type: 'checkbox',
        name: 'serverSideOptions',
        message: 'Which other technologies would you like to use?',
        choices,
        default: defaultChoice,
    };
    if (choices.length > 0) {
        await this.prompt(PROMPTS).then(answers => {
            this.jhipsterConfig.serverSideOptions = answers.serverSideOptions;
            this.jhipsterConfig.websocket = getOptionFromArray(answers.serverSideOptions, WEBSOCKET);
            this.jhipsterConfig.searchEngine = getOptionFromArray(answers.serverSideOptions, SEARCH_ENGINE);
            this.jhipsterConfig.messageBroker = getOptionFromArray(answers.serverSideOptions, MESSAGE_BROKER);
            this.jhipsterConfig.enableSwaggerCodegen = getOptionFromArray(answers.serverSideOptions, ENABLE_SWAGGER_CODEGEN);
            if (!this.jhipsterConfig.serviceDiscoveryType) {
                this.jhipsterConfig.serviceDiscoveryType = getOptionFromArray(answers.serverSideOptions, SERVICE_DISCOVERY_TYPE);
            }
        });
    }
}
export async function askForServerTestOpts({ control }) {
    if (control.existingProject && this.options.askAnswered !== true)
        return;
    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 },
                { name: 'Cucumber', value: 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 prompts = [
        {
            type: 'list',
            name: 'ormTool',
            message: `Which ${chalk.yellow('OrmTools')} would you like to use for your application?`,
            choices,
            default: 'mybatis',
        },
    ];
    const answers = await this.prompt(prompts);
    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 prompts = [
        {
            type: 'list',
            name: 'userIdType',
            message: `Which ${chalk.yellow('*type*')} of user id type would you like to use?`,
            choices,
            default: 'long',
        },
    ];
    const answers = await this.prompt(prompts);
    this.jhipsterConfig.userIdType = answers.userIdType;
}
export async function askJavaVersion({ control }) {
    if (control.existingProject && !this.options.askAnswered) {
        return;
    }
    const choices = [
        {
            value: '17',
            name: 'Java 17',
        },
        {
            value: '8',
            name: 'Java 1.8',
        },
    ];
    const prompts = [
        {
            type: 'list',
            name: 'javaVersion',
            message: `Which ${chalk.yellow('JavaVersion')} would you like to use for your application?`,
            choices,
            default: '17',
        },
    ];
    const answers = await this.prompt(prompts);
    this.jhipsterConfig.javaVersion = answers.javaVersion;
}
export async function askJobScheduler() {
    this.jobScheduler = this.jhipsterConfig.jobScheduler = 'quartz';
}
export async function askUseLombok({ control }) {
    if (control.existingProject && !this.options.askAnswered) {
        return;
    }
    const prompts = [
        {
            type: 'confirm',
            name: 'useLombok',
            message: `Would you like to use the ${chalk.yellow('Lombok')} for your application?`,
            default: true,
        },
    ];
    const answers = await this.prompt(prompts);
    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 prompts = [
        {
            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),
        },
    ];
    const answers = await this.prompt(prompts);
    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.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.frontOrBack = this.jhipsterConfig.frontOrBack = answer.frontOrBack;
    }
    else {
        this.frontOrBack = this.jhipsterConfig.frontOrBack = choices;
    }
    if (!this.frontOrBack.includes('front')) {
        this.skipClient = this.jhipsterConfig.skipClient = true;
        this.clientFramework = this.jhipsterConfig.clientFramework = 'no';
    }
    if (!this.frontOrBack.includes('mobile')) {
        this.skipMobile = this.jhipsterConfig.skipMobile = true;
        this.mobileTheme = this.jhipsterConfig.mobileTheme = 'no';
    }
    if (!this.frontOrBack.includes('back')) {
        this.skipServer = 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.useMultiTenant = 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.useMultiTenant = 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.builtInServices = 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 = [];
    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.mobileThemes = this.jhipsterConfig.mobileThemes = answer.mobileThemes;
}
