package com.github.wxiaoqi.security.gate.filter;

import com.alibaba.fastjson.JSONObject;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.api.vo.authority.PermissionInfo;
import com.github.wxiaoqi.security.api.vo.log.LogInfo;
import com.github.wxiaoqi.security.auth.client.config.ServiceAuthConfig;
import com.github.wxiaoqi.security.auth.client.config.UserAuthConfig;
import com.github.wxiaoqi.security.auth.client.jwt.ServiceAuthUtil;
import com.github.wxiaoqi.security.auth.client.jwt.UserAuthUtil;
import com.github.wxiaoqi.security.auth.common.util.jwt.IJWTInfo;
import com.github.wxiaoqi.security.common.constant.RequestTypeConstants;
import com.github.wxiaoqi.security.common.context.BaseContextHandler;
import com.github.wxiaoqi.security.common.msg.BaseResponse;
import com.github.wxiaoqi.security.common.msg.auth.TokenForbiddenResponse;
import com.github.wxiaoqi.security.gate.feign.IAppUserService;
import com.github.wxiaoqi.security.gate.feign.ILogService;
import com.github.wxiaoqi.security.gate.feign.IUserService;
import com.github.wxiaoqi.security.gate.handler.RequestBodyRoutePredicateFactory;
import com.github.wxiaoqi.security.gate.utils.DBLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
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.context.annotation.Lazy;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author ace
 * @create 2018/3/12.
 */
@Configuration
@Slf4j
public class AccessGatewayFilter implements GlobalFilter {
    @Autowired
    @Lazy
    private IUserService userService;

    @Autowired
    @Lazy
    private IAppUserService appUserService;

    @Autowired
    @Lazy
    private ILogService logService;

    @Autowired
    UserFeign userFeign;

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

    //    @Value("${zuul.prefix}")
//    private String zuulPrefix;

    private static final String GATE_WAY_PREFIX = "/api";
    @Value("${gate.ignore.unauth}")
    private String GATE_WAY_UNAUTH;
    @Autowired
    private UserAuthUtil userAuthUtil;

    @Autowired
    private ServiceAuthConfig serviceAuthConfig;

    @Autowired
    private UserAuthConfig userAuthConfig;

