package com.example.apigatewayservice.filter;

import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.env.Environment;
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;

@Component
@Slf4j
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> {
    Environment env;



    public AuthorizationHeaderFilter(Environment env){
        super(Config.class);
        this.env = env;
    }

    //추후에 작성할 Config 클래스
    public static class Config{

    }

    // Override

    /*
     * 1. 해당 필터는 Jwt 토큰을 인증하는 과정을 거칩니다.
     * 2. 기본적으로 토큰정보 확인 하는 과정 후 인증완료의 메커니즘 입니다.
     * 3. login -> token -> users (with token) -> header(include token) 안에 token 정보가 있습니다.
     */
    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();


            // 해당 IF문은 인증 실패 시 에러처리 하는 문구입니다.
            if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
                return onError(exchange,"no authorization header", HttpStatus.UNAUTHORIZED);
            }

            String authorizationHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);
            String jwt = authorizationHeader.replace("Bearer","");

            if(!isJwtValid(jwt)){
                return onError(exchange,"JWT token is not vaild", HttpStatus.UNAUTHORIZED);
            }
            return chain.filter(exchange);
        });
    }

    //JWT 유효성 체크
    private boolean isJwtValid(String jwt) {
        boolean returnValue = true;

        String subject = null;
        try {
            // https://jwt.io 에서 저희가 발급한 토큰을 체크하고
            // 해당 토큰값에 getBody 안에 Subject라는 항목 이 있는지 체크합니다.
            subject = Jwts.parser().setSigningKey(env.getProperty("token.secret")).parseClaimsJws(jwt).getBody().getSubject();
        }catch(Exception ex){
            returnValue = false;
        }

        if (subject == null || subject.isEmpty()){
            returnValue = false;
        }
        return returnValue;
    }

    // Mono, Flux -> Spring WebFlux
    private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(httpStatus);

        log.error(err);
        //Mono타입으로 전달할 수 있는 함수
        return response.setComplete();
    }
}
