/*
 * Copyright [2020-2030] [https://www.stylefeng.cn]
 *
 * 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
 *
 * http://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.
 *
 * Guns采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Guns源码头部的版权声明。
 * 3.请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://gitee.com/stylefeng/guns
 * 5.在修改包名，模块名称，项目代码等时，请注明软件出处 https://gitee.com/stylefeng/guns
 * 6.若您的项目无法满足以上几点，可申请商业授权
 */
package cn.stylefeng.roses.kernel.db.flyway;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.stylefeng.roses.kernel.db.api.exception.DaoException;
import cn.stylefeng.roses.kernel.db.api.exception.enums.FlywayExceptionEnum;
import cn.stylefeng.roses.kernel.rule.listener.ContextInitializedListener;
import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.Flyway;
import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

/**
 * 初始化flyway配置
 * <p>
 * 当spring装配好配置后开始初始化
 *
 * @author liuhanqing
 * @since 2021/1/17 21:14
 */
@Slf4j
public class FlywayInitListener extends ContextInitializedListener implements Ordered {

    private static final String FLYWAY_LOCATIONS = "classpath:db/migration/mysql";

    @Override
    public void eventCallback(ApplicationContextInitializedEvent event) {

        ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();

        // 获取数据库连接配置
        String driverClassName = environment.getProperty("spring.datasource.driver-class-name");
        String dataSourceUrl = environment.getProperty("spring.datasource.url");
        String dataSourceUsername = environment.getProperty("spring.datasource.username");
        String dataSourcePassword = environment.getProperty("spring.datasource.password");

        // 判断是否开启 sharding jdbc
        Boolean enableSharding = environment.getProperty("spring.shardingsphere.enabled", Boolean.class);

        // 如果开启了sharding jdbc，则读取 sharding jdbc 主库配置
        if (ObjectUtil.isNotNull(enableSharding) && enableSharding) {
            driverClassName = environment.getProperty("spring.shardingsphere.datasource.master.driver-class-name");
            dataSourceUrl = environment.getProperty("spring.shardingsphere.datasource.master.url");
            dataSourceUsername = environment.getProperty("spring.shardingsphere.datasource.master.username");
            dataSourcePassword = environment.getProperty("spring.shardingsphere.datasource.master.password");
        }

        // flyway的配置
        String enabledStr = environment.getProperty("spring.flyway.enabled");
        String locations = environment.getProperty("spring.flyway.locations");
        String baselineOnMigrateStr = environment.getProperty("spring.flyway.baseline-on-migrate");
        String outOfOrderStr = environment.getProperty("spring.flyway.out-of-order");
        String placeholder = environment.getProperty("spring.flyway.placeholder-replacement");

        // 是否开启flyway，默认false.
        boolean enabled = false;
        if (StrUtil.isNotBlank(enabledStr)) {
            enabled = Boolean.parseBoolean(enabledStr);
        }

        // 如果未开启flyway 直接return
        if (!enabled) {
            return;
        }

        // 如果有为空的配置，终止执行
        if (ObjectUtil.hasEmpty(dataSourceUrl, dataSourceUsername, dataSourcePassword, driverClassName)) {
            throw new DaoException(FlywayExceptionEnum.DB_CONFIG_ERROR);
        }

        // 当迁移时发现目标schema非空，而且带有没有元数据的表时，是否自动执行基准迁移，默认false.
        boolean baselineOnMigrate = false;
        if (StrUtil.isNotBlank(baselineOnMigrateStr)) {
            baselineOnMigrate = Boolean.parseBoolean(baselineOnMigrateStr);
        }

        // 如果未设置flyway路径，则设置为默认flyway路径
        if (StrUtil.isBlank(locations)) {
            locations = FLYWAY_LOCATIONS;
        }

        // 是否允许无序的迁移 开发环境最好开启, 生产环境关闭
        boolean outOfOrder = false;
        if (StrUtil.isNotBlank(outOfOrderStr)) {
            outOfOrder = Boolean.parseBoolean(outOfOrderStr);
        }

        // 是否开启占位符
        boolean enablePlaceholder = true;
        if (StrUtil.isNotBlank(placeholder)) {
            enablePlaceholder = Boolean.parseBoolean(placeholder);
        }

        DriverManagerDataSource dmDataSource = null;
        try {
            assert dataSourceUrl != null;
            // 手动创建数据源
            dmDataSource = new DriverManagerDataSource();
            dmDataSource.setDriverClassName(driverClassName);
            dmDataSource.setUrl(dataSourceUrl);
            dmDataSource.setUsername(dataSourceUsername);
            dmDataSource.setPassword(dataSourcePassword);

            // flyway配置
            Flyway flyway = Flyway.configure()
                    .dataSource(dmDataSource)

                    // 迁移脚本的位置
                    .locations(locations)

                    // 当迁移时发现目标schema非空，而且带有没有元数据的表时，是否自动执行基准迁移
                    .baselineOnMigrate(baselineOnMigrate)

                    // 是否允许无序的迁移 开发环境最好开启 , 生产环境关闭
                    .outOfOrder(outOfOrder)

                    // 是否开启占位符
                    .placeholderReplacement(enablePlaceholder)

                    // 忽略丢失的脚本
                    .ignoreMissingMigrations(true)

                    .load();

            // 执行迁移
            flyway.migrate();

        } catch (Exception e) {
            log.error("flyway初始化失败", e);
            throw new DaoException(FlywayExceptionEnum.FLYWAY_MIGRATE_ERROR, e.getMessage());
        }

    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

}
