package org.dromara.zhishu.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.dromara.zhishu.annotation.LogRecord;
import org.dromara.zhishu.domain.vo.SaveGoodsVo;
import org.dromara.zhishu.domain.vo.ShopVo;
import org.dromara.zhishu.util.EasyExcelUtil;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Aspect
@Component
public class LogRecordAspect {

    /**
     * 环绕增强方法
     *
     * @param joinPoint 连接点
     * @param logRecord 注解对象
     * @return 方法执行结果
     * @throws Throwable 异常
     */
    @Around("@annotation(logRecord)")
    public Object doLogRecord(ProceedingJoinPoint joinPoint, LogRecord logRecord) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        String[] paramNames = signature.getParameterNames();

        // 提取关键参数
        Map<String, Object> paramMap = getMethodParams(paramNames, args);

        SaveGoodsVo saveGoodsVo = extractParam(paramMap, "saveGoodsVo", SaveGoodsVo.class);
        AtomicInteger successMarkI = extractParam(paramMap, "successMarkI", AtomicInteger.class);
        AtomicInteger failMarkI = extractParam(paramMap, "failMarkI", AtomicInteger.class);
        ShopVo shopVo = extractParam(paramMap, "shopVo", ShopVo.class);
        Integer totalNum = extractParam(paramMap, "totalNum", Integer.class);
        Long time = extractParam(paramMap, "time", Long.class);
        AtomicBoolean status = extractParam(paramMap, "status", AtomicBoolean.class);

        // 执行原方法
        Object result;
        result = joinPoint.proceed();

        // 记录方法调用后的信息
        if (status.get()) {
            logAfter(successMarkI, saveGoodsVo, shopVo, totalNum, time);
        }

        if (!status.get()) {
            // 记录异常信息
            logException(failMarkI, saveGoodsVo, shopVo, totalNum);
        }

        return result;
    }

    /**
     * 获取需要记录的方法参数
     */
    private Map<String, Object> getMethodParams(String[] parameterNames, Object[] args) {
        Map<String, Object> params = new HashMap<>();

        for (int i = 0; i < parameterNames.length; i++) {
            params.put(parameterNames[i], args[i]);
        }

        return params;
    }

    /**
     * 安全提取参数并做类型检查
     */
    @SuppressWarnings("unchecked")
    private <T> T extractParam(Map<String, Object> paramMap, String key, Class<T> type) {
        Object value = paramMap.get(key);
        if (value != null && type.isAssignableFrom(value.getClass())) {
            return (T) value;
        }
        throw new IllegalArgumentException("参数缺失或类型错误: " + key + ", 类型应为: " + type.getName());
    }

    /**
     * 方法执行后的日志记录
     */
    private void logAfter(AtomicInteger successMarkI, SaveGoodsVo saveGoodsVo, ShopVo shopVo, Integer totalNum, long newTime) {
        // 更新计数器条数
        Integer successExecutedNum = successMarkI.incrementAndGet();
        EasyExcelUtil.writeTxtMsgLog(saveGoodsVo.getTaskId(), totalNum, successExecutedNum);
        EasyExcelUtil.writeTxtLog(saveGoodsVo.getTaskId(), shopVo.getId(), shopVo.getShopName(), totalNum, successExecutedNum);
        EasyExcelUtil.writeDetailTxtLog(saveGoodsVo.getTaskId(), shopVo.getId(), shopVo.getShopName(), totalNum, successExecutedNum, "同步成功", "New_" + shopVo.getId() + "_" + newTime + "_0");
    }

    /**
     * 异常时的日志记录
     */
    private void logException(AtomicInteger failMarkI, SaveGoodsVo saveGoodsVo, ShopVo shopVo, Integer totalNum) {
        // 更新计数器条数
        Integer failExecutedNum = failMarkI.incrementAndGet();
        EasyExcelUtil.writeDetailTxtLog(saveGoodsVo.getTaskId(), shopVo.getId(), shopVo.getShopName(), totalNum, failExecutedNum, "修改货号失败", "New_" + shopVo.getId() + "_0");
    }
}