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 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 {

    /**
     * 错误数据源(连接中断)
     * <p>
     * key: 租户ID
     * value: 错误数据源 list
     */
    public static Map<String, List<DynamicDataSource>> ERROR_DATASOURCE_MAP = new ConcurrentHashMap<>();

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

    @Resource
    private LockTemplate lockTemplate;


    @PostConstruct
    public void init() {
        // 10s 运行一次
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                // 项目启动时加载数据源还未完成
                if (!DynamicDataSourceConfig.COMPLETE_LOAD_DATASOURCE) {
                    return;
                }
                // 没有暂时失联的数据源, 直接结束
                if (CollUtil.isEmpty(ERROR_DATASOURCE_MAP)) {
                    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() {
        Set<String> tenantIds = ERROR_DATASOURCE_MAP.keySet();
        for (String tenantId : tenantIds) {
            List<DynamicDataSource> errorDatasourceList = ERROR_DATASOURCE_MAP.get(tenantId);
            for (int i = 0; i < errorDatasourceList.size(); i++) {
                DynamicDataSource errorDatasource = errorDatasourceList.get(i);
                // 说明连接成功
                boolean check = JdbcUtil.checkConnect(errorDatasource.getDriver(), errorDatasource.getUrl(), errorDatasource.getUsername(), errorDatasource.getPassword());
                if (check) {
                    log.info("数据源重连成功, Url: {}", errorDatasource.getUrl());
                    // 从errorMap中删除
                    errorDatasourceList.remove(errorDatasource);
                    if (CollUtil.isEmpty(errorDatasourceList)) {
                        ERROR_DATASOURCE_MAP.remove(tenantId);
                    }
                    // 加入到备用Map中
                    BackupDatasourceContext.addBackupMap(tenantId, errorDatasource);
                }
            }
        }
    }


    public static void addErrorDatasource(String tenantId, DynamicDataSource dataSource) {
        List<DynamicDataSource> errorDataSourceList = NotOnlineDatasourceMonitor.ERROR_DATASOURCE_MAP.get(tenantId);
        if (CollUtil.isEmpty(errorDataSourceList)) {
            NotOnlineDatasourceMonitor.ERROR_DATASOURCE_MAP.put(tenantId, ListUtil.toList(dataSource));
        } else {
            errorDataSourceList.add(dataSource);
        }
    }
}
