package com.persagy.dmp.mybatis.handler;

import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.persagy.dmp.common.context.AppContext;
import com.persagy.dmp.mybatis.service.IDbService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

/**
 * 动态数据源拦截器
 * @author Charlie Yu
 * @date 2021-03-29
 */
public class DynamicDataSourceHandler extends HandlerInterceptorAdapter {

    /** 忽略的url - swagger的文档不校验 */
    private static final String[] IGNORE_URL = {".html", ".js", ".css", "/swagger-resources"};

    @Autowired
    private DynamicRoutingDataSource dataSource;
    @Autowired
    private DefaultDataSourceCreator dataSourceCreator;
    @Autowired
    private DynamicDataSourceProperties defaultProperty;
    @Autowired
    private IDbService dbService;
    @Value("${persagy.common.dynamic.enabled:false}")
    private boolean dynamicEnabled;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestUri = request.getRequestURI();
        if (StrUtil.containsAny(requestUri, IGNORE_URL)) {
            return true;
        }
        resetDataSource();
        return true;
    }

    /**
     * 多租户处理，数据源切换
     */
    public void resetDataSource() {
        // 是否启用多数据源
        if(!dynamicEnabled) {
            return;
        }
        // 读取初始实例名
        String defaultSchema = getPrimarySchema();
        // 待切换的数据源名称： 初始实例名_集团编码
        String newSchema = getNewSchema(defaultSchema);
        // 数据源中是否已存在
        if(!dataSource.getCurrentDataSources().keySet().contains(newSchema)) {
            // 设置新数据源
            DataSourceProperty property = copyProperty(defaultSchema, newSchema);
            // 创建数据库实例
            dbService.createDataBase(newSchema);
            // 创建数据源
            DataSource currDs = dataSourceCreator.createDataSource(property);
            dataSource.addDataSource(newSchema, currDs);
        }
        // 设置当前数据源
        DynamicDataSourceContextHolder.push(newSchema);
    }

    /**
     * 获取新实例名
     * @param defaultSchema
     * @return 初始实例名_集团编码
     */
    private String getNewSchema(String defaultSchema) {
        String groupCode = AppContext.getContext().getGroupCode();
        // 当默认实例名或集团编码为空时，切换到默认数据源
        if(StrUtil.isBlank(defaultSchema) || StrUtil.isBlank(groupCode)) {
            return defaultProperty.getPrimary();
        }
        // 拼接groupCode
        String newSchema = defaultSchema + "_" + groupCode;
        // 转小写
        return newSchema.toLowerCase();
    }

    /**
     * 获取默认实例名
     * @return
     */
    private String getPrimarySchema() {
        // 取默认数据源
        DataSourceProperty srcProperty = defaultProperty.getDatasource().get(defaultProperty.getPrimary());
        String url = srcProperty.getUrl();
        // 截取最后一个/到?之间的内容即为schema
        int startIndex = StrUtil.lastIndexOfIgnoreCase(url, "/");
        int endIndex = StrUtil.indexOfIgnoreCase(url, "?");
        if(startIndex <= 0 || endIndex <= startIndex) {
            return null;
        }
        // 没有?时截取到最后
        return StrUtil.sub(url, startIndex + 1, endIndex);
    }

    /**
     * 复制新的数据源
     * @param defaultSchema 默认连接的实例名
     * @param newSchema 新创建的实例名
     * @return
     */
    private DataSourceProperty copyProperty(String defaultSchema, String newSchema) {
        // 取默认数据源
        DataSourceProperty srcProperty = defaultProperty.getDatasource().get(defaultProperty.getPrimary());
        // 设置新数据源
        DataSourceProperty property = new DataSourceProperty();
        BeanUtils.copyProperties(srcProperty, property);
        property.setPoolName(newSchema);
        property.setUrl(StrUtil.replace(property.getUrl(), defaultSchema, newSchema));
        return property;
    }
}
