package com.dxhy.gate.v2.filter;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashSet;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSONObject;
import com.dxhy.auth.client.config.ServiceAuthConfig;
import com.dxhy.auth.client.configuration.UserAuthConfig;
import com.dxhy.auth.client.jwt.ServiceAuthUtil;
import com.dxhy.auth.client.jwt.UserAuthUtil;
import com.dxhy.common.jwt.IJWTInfo;
import com.dxhy.common.msg.BaseResponse;
import com.dxhy.common.msg.auth.TokenForbiddenResponse;
import com.dxhy.common.utils.BaseContextHandler;
import com.dxhy.gate.v2.service.TokenService;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @author jiaohongyang
 * @date 2019年3月26日 下午6:01:42
 *
 */
@SuppressWarnings("AlibabaUndefineMagicConstant")
@Configuration
@Slf4j

public class AccessGatewayFilter implements GlobalFilter {

    @Value("${gate.ignore.startWith}")
    private String startWith;

    private static final String GATE_WAY_PREFIX = "/api";
    @Resource
    private UserAuthUtil userAuthUtil;

    @Resource
    private ServiceAuthConfig serviceAuthConfig;

    @Resource
    private UserAuthConfig userAuthConfig;

    @Resource
    private ServiceAuthUtil serviceAuthUtil;

    @Resource
    private TokenService tokenService;

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, GatewayFilterChain gatewayFilterChain) {
        log.info("check token and user permission....");
        LinkedHashSet<?> requiredAttribute =
            serverWebExchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        ServerHttpRequest serverHttpRequest = serverWebExchange.getRequest();
        String requestUri = serverHttpRequest.getPath().pathWithinApplication().value();
        log.info("请求地址：{}", requestUri);
        for (URI next : (Iterable<URI>)requiredAttribute) {
            if (next.getPath().startsWith(GATE_WAY_PREFIX)) {
                requestUri = next.getPath().substring(GATE_WAY_PREFIX.length());
            }
        }

        ServerHttpRequest.Builder mutate = serverHttpRequest.mutate();
        // 不进行拦截的地址、
        log.info("处理过后请求地址：{}", requestUri);
        if (isStartWith(requestUri)) {
            ServerHttpRequest build = mutate.build();
            return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());
        }
        try {
            IJWTInfo user = getJwtUser(serverHttpRequest, mutate);
            if (user == null) {
                return getVoidMono(serverWebExchange, new TokenForbiddenResponse("由于长时间未操作，已退出登录，请重新登录！"));
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("用户Token过期异常，{}", e.getMessage());
            return getVoidMono(serverWebExchange, new TokenForbiddenResponse("由于长时间未操作，已退出登录，请重新登录！"));
        }
        // 申请客户端密钥头
        mutate.header(serviceAuthConfig.getTokenHeader(), serviceAuthUtil.getClientToken());
        return gatewayFilterChain.filter(serverWebExchange.mutate().request(mutate.build()).build());
    }

    /**
     * 网关抛异常
     *
     * @param serverWebExchange
     *            入参
     * @param body
     *            入参
     * @return 返回
     */
    private Mono<Void> getVoidMono(ServerWebExchange serverWebExchange, BaseResponse body) {
        serverWebExchange.getResponse().setStatusCode(HttpStatus.OK);
        byte[] bytes = JSONObject.toJSONString(body).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverWebExchange.getResponse().bufferFactory().wrap(bytes);
        return serverWebExchange.getResponse().writeWith(Flux.just(buffer));
    }

    /**
     * 返回session中的用户信息
     *
     * @param request
     *            入参
     * @param ctx
     *            入参
     * @return 返回
     */
    private IJWTInfo getJwtUser(ServerHttpRequest request, ServerHttpRequest.Builder ctx) throws Exception {
        List<String> strings = request.getHeaders().get(userAuthConfig.getTokenHeader());
        String authToken = null;
        if (strings != null) {
            authToken = strings.get(0);
        }
        if (StringUtils.isBlank(authToken)) {
            strings = request.getQueryParams().get("token");
            if (strings != null) {
                authToken = strings.get(0);
            }
        }
        ctx.header(userAuthConfig.getTokenHeader(), authToken);
        BaseContextHandler.setToken(authToken);
        IJWTInfo infoFromToken = userAuthUtil.getInfoFromToken(authToken);
        String token = tokenService.getToken(infoFromToken.getLoginName(), authToken);
        if (StringUtils.isNotBlank(token)) {
            return infoFromToken;
        } else {
            return null;
        }
    }

    /**
     * URI是否以什么打头
     *
     * @param requestUri
     *            路径判断
     * @return 返回路径
     */
    private boolean isStartWith(String requestUri) {
        log.info("token拦截校验请求地址：{}", requestUri);
        for (String s : startWith.split(",")) {
            String replaceUrl = s.replaceAll(" ", "");

            if (requestUri.startsWith(replaceUrl)) {
                log.info("请求地址地址：{} ，配置地址：{} ,是否拦截token：{}", requestUri, replaceUrl, false);
                return true;
            }
        }
        log.info("是否拦截token：{}", true);
        return false;
    }

}
