package com.qiaoba.common.database.monitor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
import com.qiaoba.common.database.context.BackupDatasourceContext;
import com.qiaoba.common.database.entity.DynamicDataSource;
import com.qiaoba.common.database.util.JdbcUtil;
import com.qiaoba.common.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 不在线的数据源监控
 * 尝试连接,如果连接成功,加入到备用数据源
 *
 * @author ailanyin
 * @version 1.0
 * @since 2023/6/26 10:46
 */
@Component
@Slf4j
public class NotOnlineDatasourceMonitor {

    private static final String LOCK_KEY = "lock4j:notOnlineDatasourceMonitor";

    public static final String ERROR_DATASOURCE_KEY = "error_datasource";
    @Resource
    private LockTemplate lockTemplate;
    @Resource
    private RedisService redisService;
    @Resource
    private BackupDatasourceContext backupDatasourceContext;

    @PostConstruct
    public void init() {
        // 10s 运行一次
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                // 项目启动时加载数据源还未完成
                if (!DynamicDataSourceConfig.COMPLETE_LOAD_DATASOURCE) {
                    return;
                }
                // expire = -1 锁自动续期, 防止数据源过多或异常等待, 超过默认锁 30s
                final LockInfo lockInfo = lockTemplate.lock(LOCK_KEY, -1, 1000);
                //申请锁失败 说明集群中其他设备正在执行监控
                if (null == lockInfo) {
                    return;
                }
                //申请锁成功
                try {
                    log.trace("开始-[错误数据源重试]-线程, 时间: {}", new Date());
                    tryConnect();
                    log.trace("结束-[错误数据源重试]-线程, 时间: {}", new Date());
                } finally {
                    // 释放锁
                    lockTemplate.releaseLock(lockInfo);
                }

            }
        }, 0, 10 * 1000);
    }

    private void tryConnect() {
        Map<String, List<DynamicDataSource>> map = redisService.getHashList(ERROR_DATASOURCE_KEY, DynamicDataSource.class);
        if (Objects.nonNull(map)) {
            for (String tenantId : map.keySet()) {
                List<DynamicDataSource> errorDatasourceList = map.get(tenantId);
                List<DynamicDataSource> delList = new ArrayList<>();
                for (DynamicDataSource errorDatasource : errorDatasourceList) {
                    // 说明连接成功
                    boolean check = JdbcUtil.checkConnect(errorDatasource.getDriver(), errorDatasource.getUrl(), errorDatasource.getUsername(), errorDatasource.getPassword());
                    if (check) {
                        log.info("数据源重连成功, Url: {}", errorDatasource.getUrl());
                        delList.add(errorDatasource);
                        // 加入到备用Map中
                        backupDatasourceContext.addBackupMap(errorDatasource);
                        log.info("租户断线数据源重连成功, 已重新加入备用数据源,租户ID: {}", tenantId);
                    }
                }
                errorDatasourceList.removeAll(delList);
                removeErrorDatasource(tenantId, errorDatasourceList);
            }
        }
    }

    private void removeErrorDatasource(String tenantId, List<DynamicDataSource> errorDatasourceList) {
        if (CollUtil.isNotEmpty(errorDatasourceList)) {
            redisService.hSet(ERROR_DATASOURCE_KEY, tenantId, errorDatasourceList);
        } else {
            redisService.hDel(ERROR_DATASOURCE_KEY, tenantId);
        }
    }
}
