package top.hunfan.mail.aspect;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.persistence.Column;
import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import top.hunfan.mail.domain.Constants;
import top.hunfan.mail.domain.R;
import top.hunfan.mail.entity.po.MailSendLog;
import top.hunfan.mail.service.MailSendLogService;
import top.hunfan.mail.utils.StringTools;
import top.hunfan.mail.utils.ThreadLocalUtils;

/**
 * 邮件发送日志切面
 * @author hf-hf
 * @date 2019/1/9 14:15
 */
@Slf4j
@Aspect
@Component
@Order(2)
public class MailSendLogAspect {

    /**
     * 定义线程池
     * @author hf-hf
     * @date 2019/1/10 11:56
     */
    private final ExecutorService SAVE_LOG_EXECUTOR_SERVICE = Executors.newFixedThreadPool(10);

    @Autowired
    private MailSendLogService sendLogService;

    /**
     * 定义切点
     * @author hf-hf
     * @date 2019/1/10 11:56
     */
    @Pointcut("execution(public * top.hunfan.mail.service.MailService.send(..)) ")
    public void log(){
        
    }
    
    @AfterReturning(returning="result", pointcut="log()")
    public void doAfterReturning(JoinPoint joinPoint, R result) {
        ServletRequestAttributes sra =  (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        // 下面两个数组中,参数值和参数名的个数和位置是一一对应的。
        // 参数值
        Object[] args = joinPoint.getArgs();
        // 参数名
        String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        // 异步存储日志
        CompletableFuture.runAsync(new SaveLogThread(sendLogService, args, argNames,
                ThreadLocalUtils.get(Constants.CURRENT_MAIL_FROM), request.getRemoteHost(),
                result.getCode(), result.getMessage()), SAVE_LOG_EXECUTOR_SERVICE);
        log.debug("方法返回值:" + result);
    }

    /**
     * 保存日志线程
     * @author hf-hf
     * @date 2019/1/10 11:52
     */
    @AllArgsConstructor
    public static class SaveLogThread extends Thread{

        private MailSendLogService sendLogService;

        private Object[] args;

        private String[] argNames;

        private String form;

        private String ip;

        private Integer code;

        private String message;

        @Override
        public void run() {
            ThreadLocalUtils.remove(Constants.CURRENT_MAIL_FROM);
            MailSendLog sendLog = new MailSendLog();
            for(int i=0;i < argNames.length;i++){
                if("attachmentFile".equals(argNames[i])){
                    continue;
                }
                Field field = ReflectionUtils.findField(sendLog.getClass(), argNames[i]);
                Column column = field.getAnnotation(Column.class);
                field.setAccessible(true);
                String objStr = null == column ? StringTools.asString(args[i])
                        : StringTools.asString(args[i], column.length());
                if(null != objStr){
                    try {
                        field.set(sendLog, objStr);
                    } catch (IllegalAccessException e) {

                    }
                }
            }
            sendLog.setForm(this.form);
            sendLog.setIp(this.ip);
            sendLog.setSentCode(this.code);
            sendLog.setSentMessage(this.message);
            sendLog.setCreateTime(new Date());
            sendLogService.save(sendLog);
        }
    }
}