package com.qmth.boot.api.valve;

import com.qmth.boot.api.constant.ApiConstant;
import com.qmth.boot.api.exception.DefaultExceptionEnum;
import com.qmth.boot.api.utils.RequestUtil;
import com.qmth.boot.tools.matcher.IPMatcher;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collection;

/**
 * API层通用IP过滤服务
 */
@Component
public class IPFilterService implements ApiConstant {

    protected static final Logger log = LoggerFactory.getLogger(IPFilterService.class);

    /**
     * ipAllow和ipDeny都可以为空，ipDeny优先级高于ipAllow，过滤失败抛出异常
     *
     * @param request
     * @param ipAllow
     * @param ipDeny
     */
    public void filter(HttpServletRequest request, String[] ipAllow, String[] ipDeny) {
        Collection<String> ipAllowList = ArrayUtils.isNotEmpty(ipAllow) ? Arrays.asList(ipAllow) : null;
        Collection<String> ipDenyList = ArrayUtils.isNotEmpty(ipDeny) ? Arrays.asList(ipDeny) : null;
        this.filter(request, ipAllowList, ipDenyList);
    }

    /**
     * ipAllow和ipDeny都可以为空，ipDeny优先级高于ipAllow，过滤失败抛出异常
     *
     * @param request
     * @param ipAllow
     * @param ipDeny
     */
    public void filter(HttpServletRequest request, Collection<String> ipAllow, Collection<String> ipDeny) {
        String ip = RequestUtil.getIpAddress(request);
        // 本机IP直接通过
        if (IP_LOCAL_MACHINE.equals(ip) || IP_LOCALHOST.equals(ip)) {
            return;
        }
        // 优先匹配黑名单，任意匹配则拒绝
        if (ipDeny != null) {
            for (String pattern : ipDeny) {
                if (IPMatcher.match(pattern, ip)) {
                    log.warn("IP rejected: path={}, ip={}, rule={}", request.getServletPath(), ip, pattern);
                    throw DefaultExceptionEnum.IP_FORBIDDEN.exception("ip is denied");
                }
            }
        }
        // 其次匹配白名单，全部不匹配则拒绝
        if (!CollectionUtils.isEmpty(ipAllow)) {
            boolean allowed = false;
            for (String pattern : ipAllow) {
                if (IPMatcher.match(pattern, ip)) {
                    allowed = true;
                    break;
                }
            }
            if (!allowed) {
                log.warn("IP not in allow-list: path={}, ip={}, rules={}", request.getServletPath(), ip,
                        StringUtils.join(ipAllow, ","));
                throw DefaultExceptionEnum.IP_FORBIDDEN.exception("ip not in allow list");
            }
        }
    }
}
