package com.codvision.webcore.config.encrypt.advice;

import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import com.codvision.commoncore.common.ResponseEntity;
import com.codvision.webcore.config.encrypt.ApiSecretProperties;
import com.codvision.webcore.config.encrypt.ApiSecretSupports;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * 响应数据加密处理
 * 仅对使用{@link org.springframework.web.bind.annotation.ResponseBody}进行标记的方法生效
 *
 * @author hxl
 */
@ControllerAdvice
@RequiredArgsConstructor
@ConditionalOnProperty(value = ApiSecretProperties.PREFIX + ".enabled", havingValue = "true")
@ConditionalOnExpression("!'${security.api.encrypt.secret-key}'.isEmpty()")
public class ApiSecretResponseBodyAdvice implements ResponseBodyAdvice<ResponseEntity<Object>> {

    private final ObjectMapper objectMapper;
    private final ApiSecretProperties properties;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return ApiSecretSupports.supports(returnType, properties);
    }

    @Override
    @SneakyThrows
    public ResponseEntity<Object> beforeBodyWrite(@Nullable ResponseEntity<Object> responseEntity, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (responseEntity == null || responseEntity.getData() == null) {
            return responseEntity;
        }

        byte[] bytes = objectMapper.writeValueAsBytes(responseEntity.getData());
        AES aes = new AES(Mode.CBC, Padding.ZeroPadding, new SecretKeySpec(properties.getSecretKey().getBytes(), "AES"),
                new IvParameterSpec(properties.getSecretKey().getBytes()));
        return responseEntity.data(aes.encryptBase64(bytes));
    }
}
