package ink.wgink.common.datasource;

import com.alibaba.druid.filter.config.ConfigFilter;
import com.alibaba.druid.filter.encoding.EncodingConvertFilter;
import com.alibaba.druid.filter.logging.CommonsLogFilter;
import com.alibaba.druid.filter.logging.Log4j2Filter;
import com.alibaba.druid.filter.logging.Log4jFilter;
import com.alibaba.druid.filter.logging.Slf4jLogFilter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: StandbyMySqlDataSource
 * @Description: 主备mysql数据库源
 * @Author: wanggeng
 * @Date: 2021/11/28 9:32 下午
 * @Version: 1.0
 */
@Primary
@Component
@ConfigurationProperties("spring.datasource.druid")
public class StandbyDataSource extends DruidDataSource implements InitializingBean {
    private static final Logger LOG = LoggerFactory.getLogger(StandbyDataSource.class);
    private ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(2);

    @Autowired
    private DataSourceProperties basicProperties;

    private boolean lastInited;
    /**
     * 备库url连接
     */
    private String standbyUrl;

    @Override
    public void init() throws SQLException {
        lastInited = inited;
        super.init();
        // 第一次初始化
        if (!lastInited && inited) {
            // 如果没有配置，不启用主备切换
            if (standbyUrl == null) {
                return;
            }
            scheduledExecutorService.schedule(new ValidateUrlTask(), 1, TimeUnit.SECONDS);
        }
    }

    /**
     * 验证连接
     */
    private class ValidateUrlTask implements Runnable {
        @Override
        public void run() {
            // 备库下标
            while (true) {
                // 如果这个数据源被关闭了,就结束这个定时任务
                if (isClosed()) {
                    LOG.debug("Jdbc connection closed");
                    break;
                }
                //如果这个数据源已经被初始化了,同时连接异常才进行处理
                if (isInited() && !isConnectionActive()) {
                    // 主库有问题时，切换备库
                    LOG.debug("Change standby urls");
                    changeUrl(standbyUrl);
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    LOG.error(e.getMessage(), e);
                }
            }
        }
    }

    /**
     * 切换连接
     *
     * @param url
     */
    private void changeUrl(String url) {
        LOG.debug("jdbc url: {}", url);
        inited = false;
        setUrl(url);
    }

    public void setStandbyUrl(String standbyUrl) {
        this.standbyUrl = standbyUrl;
    }

    /**
     * 判断连接是否存活
     *
     * @return
     */
    public boolean isConnectionActive() {
        try (Connection conn = getConnection();
             Statement stmt = conn.createStatement();
        ) {
            stmt.execute("select 1");
            return true;
        } catch (SQLException e) {
            LOG.error(e.getMessage());
            return false;
        }
    }

    public void afterPropertiesSet() throws Exception {
        if (super.getUsername() == null) {
            super.setUsername(this.basicProperties.determineUsername());
        }

        if (super.getPassword() == null) {
            super.setPassword(this.basicProperties.determinePassword());
        }

        if (super.getUrl() == null) {
            super.setUrl(this.basicProperties.determineUrl());
        }

        if (super.getDriverClassName() == null) {
            super.setDriverClassName(this.basicProperties.getDriverClassName());
        }

    }

    @Autowired(required = false)
    public void addStatFilter(StatFilter statFilter) {
        super.filters.add(statFilter);
    }

    @Autowired(required = false)
    public void addConfigFilter(ConfigFilter configFilter) {
        super.filters.add(configFilter);
    }

    @Autowired(required = false)
    public void addEncodingConvertFilter(EncodingConvertFilter encodingConvertFilter) {
        super.filters.add(encodingConvertFilter);
    }

    @Autowired(required = false)
    public void addSlf4jLogFilter(Slf4jLogFilter slf4jLogFilter) {
        super.filters.add(slf4jLogFilter);
    }

    @Autowired(required = false)
    public void addLog4jFilter(Log4jFilter log4jFilter) {
        super.filters.add(log4jFilter);
    }

    @Autowired(required = false)
    public void addLog4j2Filter(Log4j2Filter log4j2Filter) {
        super.filters.add(log4j2Filter);
    }

    @Autowired(required = false)
    public void addCommonsLogFilter(CommonsLogFilter commonsLogFilter) {
        super.filters.add(commonsLogFilter);
    }

    @Autowired(required = false)
    public void addWallFilter(WallFilter wallFilter) {
        super.filters.add(wallFilter);
    }

}
