package com.yc.ship.module.trade.framework.aop;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.yc.ship.framework.security.core.util.SecurityFrameworkUtils;
import com.yc.ship.framework.web.core.util.WebFrameworkUtils;
import com.yc.ship.module.trade.framework.annotation.TradeOrderLog;
import com.yc.ship.module.trade.service.order.TradeOrderLogService;
import com.yc.ship.module.trade.service.order.bo.TradeOrderLogCreateReqBO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.util.Map;

import static com.yc.ship.framework.common.util.json.JsonUtils.toJsonString;
import static java.util.Collections.emptyMap;

/**
 * 交易订单的操作日志的记录 AOP 切面
 */
@Component
@Aspect
@Slf4j
public class TradeOrderLogAspect {

    /**
     * 用户编号
     *
     * 目前的使用场景：支付回调时，需要强制设置下用户编号
     */
    private static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
    /**
     * 用户类型
     */
    private static final ThreadLocal<Integer> USER_TYPE = new ThreadLocal<>();
    /**
     * 订单编号
     */
    private static final ThreadLocal<Long> ORDER_ID = new ThreadLocal<>();
    /**
     * 操作前的状态
     */
    private static final ThreadLocal<Integer> BEFORE_STATUS = new ThreadLocal<>();
    /**
     * 操作后的状态
     */
    private static final ThreadLocal<Integer> AFTER_STATUS = new ThreadLocal<>();
    /**
     * 拓展参数 Map，用于格式化操作内容
     */
    private static final ThreadLocal<Map<String, Object>> EXTS = new ThreadLocal<>();

    @Resource
    private TradeOrderLogService orderLogService;

    @Before("@annotation(orderLog)")
    public void doBefore(JoinPoint joinPoint, TradeOrderLog orderLog) {
        clear();
    }

    @AfterReturning("@annotation(orderLog)")
    public void doAfterReturning(JoinPoint joinPoint, TradeOrderLog orderLog) {
        saveRecord(orderLog);
    }

    @AfterThrowing(value = "@annotation(orderLog)", throwing = "e")
    public void doAfterThrowing(TradeOrderLog orderLog,Throwable e) {
        saveRecord(orderLog);
    }

    private void saveRecord(TradeOrderLog orderLog){
        try {
            // 1.1 操作用户
            Long userId = getUserId();
            // 1.2 订单信息
            Long orderId = ORDER_ID.get();
            if (orderId == null) { // 如果未设置，只有注解，说明不需要记录日志
                return;
            }
            Integer beforeStatus = BEFORE_STATUS.get();
            Integer afterStatus = AFTER_STATUS.get();
            Map<String, Object> exts = ObjectUtil.defaultIfNull(EXTS.get(), emptyMap());
            String content = StrUtil.format(orderLog.operateType().getContent(), exts);

            // 2. 记录日志
            TradeOrderLogCreateReqBO createBO = new TradeOrderLogCreateReqBO()
                    .setId(IdWorker.getId())
                    .setUserId(userId)
                    .setUserType(getUserType())
                    .setOrderId(orderId).setBeforeStatus(beforeStatus).setAfterStatus(afterStatus)
                    .setOperateType(orderLog.operateType().getType()).setContent(content);
            orderLogService.createOrderLog(createBO);
        } catch (Exception ex) {
            log.error("[doAfterReturning][orderLog({}) 订单日志错误]", toJsonString(orderLog), ex);
        } finally {
            clear();
        }
    }

    /**
     * 获得用户编号
     * @return 默认0 为系统操作
     */
    private static Long getUserId() {
        return ObjectUtil.defaultIfNull(WebFrameworkUtils.getLoginUserId(), 0L);
    }
    private static Long getTenId() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (!(requestAttributes instanceof ServletRequestAttributes)) {
            return null;
        }
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        return ObjectUtil.defaultIfNull(WebFrameworkUtils.getTenantId(servletRequestAttributes.getRequest()), 0L);
    }

    private static Integer getUserType() {
        if(SecurityFrameworkUtils.getLoginUser() !=null){
            return SecurityFrameworkUtils.getLoginUser().getUserType();
        }
        return ObjectUtil.defaultIfNull(WebFrameworkUtils.getLoginUserType(), 0);
    }



    public static void setOrderInfo(Long id, Integer beforeStatus, Integer afterStatus, Map<String, Object> exts) {
        ORDER_ID.set(id);
        BEFORE_STATUS.set(beforeStatus);
        AFTER_STATUS.set(afterStatus);
        EXTS.set(exts);
    }

    public static void setExts(Map<String, Object> exts) {
        EXTS.set(exts);
    }

    public static Map<String, Object> getExts() {
        return EXTS.get();
    }

    public static void setUserInfo(Long userId, Integer userType) {
        USER_ID.set(userId);
        USER_TYPE.set(userType);
    }

    private static void clear() {
        USER_ID.remove();
        USER_TYPE.remove();
        ORDER_ID.remove();
        BEFORE_STATUS.remove();
        AFTER_STATUS.remove();
        EXTS.remove();
    }

}