    @Autowired
    private ServiceAuthUtil serviceAuthUtil;

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, GatewayFilterChain gatewayFilterChain) {

        log.error("请求进入:AccessGatewayFilter");
        log.info("check token and user permission....");
        LinkedHashSet requiredAttribute = serverWebExchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        ServerHttpRequest request = serverWebExchange.getRequest();
//        setLogService(serverWebExchange, gatewayFilterChain);
        String requestUri = request.getPath().pathWithinApplication().value();
        if (requiredAttribute != null) {
            Iterator<URI> iterator = requiredAttribute.iterator();
            while (iterator.hasNext()) {
                URI next = iterator.next();
                if (next.getPath().startsWith(GATE_WAY_PREFIX)) {
                    requestUri = next.getPath().substring(GATE_WAY_PREFIX.length());
                }
            }
        }
        final String method = request.getMethod().toString();
        BaseContextHandler.setToken(null);
        ServerHttpRequest.Builder mutate = request.mutate();
        // 不进行拦截的地址
        if (isStartWith(requestUri)) {
            //zjw20190806添加 start ：避免已带token 却拿不到服务client/用户信息
//            IJWTInfo user = null;
//            try {
//                user = getJWTUser(request, mutate);
            // 申请客户端密钥头
            mutate.header(serviceAuthConfig.getTokenHeader(), serviceAuthUtil.getClientToken());
//            } catch (Exception e) {
//                log.info("调用免登陆接口的用户Token过期异常", e);
//            }
            //zjw20190806添加 end
            ServerHttpRequest build = mutate.build();
            return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());
        }
        IJWTInfo user = null;
        try {
            user = getJWTUser(request, mutate);
        } catch (Exception e) {
            log.error("用户Token过期异常", e);
            return getVoidMono(serverWebExchange, new TokenForbiddenResponse("User Token Forbidden or Expired!"));
        }
        List<PermissionInfo> permissionIfs = userService.getAllPermissionInfo();
        // 判断资源是否启用权限约束
        Stream<PermissionInfo> stream = getPermissionIfs(requestUri, method, permissionIfs);
        List<PermissionInfo> result = stream.collect(Collectors.toList());
        PermissionInfo[] permissions = result.toArray(new PermissionInfo[]{});
        if (permissions.length > 0) {
            if (checkUserPermission(permissions, serverWebExchange, user)) {
                return getVoidMono(serverWebExchange, new TokenForbiddenResponse("User Forbidden!Does not has Permission!"));
            }
        }


        // 申请客户端密钥头
        mutate.header(serviceAuthConfig.getTokenHeader(), serviceAuthUtil.getClientToken());
        ServerHttpRequest build = mutate.build();
        return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());

    }

    /**
     * 网关抛异常
     *
     * @param body
     */
    @NotNull
    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));
    }


    /**
     * 获取目标权限资源
     *
     * @param requestUri
     * @param method
     * @param serviceInfo
     * @return
     */
    private Stream<PermissionInfo> getPermissionIfs(final String requestUri, final String method, List<PermissionInfo> serviceInfo) {
        return serviceInfo.parallelStream().filter(new Predicate<PermissionInfo>() {
            @Override
            public boolean test(PermissionInfo permissionInfo) {
                String uri = permissionInfo.getUri();
                if (uri.indexOf("{") > 0) {
                    uri = uri.replaceAll("\\{\\*\\}", "[a-zA-Z-\\\\d]+");
                }
                String regEx = "^" + uri + "$";
                return (Pattern.compile(regEx).matcher(requestUri).find())
                        && method.equals(permissionInfo.getMethod());
            }
        });
    }

    private void setCurrentUserInfoAndLog(ServerWebExchange serverWebExchange, IJWTInfo user, PermissionInfo pm) {
        String host = serverWebExchange.getRequest().getRemoteAddress().toString();
        LogInfo logInfo = new LogInfo(pm.getMenu(), pm.getName(), pm.getUri(), new Date(), user.getId(), user.getName(), host, String.valueOf(serverWebExchange.getAttributes().get(RequestBodyRoutePredicateFactory.REQUEST_BODY_ATTR)));
        DBLog.getInstance().setLogService(logService).offerQueue(logInfo);
    }

    /**
     * 返回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);
        return userAuthUtil.getInfoFromToken(authToken);
    }


    private boolean checkUserPermission(PermissionInfo[] permissions, ServerWebExchange ctx, IJWTInfo user) {
        List<PermissionInfo> permissionInfos;
        // keliii 根据请求类型区别权限
        if (RequestTypeConstants.APP.equals(user.getRequestType())) {
            permissionInfos = appUserService.getPermissionByUsername(user.getUniqueName());
        } else {
            permissionInfos = userService.getPermissionByUsername(user.getUniqueName());
        }
        PermissionInfo current = null;
        for (PermissionInfo info : permissions) {
            boolean anyMatch = permissionInfos.parallelStream().anyMatch(new Predicate<PermissionInfo>() {
                @Override
                public boolean test(PermissionInfo permissionInfo) {
                    return permissionInfo.getCode().equals(info.getCode());
                }
            });
            if (anyMatch) {
                current = info;
                break;
            }
        }
        if (current == null) {
            return true;
        } else {
            if (!RequestMethod.GET.toString().equals(current.getMethod())) {
                setCurrentUserInfoAndLog(ctx, user, current);
            }
            return false;
        }
    }


    /**
     * URI是否以什么打头
     *
     * @param requestUri
     * @return
     */
    private boolean isStartWith(String requestUri) {
        boolean flag = false;
        for (String s : GATE_WAY_UNAUTH.split(",")) {
            if (requestUri.contains(s)) {
                return true;
            }
        }
        for (String s : startWith.split(",")) {
            if (requestUri.startsWith(s)) {
                return true;
            }
        }
        return flag;
    }

    /**
     * 网关抛异常
     *
     * @param body
     * @param code
     */
    private Mono<Void> setFailedRequest(ServerWebExchange serverWebExchange, String body, int code) {
        serverWebExchange.getResponse().setStatusCode(HttpStatus.OK);
        return serverWebExchange.getResponse().setComplete();
    }


