package com.fs.framework.aspectj;

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fs.app.annotation.UserOperationLog;
import com.fs.app.utils.JwtUtils;
import com.fs.common.core.domain.R;
import com.fs.common.param.LoginMaWxParam;
import com.fs.common.utils.DateUtils;
import com.fs.common.utils.ServletUtils;
import com.fs.common.utils.StringUtils;
import com.fs.course.domain.FsCourseQuestionBank;
import com.fs.course.mapper.FsUserCourseVideoMapper;
import com.fs.course.param.FsCourseQuestionAnswerUParam;
import com.fs.course.param.FsCourseSendRewardUParam;
import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
import com.fs.course.vo.FsUserCourseVO;
import com.fs.course.vo.FsUserCourseVideoH5DVO;
import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
import com.fs.his.domain.FsUser;
import com.fs.his.domain.FsUserOperationLog;
import com.fs.his.enums.FsUserOperationEnum;
import com.fs.his.mapper.FsUserMapper;
import com.fs.his.mapper.FsUserOperationLogMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class UserOperationLogAspect {

    @Autowired
    private FsUserOperationLogMapper logMapper;
    @Autowired
    private FsUserMapper userMapper;
    @Autowired
    private FsUserCourseVideoMapper userCourseVideoMapper;
    @Autowired
    JwtUtils jwtUtils;

    private final ObjectMapper objectMapper = new ObjectMapper();
    private static final ThreadLocal<FsUserOperationLog> LOG_HOLDER = new ThreadLocal<>();

    @Pointcut("@annotation(com.fs.app.annotation.UserOperationLog)")
    public void logPointcut() {}


    @AfterReturning(pointcut = "logPointcut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        handleLog(joinPoint, null);
    }

    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e);
    }

    @AfterReturning(pointcut = "@annotation(userOpLog)", returning = "result")
    public void afterReturning(JoinPoint joinPoint,UserOperationLog userOpLog, Object result) {
        FsUserOperationLog operationLog = LOG_HOLDER.get();
        if (operationLog == null || StringUtils.isBlank(operationLog.getOperationType())) return;

        try {
            if (operationLog.getUserId() == null){
                FsUser fsUser = extractUserFromResult(result);
                if (fsUser != null && operationLog.getUserId() == null) {
                    operationLog.setUserId(fsUser.getUserId());
                    StringBuilder details = getDetail(
                            userOpLog,
                            FsUserOperationEnum.toType(operationLog.getOperationType()),
                            fsUser,
                            joinPoint.getArgs());
                    operationLog.setDetails(details.toString());
                }
            }


            if (operationLog.getUserId() != null) {
                if ("学习课程".equals(operationLog.getOperationType())){
                    if (StringUtils.isBlank(operationLog.getDetails())){
                        operationLog = extractCourseFromResult(result);

                    }
                }
                logMapper.insertFsUserOperationLog(operationLog);
            }
        } catch (Exception ex) {
            log.error("操作日志插入异常", ex);
        } finally {
            LOG_HOLDER.remove();
        }
    }

    private void handleLog(JoinPoint joinPoint, Exception e) {
        FsUserOperationLog operationLog = new FsUserOperationLog();
        LOG_HOLDER.set(operationLog);
        try {
            //时间
            operationLog.setCreateTime(DateUtils.getNowDate());
            //操作类型
            Method method = getMethod(joinPoint);
            UserOperationLog annotation = method.getAnnotation(UserOperationLog.class);
            if (annotation == null) return;
            operationLog.setOperationType(annotation.operationType().getLabel());

            //用户
            Long userId =null;
            try {
                String appToken = ServletUtils.getRequest().getHeader("APPToken");
                if (StringUtils.isNotBlank(appToken)){
                    userId = Long.valueOf(jwtUtils.getClaimByToken(appToken).getSubject().toString());
                }
            } catch (Exception ie){
                log.info("获取用户id失败");
            }
            if (userId == null) {
                LOG_HOLDER.set(operationLog);
                return;
            }

            FsUser fsUser = userMapper.selectFsUserByUserId(userId);
            if (fsUser == null) {
                LOG_HOLDER.set(operationLog);
                return;
            }
            operationLog.setUserId(userId);


            StringBuilder details = getDetail(annotation, annotation.operationType(), fsUser,joinPoint.getArgs());
            operationLog.setDetails(details.toString());

            if (e != null) {
                details.append("，异常: ").append(e.getMessage());
            }


            LOG_HOLDER.set(operationLog);
        } catch (Exception ex) {
            log.error("记录操作日志异常", ex);
        }
    }

    private StringBuilder getDetail(UserOperationLog annotation, FsUserOperationEnum opType, FsUser fsUser,Object[] args) {
        StringBuilder details = new StringBuilder();
        String nickName = fsUser.getNickName();
        if (StringUtils.isBlank(nickName)){
            nickName = fsUser.getNickname();
        }
        if (annotation.detail() == null || annotation.detail().isEmpty()) {
            switch (opType.getValue()) {
                case 1: // 小程序登录
                    details.append(nickName)
                            .append("在")
                            .append(DateUtils.getTime())
                            .append("登录了小程序");
                    break;
                case 2: // h5登录
                    details.append(nickName)
                            .append("在")
                            .append(DateUtils.getTime())
                            .append("登录了h5");
                    break;
                case 3: // 成为会员
                    details.append(nickName)
                            .append("在")
                            .append(DateUtils.getTime())
                            .append("注册成为会员");
                    break;
                case 4: // 判断是否成为会员
                    break;
                case 5: // 学习课程
                    String courseInfo = extractCourseInfo(args);
                    if (courseInfo != null) {
                        details.append(nickName)
                                .append("在")
                                .append(DateUtils.getTime())
                                .append("观看 ").append(courseInfo);
                    }
                    break;
                case 6: // 答题
                    String answerCourse = answerCourse(args);
                    details.append(nickName)
                            .append("在 ")
                            .append(DateUtils.getTime())
                            .append("\n")
                            .append(answerCourse);
                    break;
                case 7: // 发送奖励
                    String sendReward = sendReward(args);

                    details.append(nickName)
                            .append("在 ")
                            .append(DateUtils.getTime())
                            .append("领取 红包/积分 奖励")
                            .append("\n")
                            .append(sendReward);
                    break;
                default:
                    details.append(opType.getLabel());
                    break;
            }
        } else {
            details.append(annotation.detail());
        }
        return details;
    }

    private Method getMethod(JoinPoint joinPoint) throws NoSuchMethodException {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        return joinPoint.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
    }


    private FsUser extractUserFromResult(Object result) {
        if (result instanceof R) {
            R r = (R) result;
            Object user = r.get("user");
            if (user instanceof FsUser) {
                return (FsUser) user;
            }
        }
        return null;
    }
    private FsUserOperationLog extractCourseFromResult(Object result) {
        if (result instanceof R) {
            R r = (R) result;
            Object course = r.get("course");
            if (course instanceof FsUserCourseVideoH5DVO) {
                FsUserOperationLog operationLog = LOG_HOLDER.get();
                FsUser fsUser = userMapper.selectFsUserByUserId(operationLog.getUserId());
                FsUserCourseVideoH5DVO vo = (FsUserCourseVideoH5DVO) course;
                operationLog.setParam(JSON.toJSONString(vo));
                StringBuilder details = new StringBuilder();
                String nickName = fsUser.getNickName();
                if (StringUtils.isBlank(nickName)){
                    nickName = fsUser.getNickname();
                }
                details.append(nickName)
                        .append("在")
                        .append(DateUtils.getTime())
                        .append("观看 课程:").append(vo.getCourseName()).append(" 的 ").append(vo.getTitle()).append(" 小节");
                operationLog.setDetails(details.toString());
                return operationLog;
            }
        }
        return null;
    }
    private String extractCourseInfo(Object[] args) {
        if (args == null) return "未知课程";

        for (Object arg : args) {
            if (arg instanceof FsUserCourseVideoLinkParam) {
                FsUserCourseVideoLinkParam param = (FsUserCourseVideoLinkParam) arg;
                if (param.getVideoId() != null && param.getPeriodId() != null) {
                    FsUserCourseVO vo = userCourseVideoMapper.selectFsUserCourseVideoVoByVideoId(param.getVideoId(), param.getPeriodId());
                    if (vo != null){
                        FsUserOperationLog operationLog = LOG_HOLDER.get();
                        operationLog.setParam(JSON.toJSONString(vo));
                        LOG_HOLDER.set(operationLog);
                        return "课程:"+ vo.getCourseName() + " 的 " + vo.getTitle() + " 小节";
                    }
                }
            } else {
                return null; //自动发课 接口执行完生成
            }
        }
        return "未知课程";
    }

    private String answerCourse(Object[] args) {
        if (args == null) return "未知课程";
        for (Object arg : args) {
            if (arg instanceof FsCourseQuestionAnswerUParam) {
                FsCourseQuestionAnswerUParam param = (FsCourseQuestionAnswerUParam) arg;
                Long videoId = param.getVideoId();
                Long periodId = param.getPeriodId();
                Long courseId = param.getCourseId();
                FsUserCourseVO vo = null;
                if (param.getQwExternalId() != null){ //自动看课
                    vo = userCourseVideoMapper.selectFsUserCourseVideoVoByVideoIdAndCourdeId(videoId,courseId);
                } else if (videoId != null && param.getPeriodId() != null) {  //手动看课
                    vo = userCourseVideoMapper.selectFsUserCourseVideoVoByVideoId(videoId, periodId);
                }
                if (vo != null){
                    FsUserOperationLog operationLog = LOG_HOLDER.get();
                    operationLog.setParam(JSON.toJSONString(vo));
                    LOG_HOLDER.set(operationLog);
                    StringBuilder details =new StringBuilder("课程:"+ vo.getCourseName() + " 的 " + vo.getTitle() + " 小节 问题为:");
                    List<FsCourseQuestionBank> questions = param.getQuestions();
                    if (questions != null && !questions.isEmpty()) {
                        for (int i = 0; i < questions.size(); i++) {
                            details.append("\n").append(i+1).append(".").append(questions.get(i).getTitle());
                            details.append(" 提交答案为:").append(questions.get(i).getAnswer());
                        }
                    }
                    return details.toString();
                }
            }
        }
        return "未知课程";
    }

    private String sendReward(Object[] args) {
        if (args == null) return "未知课程";
        for (Object arg : args) {
            if (arg instanceof FsCourseSendRewardUParam) {
                FsCourseSendRewardUParam param = (FsCourseSendRewardUParam) arg;
                Long videoId = param.getVideoId();
                Long periodId = param.getPeriodId();
                Long courseId = param.getCourseId();
                FsUserCourseVO vo = null;
                if (param.getQwExternalId() != null){ //自动看课
                    vo = userCourseVideoMapper.selectFsUserCourseVideoVoByVideoIdAndCourdeId(videoId,courseId);
                } else if (videoId != null && param.getPeriodId() != null) {  //手动看课
                    vo = userCourseVideoMapper.selectFsUserCourseVideoVoByVideoId(videoId, periodId);
                }
                if (vo != null){
                    FsUserOperationLog operationLog = LOG_HOLDER.get();
                    operationLog.setParam(JSON.toJSONString(vo));
                    LOG_HOLDER.set(operationLog);
                    StringBuilder details =new StringBuilder();
                    details.append("课程:"+ vo.getCourseName() + " 的 " + vo.getTitle() + " 小节");
                    return details.toString();
                }
            }
        }
        return "未知课程";
    }

}
