package com.trade.common.annotation;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ReflectUtil;
import com.trade.common.constant.HttpStatus;
import com.trade.common.exception.ServiceException;
import com.trade.common.utils.DateUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Aspect
@Component
public class RewriteDateAspect {

    private static final Logger logger = LoggerFactory.getLogger(RewriteDateAspect.class);

    /**
     * RewriteDateAspect 的切点为被 @RewriteDate 标记的位置
     */
    @Pointcut("@annotation(com.trade.common.annotation.RewriteDate))")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void decodeRsaAroundAdvice(JoinPoint joinPoint) {
        try {
            // 获取切点方法的参数
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length < 1 || !(joinPoint.getSignature() instanceof MethodSignature)) {
                return;
            }
            // 如果参数中不存在·@RewriteDate·注解，则直接返回
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            boolean hasRewriteDateAnnotation = Arrays.stream(methodSignature.getMethod().getParameterAnnotations())
                    .flatMap(Arrays::stream).map(Annotation::annotationType)
                    .anyMatch(annType -> annType.equals(RewriteDate.class));
            if (!hasRewriteDateAnnotation) {
                return;
            }
            // 中间处理 ...
            // 对切点方法的参数进行重新赋值
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                // 基础类型不处理
                if (arg == null || arg.getClass().isPrimitive()) {
                    continue;
                }
                hasRewriteDateAnnotation = Arrays.stream(methodSignature.getMethod().getParameterAnnotations()[i])
                        .map(Annotation::annotationType).anyMatch(annType -> annType.equals(RewriteDate.class));
                if (!hasRewriteDateAnnotation) {
                    continue;
                }
                List<Field> fields = Arrays.stream(ReflectUtil.getFields(arg.getClass()))
                        .filter(field -> (Date.class.equals(field.getType()))
                                && AnnotationUtil.hasAnnotation(field, RewriteDateAt.class))
                        .collect(Collectors.toList());
                for (Field field : fields) {
                    RewriteDateAt annotation = AnnotationUtil.getAnnotation(field, RewriteDateAt.class);
                    if (annotation == null || RewriteDateAtEnum.NONE.equals(annotation.value())) {
                        continue;
                    }
                    Date date = (Date) ReflectUtil.getFieldValue(arg, field);
                    if (date == null) {
                        continue;
                    }
                    switch (annotation.value()) {
                        case START_OF_DAY:
                            ReflectUtil.setFieldValue(arg, field, DateUtils.atStartOfDay(date));
                            break;
                        case END_OF_DAY:
                            ReflectUtil.setFieldValue(arg, field, DateUtils.atEndOfDay(date));
                            break;
                        case START_OF_MONTH:
                            ReflectUtil.setFieldValue(arg, field, DateUtils.atStartOfMonth(date));
                            break;
                        case END_OF_MONTH:
                            ReflectUtil.setFieldValue(arg, field, DateUtils.atEndOfMonth(date));
                            break;
                        default:
                            break;
                    }
                }
            }
            // 执行切点方法，并传递重新赋值后的参数列表
        } catch (Throwable e) {
            logger.error("时间格式转化异常", e);
            throw new ServiceException("时间格式转化异常", HttpStatus.BAD_REQUEST);
        }
    }
}
