package com.inspect.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.inspect.base.core.utils.ServletUtils;
import com.inspect.base.core.utils.SmTools;
import com.inspect.base.core.web.domain.AjaxResult;
import com.inspect.base.redis.service.RedisService;
import com.inspect.gateway.config.properties.SecondUrls;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest.Builder;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Component
public class SecondFilter implements GlobalFilter, Ordered {
    private static final Logger log = LoggerFactory.getLogger(SecondFilter.class);
    private final RedisService redisService;
    @Resource
    private SecondUrls secondUrls;

    public SecondFilter(RedisService redisService) {
        this.redisService = redisService;
    }

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        Builder mutate = request.mutate();
        String url = request.getURI().getPath();
        List urls = this.getUrls(this.secondUrls);
        ServerHttpResponse response = exchange.getResponse();
        String anAbstract;
        if (this.needIntercept(url, urls)) {
            anAbstract = this.getTicket(request);
            if (StringUtils.isNotBlank(anAbstract) && this.redisService.hasKey(anAbstract)) {
                this.redisService.deleteObject(anAbstract);
                return chain.filter(exchange.mutate().request(mutate.build()).build());
            } else {
                return ServletUtils.webFluxResponseWriter(response, "请进行确认后操作", 20010);
            }
        } else {
            if (request.getMethod().equals(HttpMethod.POST) || request.getMethod().equals(HttpMethod.PUT)) {
                anAbstract = request.getQueryParams().getFirst("abstract");
                HttpHeaders headers = request.getHeaders();
                MediaType contentType = headers.getContentType();
                if (StringUtils.isNoneBlank(anAbstract) && (MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType))) {
                    Flux<DataBuffer> bodyBuf = request.getBody();
                    AtomicReference<String> requestBody = new AtomicReference(StringUtils.EMPTY);
                    bodyBuf.subscribe((buffer) -> {
                        CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
                        requestBody.set(charBuffer.toString());
                    });
                    String body = (String) requestBody.get();
                    if (StringUtils.isNotBlank(body)) {
                        String sm3Result = SmTools.sm3(body);
                        if (url.contains("auth/login")) {
                            Object cacheObject = this.redisService.getCacheObject(anAbstract);
                            if (null != cacheObject) {
                                return this.errorResponse(response, "请求不正确,请勿重复提交");
                            }

                            this.redisService.setCacheObject(anAbstract, 1, 1L, TimeUnit.MINUTES);
                        }

                        if (!StringUtils.equalsIgnoreCase(anAbstract, sm3Result)) {
                            return this.errorResponse(response, "数据被破坏，请重试");
                        }
                    }
                }
            }

            return chain.filter(exchange.mutate().request(mutate.build()).build());
        }
    }

    public Mono<Void> errorResponse(ServerHttpResponse response, String text) {
        response.setStatusCode(HttpStatus.OK);
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        AjaxResult fail = AjaxResult.fail(500, text);
        byte[] bits = JSONObject.toJSONString(fail).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.OK);
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }

    public String getTicket(ServerHttpRequest request) {
        String ticket = request.getHeaders().getFirst("ticket");
        return ticket;
    }

    public boolean needIntercept(String target, List<String> urls) {
        Iterator<String> iterator = urls.iterator();

        String url;
        do {
            if (!iterator.hasNext()) {
                return false;
            }

            url = iterator.next();
        } while (!target.contains(url));

        return true;
    }

    public List<String> getUrls(SecondUrls secondUrls) {
        if (null != secondUrls && !StringUtils.isBlank(secondUrls.getUrls())) {
            String urls = secondUrls.getUrls();
            String[] urlArr = urls.replaceAll("，", "").split(",");
            return Arrays.asList(urlArr);
        } else {
            return Collections.EMPTY_LIST;
        }
    }

    public int getOrder() {
        return -200;
    }
}
