package com.qiaoba.module.tenant.filters;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import com.qiaoba.common.base.code.TenantErrorCode;
import com.qiaoba.common.base.constants.TenantConstant;
import com.qiaoba.common.base.context.BaseContext;
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
import com.qiaoba.common.web.utils.ResponseUtil;
import com.qiaoba.common.web.utils.UriUtil;
import com.qiaoba.module.tenant.entity.SysTenant;
import com.qiaoba.module.tenant.enums.TenantStatusEnum;
import com.qiaoba.module.tenant.service.SysTenantService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 动态切换不同租户的数据源
 *
 * @author ailanyin
 * @version 1.0
 * @since 2023-04-25 22:48:43
 */
@Component
@Order(-10000)
@Slf4j
public class DynamicDataSourceFilter extends OncePerRequestFilter {

    @Resource
    private SysTenantService sysTenantService;
    @Resource
    private DynamicDataSourceConfig dynamicDataSourceConfig;

    private static final List<String> NOT_NEED_TENANT_URIS = ListUtil.toList("/**/*.html",
            "/**/*.css", "/**/*.js", "/swagger-resources", "/v3/api-docs/**", "/favicon.ico");

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.debug("Start run DynamicDataSourceFilter, Uri: {}", request.getRequestURI());
        String tenantId = request.getHeader(TenantConstant.HEADER_KEY_TENANT);
        // 主系统 or 登录入口获取租户列表
        if (TenantConstant.DEFAULT_TENANT_ID.equals(tenantId) || TenantConstant.LOGIN_TENANT_LIST_URI.equals(request.getRequestURI())) {
            dynamicDataSourceConfig.setDefaultSetting();
            filterChain.doFilter(request, response);
            after();
            return;
        }

        // 不需要租户ID的接口
        for (String uri : NOT_NEED_TENANT_URIS) {
            if (UriUtil.match(uri, request.getRequestURI())) {
                filterChain.doFilter(request, response);
                return;
            }
        }

        SysTenant sysTenant = sysTenantService.selectFromCache(tenantId);
        // 检查租户是否允许访问
        if (checkTenantIsNotAllow(response, sysTenant)) {
            return;
        }
        // 设置租户信息
        before(sysTenant);
        filterChain.doFilter(request, response);
        after();
    }

    private void before(SysTenant sysTenant) {
        log.debug("设置租户信息, 租户ID: {},租户模式: {}", sysTenant.getTenantId(), sysTenant.getMode());
        BaseContext.setTenantId(sysTenant.getTenantId());
        BaseContext.setSchema(sysTenant.getMode().equals(SysTenant.SCHEMA_MODE));
        // 数据源模式-设置第三方数据源
        if (sysTenant.getMode().equals(SysTenant.DATASOURCE_MODE)) {
            //设置当前租户对应的数据源
            BaseContext.setDataSource(DynamicDatasourceConstant.MASTER_PREFIX + sysTenant.getTenantId());
        }
        // 字段模式 or Schema模式-数据源选择默认数据源
        else {
            BaseContext.setDataSource(DynamicDatasourceConstant.MASTER_PREFIX + TenantConstant.DEFAULT_TENANT_ID);
        }


    }

    private void after() {
        log.debug("clear the BaseContext");
        BaseContext.clearAllHolder();
    }

    /**
     * 检查租户是否不允许访问
     *
     * @param sysTenant sysTenant
     * @return 是->不允许
     */
    private boolean checkTenantIsNotAllow(HttpServletResponse response, SysTenant sysTenant) throws IOException {
        if (Objects.isNull(sysTenant)) {
            // 未找到租户信息
            log.debug("未找到租户信息");
            ResponseUtil.errorAuth(response, TenantErrorCode.NOT_FIND.getCode(), TenantErrorCode.NOT_FIND.getMsg());
            return true;
        }

        if (TenantStatusEnum.DISABLE.getStatus().equals(sysTenant.getStatus())) {
            // 封禁状态
            log.debug("租户已封禁, 租户ID: {}", sysTenant.getTenantId());
            ResponseUtil.errorAuth(response, TenantErrorCode.DISABLE.getCode(), TenantErrorCode.DISABLE.getMsg());
            return true;
        }

        if (TenantStatusEnum.EXPIRE.getStatus().equals(sysTenant.getStatus())) {
            // 已过期
            log.debug("租户已过期, 租户ID: {}", sysTenant.getTenantId());
            ResponseUtil.errorAuth(response, TenantErrorCode.EXPIRE.getCode(), TenantErrorCode.EXPIRE.getMsg());
            return true;
        }
        if (DateUtil.compare(sysTenant.getExpireTime(), new Date()) < 0) {
            // 已过期
            log.debug("租户已过期, 租户ID: {}", sysTenant.getTenantId());
            ResponseUtil.errorAuth(response, TenantErrorCode.EXPIRE.getCode(), TenantErrorCode.EXPIRE.getMsg());
            // 更新租户状态为已过期
            dynamicDataSourceConfig.setDefaultSetting();
            sysTenantService.updateStatus(sysTenant.getTenantId(), TenantStatusEnum.EXPIRE.getStatus());
            after();
            return true;
        }
        return false;
    }
}
