package com.qmth.themis.business.aspect;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.reflect.TypeToken;
import com.qmth.themis.business.annotation.Logs;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.request.TEExamDto;
import com.qmth.themis.business.entity.*;
import com.qmth.themis.business.enums.InvigilateMonitorStatusEnum;
import com.qmth.themis.business.enums.LogEnum;
import com.qmth.themis.business.service.*;
import com.qmth.themis.business.util.JacksonUtil;
import com.qmth.themis.business.util.ServletUtil;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.GsonUtil;
import com.qmth.themis.common.util.ResultUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @Description: 日志切入点
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2023/2/27
 */
@Aspect
@Component
public class LogsAspect {
    private final static Logger log = LoggerFactory.getLogger(LogsAspect.class);

    @Resource
    TEExamPaperService teExamPaperService;

    @Resource
    TSLogService tsLogService;

    @Resource
    TEExamService teExamService;

    @Resource
    TEExamActivityService teExamActivityService;

    @Resource
    TEExamCourseService teExamCourseService;

    /**
     * api切入点
     */
    @Pointcut("@annotation(com.qmth.themis.business.annotation.Logs)")
    public void logAspect() {
    }

    /**
     * 后置通知
     *
     * @param joinPoint
     */
    @Around(value = "logAspect()")
    public Object afterReturnAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        StringJoiner stringJoiner = new StringJoiner("");
        try {
            MethodSignature msig = (MethodSignature) joinPoint.getSignature();
            String className = msig.getDeclaringTypeName();
            String methodName = msig.getName();
            Object[] args = joinPoint.getArgs();
            String[] paramsName = msig.getParameterNames();
            Logs logs = msig.getMethod().getAnnotation(Logs.class);
            HttpServletRequest request = ServletUtil.getRequest();
            stringJoiner.add("============logs 请求地址============:").add(request.getServletPath()).add("\r\n");
            stringJoiner.add("============logs 类============:").add(className).add("\r\n");
            stringJoiner.add("============logs 方法============:").add(methodName).add("\r\n");
            stringJoiner.add("============logs 注解============:").add(logs.value().name()).add("|").add(logs.value().getTitle()).add("|").add(logs.value().getObject().toString()).add("\r\n");

            List<Map> list = (List<Map>) getArgsData(args, paramsName, stringJoiner);
            List<Object> objectDbList = null, objectList = null;
            List<String> nameList = null;
            List<Long> objIdList = null;
            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
            if (!CollectionUtils.isEmpty(list)) {
                objectDbList = new ArrayList<>(list.size());
                objectList = new ArrayList<>(list.size());
                nameList = new ArrayList<>(list.size());
                objIdList = new ArrayList<>(list.size());
                for (int i = 0; i < list.size(); i++) {
                    Class c = (Class) logs.value().getObject();
                    Object objectDb = null;
                    Object object = null;
                    if (c == TEExamPaper.class) {//考试试卷
                        TEExamPaper argParam = (TEExamPaper) GsonUtil.fromJson(GsonUtil.toJson(list.get(i)), c);
                        if (Objects.nonNull(argParam.getId())) {
                            objectDb = teExamPaperService.getById(argParam.getId());
                        } else {
                            continue;
                        }
                        object = new TEExamPaper();
                        BeanUtils.copyProperties(objectDb, object);
                        ((TEExamPaper) object).setWeight(argParam.getWeight());
                        ((TEExamPaper) object).setAudioPlayCount(argParam.getAudioPlayCount());
                        nameList.add(((TEExamPaper) object).getName());
                        objIdList.add(((TEExamPaper) object).getId());
                    } else if (c == TEExam.class) {//考试批次
                        TEExamDto argParam = (TEExamDto) GsonUtil.fromJson(GsonUtil.toJson(list.get(i)), TEExamDto.class);
                        argParam.setOrgId(tbUser.getOrgId());
                        if (Objects.nonNull(argParam.getId())) {
                            ExamCacheBean examCacheBean = teExamService.getExamCacheBean(argParam.getId());
                            objectDb = teExamService.cacheConvert(examCacheBean);
                        } else {
                            continue;
                        }
                        argParam.setMonitorStatus(Objects.nonNull(objectDb) ? ((TEExam) objectDb).getMonitorStatus() : InvigilateMonitorStatusEnum.NOT_START);
                        object = new TEExam(argParam);
                        nameList.add(((TEExam) object).getName());
                        objIdList.add(((TEExam) object).getId());
                    } else if (c == TEExamActivity.class) {//考试场次
                        TEExamActivity argParam = (TEExamActivity) GsonUtil.fromJson(GsonUtil.toJson(list.get(i)), c);
                        if (Objects.nonNull(argParam.getId())) {
                            objectDb = teExamActivityService.getById(argParam.getId());
                        } else {
                            continue;
                        }
                        argParam.setCode(((TEExamActivity) objectDb).getCode());
                        object = new TEExamActivity();
                        BeanUtils.copyProperties(argParam, object);
                        nameList.add(((TEExamActivity) object).getCode());
                        objIdList.add(((TEExamActivity) object).getId());
                    } else if (c == TEExamCourse.class) {//考试科目
                        TEExamCourse argParam = (TEExamCourse) GsonUtil.fromJson(GsonUtil.toJson(list.get(i)), c);
                        QueryWrapper<TEExamCourse> teExamCourseQueryWrapper = new QueryWrapper<>();
                        teExamCourseQueryWrapper.lambda()
                                .eq(TEExamCourse::getExamId, argParam.getExamId())
                                .eq(TEExamCourse::getCourseCode, argParam.getCourseCode());
                        List<TEExamCourse> teExamCourseDbList = teExamCourseService.list(teExamCourseQueryWrapper);
                        if (!CollectionUtils.isEmpty(teExamCourseDbList)) {
                            for (int y = 0; y < teExamCourseDbList.size(); y++) {
                                objectDb = teExamCourseDbList.get(y);
                                object = new TEExamCourse();
                                BeanUtils.copyProperties(objectDb, object);
                                ((TEExamCourse) object).setObjectiveShuffle(argParam.getObjectiveShuffle());
                                ((TEExamCourse) object).setOptionShuffle(argParam.getOptionShuffle());
                                ((TEExamCourse) object).setMobilePhotoUpload(argParam.getMobilePhotoUpload());
                                ((TEExamCourse) object).setUpdateId(tbUser.getId());
                                ((TEExamCourse) object).setUpdateTime(System.currentTimeMillis());
                                saveLogs(objectDb, object, logs.value(), Objects.nonNull(tbUser) ? tbUser.getId() : 1L, ((TEExamCourse) object).getCourseCode(), ((TEExamCourse) object).getId());
                            }
                        }
                    }
                    objectDbList.add(objectDb);
                    objectList.add(object);
                }
            }
            if (!CollectionUtils.isEmpty(objectDbList) && !CollectionUtils.isEmpty(nameList) && objectDbList.size() == nameList.size()) {
                for (int i = 0; i < objectDbList.size(); i++) {
                    saveLogs(objectDbList.get(i), objectList.get(i), logs.value(), Objects.nonNull(tbUser) ? tbUser.getId() : 1L, nameList.get(i), objIdList.get(i));
                }
            }
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof BusinessException) {
                return ResultUtil.error((BusinessException) e, e.getMessage());
            } else {
                return ResultUtil.error(e.getMessage());
            }
        } finally {
            long end = System.currentTimeMillis();
            stringJoiner.add("============logs 耗时============:").add((end - start) / 1000 + "").add("秒");
            log.info("logs request:{}", stringJoiner.toString());
        }
        return joinPoint.proceed();
    }

    /**
     * 获取参数
     *
     * @param args
     * @param paramsName
     * @param stringJoiner
     * @return
     */
    protected Object getArgsData(Object[] args, String[] paramsName, StringJoiner stringJoiner) {
        List<Map<String, String>> list = null;
        Map<String, String> map = null;
        if (Objects.nonNull(args) && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (Objects.nonNull(args[i]) && (args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse || args[i] instanceof CommonsMultipartFile || args[i] instanceof MultipartFile || args[i] instanceof BeanPropertyBindingResult)) {
                    continue;
                } else {
                    stringJoiner.add("参数key:").add(JacksonUtil.parseJson(paramsName[i])).add(",参数value:").add(JacksonUtil.parseJson(args[i])).add("\r\n");
                }
                if (args[i] instanceof ArrayList) {
                    list = GsonUtil.fromJson(GsonUtil.toJson(args[i]), new TypeToken<List<Map<String, String>>>() {
                    }.getType());
                } else {
                    map = GsonUtil.fromJson(GsonUtil.toJson(args[i]), Map.class);
                }
            }
            if (!CollectionUtils.isEmpty(list)) {
                return list;
            } else {
                return Arrays.asList(map);
            }
        }
        return null;
    }

    /**
     * 保存日志
     *
     * @param o1
     * @param o2
     * @param logEnum
     * @param userId
     * @param name
     * @param objId
     */
    protected void saveLogs(Object o1, Object o2, LogEnum logEnum, Long userId, String name, Long objId) {
        if (Objects.nonNull(o1) && !o1.equals(o2)) {//不一样则日志记录
            tsLogService.save(new TSLog(logEnum, name,
                    JacksonUtil.parseJson(o1),
                    JacksonUtil.parseJson(o2),
                    userId, objId));
        }
    }
}