//    public void setLogService(ServerWebExchange exchange, GatewayFilterChain chain) {
//        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
//        ServerRequest serverRequest = new DefaultServerRequest(exchange);
//
//        // 如果是json格式，将body内容转化为object or map 都可
//        if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
//            Mono<Object> modifiedBody = serverRequest.bodyToMono(Object.class)
//                    .flatMap(body -> {
//                        recordLog(exchange.getRequest(), body);
//                        return Mono.just(body);
//                    });
//
//        }
//        // 如果是表单请求
//        else if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) {
//            Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
//                    // .log("modify_request_mono", Level.INFO)
//                    .flatMap(body -> {
//                        recordLog(exchange.getRequest(), body);
//
//                        return Mono.just(body);
//                    });
//
//        }
//        // TODO 这里未来还可以限制一些格式
//
//        // 无法兼容的请求，则不读取body，像Get请求这种
//        recordLog(exchange.getRequest(), "");
//    }
//
//    private void recordLog(ServerHttpRequest request, Object body) {
//        // 记录要访问的url
//        if (!getNotLogUri().contains(request.getURI().getRawPath())) {
//
//
//            StringBuilder builder = new StringBuilder();
//            log.info("=================请求uri:" + request.getURI().getRawPath());
//            // 记录访问的方法
//            HttpMethod method = request.getMethod();
//            if (null != method) {
//                log.info("=================请求方法:" + method.name());
//            }
//            request.getHeaders().add("uuid", UUID.randomUUID().toString());
//            // 记录头部信息
//            builder.append(", header { ");
//            for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
//                builder.append(entry.getKey()).append(":").append(StringUtils.join(entry.getValue(), ",")).append(",");
//                if("uuid".equals(entry.getKey())) {
//                    if(entry.getValue() != null && entry.getValue().size() > 0) {
//                        log.info("=================请求方法uuid:" + entry.getValue().get(0));
//                    }
//                }
//                if("Authorization".equals(entry.getKey())) {
//                    if(entry.getValue() != null && entry.getValue().size() > 0) {
//                        getAdminUserInfo(entry.getValue().get(0));
//                    }
//
//                }
//            }
//            log.info("=================请求头header:" + builder.toString());
//            // 记录参数
//            builder = new StringBuilder();
//            // 处理get的请求
//            if (null != method && HttpMethod.GET.matches(method.name())) {
//                // 记录请求的参数信息 针对GET 请求
//                MultiValueMap<String, String> queryParams = request.getQueryParams();
//                for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
//                    builder.append(entry.getKey()).append("=").append(StringUtils.join(entry.getValue(), ",")).append(",");
//                }
//            } else {
//                // 从body中读取参数
//                builder.append(body);
//            }
//            log.info("=================请求参数:" + builder.toString());
//        }
//    }
//
//    private void getAdminUserInfo(String token) {
//        if(token !=null) {
//            UserDTO userDTO = userFeign.userinfoByToken(token).getData();
//            if(userDTO != null) {
//                log.info("=================后台用户名：username = {}", userDTO.getUsername());
//                log.info("=================后台姓名： name = {}", userDTO.getName());
//            } else {
//                AppUserDTO appUserDTO = userFeign.userDetailByToken(token).getData();
//                if(appUserDTO != null) {
//                    log.info("=================APP用户名：userId = {}", appUserDTO.getUserid());
//                    log.info("=================APP姓名： name = {}", appUserDTO.getRealname());
//                    log.info("=================APP手机号： phone = {}", appUserDTO.getUsername());
//                }
//            }
//        }
//    }
//
//    @Value("${logback.ignore-log-path}")
//    String[] path;
//    public List<String> getNotLogUri() {
//        return Arrays.asList(path);
//    }


}
