package com.qiaoba.module.tenant.filter;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import com.qiaoba.api.tenant.entity.SysTenant;
import com.qiaoba.api.tenant.enums.TenantStatusEnum;
import com.qiaoba.api.tenant.utils.TenantUtil;
import com.qiaoba.common.base.code.TenantErrorCode;
import com.qiaoba.common.base.constant.BaseConstant;
import com.qiaoba.common.base.constant.TenantConstant;
import com.qiaoba.common.base.context.BaseContext;
import com.qiaoba.common.base.order.FilterOrder;
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
import com.qiaoba.common.web.util.ResponseUtil;
import com.qiaoba.common.web.util.UriUtil;
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(FilterOrder.DYNAMIC_DATASOURCE_FILTER_ORDER)
@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",
            // Knife4j
            "/swagger-resources", "/v3/api-docs/**", "/favicon.ico",
            // Druid 可视化web监控
            "/druid/**",
            // File
            BaseConstant.RESOURCE_PATTERN);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            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);
                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);
        } finally {
            after();
        }
    }

    private void before(SysTenant sysTenant) {
        log.debug("设置租户信息, 租户ID: {},租户模式: {}", sysTenant.getTenantId(), sysTenant.getMode());
        TenantUtil.setContext(sysTenant);
    }

    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.isDisable(sysTenant.getStatus())) {
            // 封禁状态
            log.debug("租户已封禁, 租户ID: {}", sysTenant.getTenantId());
            ResponseUtil.errorAuth(response, TenantErrorCode.DISABLE.getCode(), TenantErrorCode.DISABLE.getMsg());
            return true;
        }

        if (TenantStatusEnum.isExpire(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());
            return true;
        }
        return false;
    }
}
