package cn.reghao.tnb.data.app.security;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 执行本 filter 前会经过 ServerHttpSecurity.OAuth2ResourceServerSpec.BearerTokenAuthenticationWebFilter
 * 若在 Authorization header 中存在 jwt bearer token, BearerTokenAuthenticationWebFilter 会自动将其转化为 JwtAuthenticationToken
 * 调用栈 ServerBearerTokenAuthenticationConverter -> BearerTokenAuthenticationToken
 * -> JwtReactiveAuthenticationManager -> JwtAuthenticationToken
 * @author reghao
 * @date 2024-11-14 17:41:37
 */
@Slf4j
public class TokenTransferFilter implements WebFilter {
    private final String cookieName = "USERDATA";
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
        OBJECT_MAPPER.registerModule(new Jdk8Module());
        OBJECT_MAPPER.registerModule(new JavaTimeModule());
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        /*return ReactiveSecurityContextHolder.getContext().switchIfEmpty(Mono.defer(() -> {
            Authentication authentication = createAuthentication(exchange);
            SecurityContext securityContext = new SecurityContextImpl(authentication);
            log.debug("Populated SecurityContext with anonymous token: {}", authentication);
            return chain.filter(exchange)
                    .subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))
                    .then(Mono.empty());
        })).flatMap((securityContext) -> {
            log.debug("SecurityContext contains anonymous token: {}", securityContext.getAuthentication());
            return chain.filter(exchange);
        });*/

        return chain.filter(exchange);
        /*return ReactiveSecurityContextHolder.getContext()
                .map(securityContext -> {
                    return securityContext.getAuthentication();
                })
                .cast(JwtAuthenticationToken.class)
                .flatMap(authentication -> {
                    ServerHttpRequest request = exchange.getRequest();
                    request = request.mutate()
                            .header("tokenInfo", toJson(authentication.getPrincipal()))
                            .build();
                    ServerWebExchange newExchange = exchange.mutate().request(request).build();
                    return chain.filter(newExchange);
                });*/
    }

    protected Authentication createAuthentication(ServerWebExchange exchange) {
        String userdata = getUserData(exchange.getRequest());
        if (userdata.isBlank()) {
            return getJwtAuthenticationToken();
        }

        String jwtToken = userdata;
        return getJwtAuthenticationToken();
    }

    protected JwtAuthenticationToken getJwtAuthenticationToken() {
        String tokenValue = "anonymous";
        Instant issuedAt = Instant.now();
        Instant expiresAt = Instant.ofEpochSecond(System.currentTimeMillis() + 3600_000);
        Map<String, Object> headers = new HashMap<>();
        headers.put("anonymous", "anonymous");
        Map<String, Object> claims = new HashMap<>();
        claims.put("anonymous", "anonymous");

        Jwt jwt = new Jwt(tokenValue, issuedAt, expiresAt, headers, claims);
        return new JwtAuthenticationToken(jwt);
    }

    private String getUserData(ServerHttpRequest request) {
        String userdata = "";
        List<String> headerValues = request.getHeaders().get("Authorization");
        if (headerValues != null && headerValues.size() == 1) {
            String headerValue = headerValues.get(0);
            if (headerValue != null && headerValue.startsWith("Bearer ")) {
                userdata = headerValue.replace("Bearer ", "");
            }
        }

        if (userdata.isBlank()) {
            MultiValueMap<String, HttpCookie> cookies = request.getCookies();
            for (List<HttpCookie> list : cookies.values()) {
                for (HttpCookie cookie : list) {
                    String name = cookie.getName();
                    if (name.equalsIgnoreCase(cookieName)) {
                        userdata = cookie.getValue();
                    }
                }
            }
        }
        return userdata;
    }

    public String toJson(Object obj) {
        try {
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            return null;
        }
    }
}
