package cn.axzo.workflow.server.initializer;

import cn.azxo.framework.common.utils.LogUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static org.flowable.common.engine.impl.AbstractEngineConfiguration.DB_SCHEMA_UPDATE_FALSE;

/**
 * 版本升级脚本
 *
 * @author wangli
 * @since 2024/1/22 16:02
 */
@Component
@Slf4j
@AllArgsConstructor
public class VersionUpgradeInitializer implements ApplicationRunner {
    private static final String FILE_PREFIX = "upgrade_to_";
    private static final String COLUMN_NAME = "workflow.engine.version";
    private final JdbcTemplate jdbcTemplate;
    private final ResourceLoader resourceLoader;
    private final ApplicationContext applicationContext;
    private ResourcePatternResolver resourcePatternResolver;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        SpringProcessEngineConfiguration springProcessEngineConfiguration =
                (SpringProcessEngineConfiguration) applicationContext.getBean("springProcessEngineConfiguration");
        if (Objects.equals(DB_SCHEMA_UPDATE_FALSE, springProcessEngineConfiguration.getDatabaseSchemaUpdate())) {
            return;
        }

        DefaultArtifactVersion dbVersion = new DefaultArtifactVersion(selectWorkflowEngineVersion());
        AtomicBoolean result = new AtomicBoolean(true);
        List<DefaultArtifactVersion> newVersions = new ArrayList<>();
        localSqlFiles().stream().map(str -> str.replace(FILE_PREFIX, "").replace(".sql", ""))
                .map(DefaultArtifactVersion::new)
                .sorted()
                .forEach(upgradeVersion -> {
                    log.info("数据库升级版本比较: upgradeVersion: {}, inDbVersion:{} ", upgradeVersion, dbVersion);
                    if (upgradeVersion.compareTo(dbVersion) > 0) {
                        newVersions.add(upgradeVersion);
                        String fileName = FILE_PREFIX + upgradeVersion + ".sql";
                        try {
                            executeSqlScript(fileName);
                            log.info("execute sql script: {}", fileName);
                        } catch (Exception e) {
                            LogUtil.error(LogUtil.ErrorType.ERROR_SQL, "升级服务数据库版本发生异常,文件: {}, 异常信息: {}", fileName, e.getMessage());
                            result.compareAndSet(true, false);
                        }
                    }
                });

        updateVersion(result, newVersions);
    }

    private void updateVersion(AtomicBoolean result, List<DefaultArtifactVersion> newVersions) {
        if (result.get() && !newVersions.isEmpty()) {
            String updateSql = "UPDATE act_ge_property SET VALUE_ = ? where NAME_ = '" + COLUMN_NAME + "'";
            jdbcTemplate.update(updateSql, findMaxVersion(newVersions));
        }
    }

    private String findMaxVersion(List<DefaultArtifactVersion> newVersions) {
        // 使用 forEach 遍历列表
        DefaultArtifactVersion max = newVersions.get(0);
        for (DefaultArtifactVersion i : newVersions) {
            // 如果当前元素大于 max，则更新 max
            if (i.compareTo(max) > 0) {
                max = i;
            }
        }

        log.info("max version: {}", max.toString());
        return max.toString();
    }

    private void executeSqlScript(String fileName) {
        Resource resource = resourceLoader.getResource("classpath:sql/" + fileName.toLowerCase());

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
            StringBuilder scriptBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                if (!line.startsWith("-- ")) {
                    scriptBuilder.append(line).append(" ");
                }
            }

            // Split SQL statements and execute each one
            String[] sqlStatements = scriptBuilder.toString().split(";");
            for (String sql : sqlStatements) {
                if (StringUtils.hasText(sql)) {
                    log.info("sql: {}", sql);
                    jdbcTemplate.execute(sql);
                }
            }
        } catch (IOException e) {
            // Handle IOException
            e.printStackTrace();
        }
    }

    private List<String> localSqlFiles() throws IOException {
        return Arrays.stream(resourcePatternResolver.getResources("classpath:sql/" + FILE_PREFIX + "*.sql"))
                .map(Resource::getFilename).collect(Collectors.toList());
    }

    private String selectWorkflowEngineVersion() {
        String query = "select VALUE_ from act_ge_property where NAME_ = ?";
        String search = "workflow.engine.version";
        String version;
        try {
            version = jdbcTemplate.queryForObject(query, new Object[]{search}, String.class);
        } catch (Exception e) {
            jdbcTemplate.execute("insert into act_ge_property values ('workflow.engine.version', '1.2.1', 1)");
            version = "1.2.1";
        }
        return version;
    }
}
