package com.fdkankan.gateway.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fdkankan.common.constant.RedisKey;
import com.fdkankan.common.constant.ServerCode;
import com.fdkankan.common.response.ResultData;
import com.fdkankan.common.util.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Slf4j
@Component
public class TokenFilter implements GlobalFilter, Ordered {

    private static final String TOKEN = "token";

    private static final Map<String, List<String>> permissionMap = new HashMap<>();

    private static final List<String> ignoreAuthUrls = new ArrayList<>();

//    @Resource
//    private RedisUtil redisUtil;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    static {
        List<String> userPermissions = new ArrayList<>();
        permissionMap.put("user", userPermissions);
        userPermissions.add("/api/user");
        userPermissions.add("/api/scene/edit");
        userPermissions.add("/api/scene/edit");
        userPermissions.add("/api/order/scanPay");

        List<String> managerPermissions = new ArrayList<>();
        permissionMap.put("manager", managerPermissions);
        userPermissions.add("/api/manager");

        List<String> agentPermissions = new ArrayList<>();
        permissionMap.put("agent", agentPermissions);
        userPermissions.add("/api/agent");

        List<String> appPermissions = new ArrayList<>();
        permissionMap.put("app", appPermissions);
        userPermissions.add("/api/app");


        ignoreAuthUrls.add("/api/sso");

    }


    /**
     * 忽略过滤的路径
     */

//    @Value("${com.cuslink.ignoreAuthUrls}")
//    private String ignoreAuthUrls;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // 获取当前请求路径
        String path = request.getURI().getPath();
        log.info("当前路径为" + path + ",判断是否为忽略路径.");
        // 查询是否是忽略路径
        boolean ignore = decideIgnore(path);
        if (ignore) {
            // 忽略路径直接放行
            log.info("当前路径为" + path + ",是忽略过滤路径,直接放行!");
            return chain.filter(exchange);
        } else {
            ServerCode serverCode = null;
            log.info("当前路径为" + path + ",不是忽略过滤路径,开始校验!");
            // 获取当前请求中的token
            String token = request.getHeaders().getFirst(TOKEN);
            if(StrUtil.isBlank(token)){
                serverCode = ServerCode.TOKEN_NOT_FOUND;
                DataBuffer buffer = setResponseInfo(response, ResultData.error(serverCode.code(), serverCode.message()));
                return response.writeWith(Mono.just(buffer));
            }else{
                //验证token是否被篡改
                Claims claims = null;
                try {
                    claims = JwtUtil.parseJWT(token);
                }catch (Exception e){
                    serverCode = ServerCode.TOKEN_ILLEGAL;
                    DataBuffer buffer = setResponseInfo(response, ResultData.error(serverCode.code(), serverCode.message()));
                    return response.writeWith(Mono.just(buffer));
                }

                String userName = (String)claims.get("userName");
                String loginType = (String)claims.get("loginType");

                //查询redis，token是否存在
//                String key = String.format(RedisKey.TOKEN_USER_TYPE, userName, loginType);
//                boolean exist = redisTemplate.hasKey(key);
//                if(!exist){
//                    DataBuffer buffer = setResponseInfo(response, ResultData.error(ServerCode.TOKEN_NOT_FOUND.code(), ServerCode.TOKEN_NOT_FOUND.message()));
//                    return response.writeWith(Mono.just(buffer));
//                }

                // TODO: 2021/12/21  校验资源权限  用户资源可以从token中取，这里暂时先写死
                //根据用户ID查询角色列表
                //根据角色查询可访问资源权限列表
                //遍历权限列表，对比path，如果匹配上，则放行
                List<String> permissions = permissionMap.get(loginType);
                Boolean isPermission = true;
//                for (String permission : permissions) {
//                    if(path.contains(permission)){
//                        isPermission = true;
//                        break;
//                    }
//                }

                if(isPermission){
                    return chain.filter(exchange);
                }
            }
        }
        DataBuffer buffer = setResponseInfo(response, ResultData.error(ServerCode.AUTH_FAIL.code(), ServerCode.AUTH_FAIL.message()));
        return response.writeWith(Mono.just(buffer));
    }


    private DataBuffer setResponseInfo(ServerHttpResponse response, ResultData resultData) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] responseByte = JSONUtil.parse(resultData).toString().getBytes();
        DataBuffer buffer = response.bufferFactory().wrap(responseByte);
        return buffer;
    }

    /**
     * 判断是否是忽略路径
     *
     * @param servletPath
     * @return
     */
    private boolean decideIgnore(String servletPath) {
        //跳过不需要验证的路径
        for (String ignore : ignoreAuthUrls) {
            if (servletPath.contains(ignore)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int getOrder() {
        return -101;
    }

}
