package com.bocloud.ims.service.upgrade;

import com.alibaba.druid.pool.DruidDataSource;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.RequestContext;
import com.bocloud.ims.service.upgrade.migration.DataMigrationTask;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * 系统升级
 * @author luyanchao
 * @date 2021-06-09
 */
@Service
public class UpgradeService {

    private static final Logger logger = LoggerFactory.getLogger(UpgradeService.class);

    private final ApplicationContext context;

    @Autowired
    public UpgradeService(ApplicationContext context) {
        this.context = context;
    }

    private List<UpgradeTask> getPreparedTasks(Collection<UpgradeTask> allTasks, Collection<UpgradeTask> completedTasks) {
        return allTasks.stream().filter(task -> !completedTasks.contains(task))
                .filter(task -> CollectionUtils.isEmpty(task.getDependOnTasks()) || completedTasks.containsAll(task.getDependOnTasks()))
                .collect(Collectors.toList());
    }

    public BocloudResult<Boolean> dataMigration(String host, Integer port, String user, String pass, String database,
                                                UpgradeVersion upgradeVersion, RequestContext requestContext) {
        logger.info("开始执行数据库迁移任务[{}], 源数据库: {}:{}/{}", upgradeVersion.name(), host, port,database);
        // 准备当前版本数据库的数据源和上一版本数据库的数据源
        DruidDataSource currDataSource = context.getBean(DruidDataSource.class);
        String[] currUrl = currDataSource.getUrl().split("\\?");
        String query = currUrl.length > 1 ? "?" + currUrl[1] : "";
        DruidDataSource oldDataSource = currDataSource.cloneDruidDataSource();
        oldDataSource.getConnectProperties().setProperty("config.decrypt", "false");
        oldDataSource.setUrl(String.format("jdbc:mysql://%s:%d/%s", host, port, database) + query);
        oldDataSource.setUsername(user);
        oldDataSource.setPassword(pass);
        // 准备相关bean
        final ThreadPoolTaskExecutor taskExecutor = context.getBean(ThreadPoolTaskExecutor.class);
        final ResourceEventPublisher eventPublisher = context.getBean(ResourceEventPublisher.class);
        final AmqpTemplate amqpTemplate = context.getBean(AmqpTemplate.class);
        // 查找所有的UpgradeTask
        Collection<UpgradeTask> upgradeTasks= context.getBeansOfType(UpgradeTask.class).values();
        upgradeTasks.removeIf(task -> !task.getUpgradeVersion().equals(upgradeVersion));
        logger.info("本次数据库迁移任务数量：{}个", upgradeTasks.size());
        upgradeTasks.stream().filter(task -> task instanceof DataMigrationTask)
                .forEach(task -> ((DataMigrationTask) task).setFromDataSource(oldDataSource));
        taskExecutor.execute(() -> {
            OperateResult result = new OperateResult(true, "数据迁移成功", "system.upgrade", null,
                    OperateResult.OperateCategory.ResourceEvent, requestContext);
            NotifyMessage message = new NotifyMessage(requestContext, "数据迁移情况通知", "数据迁移成功",
                    String.format("您好，从数据库%s:%d/%s迁移数据任务执行成功！", host, port, database), true, false);
            try {
                List<UpgradeTask> completedTasks = new ArrayList<>();
                List<UpgradeTask> preparedTasks = this.getPreparedTasks(upgradeTasks, completedTasks);
                int index = 1;
                while (CollectionUtils.isNotEmpty(preparedTasks)) {
                    logger.info("开始执行第{}批数据迁移任务：{}", index, preparedTasks.stream()
                            .map(Object::getClass).map(Class::getName).collect(Collectors.joining(", ")));
                    List<Future> futures = preparedTasks.stream().map(taskExecutor::submit).collect(Collectors.toList());
                    for (Future future : futures) {
                        future.get();
                    }
                    completedTasks.addAll(preparedTasks);
                    preparedTasks = this.getPreparedTasks(upgradeTasks, completedTasks);
                    index++;
                }
                // 发消息和站内信
                eventPublisher.send(result);
                amqpTemplate.convertAndSend("event.notice.queue", message);
                logger.error("从数据库{}:{}/{}迁移数据任务执行成功", host, port, database);
            } catch (Exception e) {
                logger.error("从数据库{}:{}/{}迁移数据任务执行失败:{}", host, port, database, e.getMessage(), e);
                result.setSuccess(false);
                result.setMessage(String.format("数据迁移入失败：%s", e.getMessage()));
                eventPublisher.send(result);
                message.setContent(String.format("您好，从数据库%s:%d/%s迁移数据任务执行失败，异常信息：%s。请重新创建当前版本标准库后再试！", host, port, database, e.getMessage()));
                amqpTemplate.convertAndSend("event.notice.queue", message);
            }
        });
        return new BocloudResult<>(true, true, "任务已经下发，正在执行...");
    }


}
