package com.czg.log.aspect;


import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.czg.log.annotation.OperationLog;
import com.czg.log.enums.LogTypeEnum;
import com.czg.log.enums.OperationStatusEnum;
import com.czg.log.producer.LogProducer;
import com.czg.sa.StpKit;
import com.czg.service.RedisService;
import com.czg.utils.AddressUtil;
import com.czg.utils.HttpContextUtil;
import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator;
import com.mybatisflex.core.row.Row;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
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.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 操作日志，切面处理类
 *
 * @author admin admin@cashier.com
 * @since 1.0.0
 */
@Aspect
@Component
public class OperationLogAspect {

    @Resource
    private LogProducer logProducer;

    @Resource
    private RedisService redisService;

    @Pointcut("@annotation(com.czg.log.annotation.OperationLog)")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        try {
            //执行方法
            Object result = point.proceed();

            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            //保存日志
            saveLog(point, time, OperationStatusEnum.SUCCESS.value());

            return result;
        } catch (Exception e) {
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            String errorInfo = ExceptionUtil.stacktraceToString(e);
            //保存日志
            saveLog(point, time, OperationStatusEnum.FAIL.value(), errorInfo);

            throw e;
        }
    }

    private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status) {
        saveLog(joinPoint, time, status, null);
    }


    private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status, String errorInfo) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        Row log = new Row();
        OperationLog annotation = method.getAnnotation(OperationLog.class);
        if (annotation != null) {
            //注解上的描述
            log.set("operation", annotation.value());
        }

        //登录用户信息
        Long shopId = StpKit.USER.getShopId(0L);
        Long createUserId = StpKit.USER.getLoginIdAsLong();
        String createUserName = StpKit.USER.getAccount();

        log.set("createUserId", createUserId);
        log.set("createUserName", createUserName);
        log.set("shopId", shopId);

        log.set("type", LogTypeEnum.INFO.value());
        if (StrUtil.isNotBlank(errorInfo)) {
            log.set("type", LogTypeEnum.ERROR.value());
        }
        log.set("status", status);
        log.set("requestTime", (int) time);
        log.set("createTime", LocalDateTime.now());

        //请求相关信息
        HttpServletRequest request = HttpContextUtil.getHttpServletRequest();
        String requestMethod = request.getMethod();
        if ("GET".equalsIgnoreCase(requestMethod)) {
            return;
        }
        String ip = JakartaServletUtil.getClientIP(request);
        log.set("ip", ip);
        Object location = redisService.get("IP::" + ip);
        if (ObjUtil.isNotNull(location)) {
            log.set("location", Convert.toStr(location));
        } else {
            String address = AddressUtil.getRealAddressByIp(ip);
            redisService.set("IP::" + ip, address, 60 * 60 * 24);
            log.set("location", address);
        }
        log.set("userAgent", request.getHeader(HttpHeaders.USER_AGENT));
        log.set("requestUri", request.getRequestURI());
        log.set("requestMethod", request.getMethod());
        log.set("errorInfo", errorInfo);
        //请求参数
        Object[] args = joinPoint.getArgs();
        Map<String, String> param = JakartaServletUtil.getParamMap(request);
        try {
            String params = JSON.toJSONString(args[0], JSONWriter.Feature.WriteMapNullValue);
            log.set("requestParams", params);
            boolean isJsonObject = JSONUtil.isTypeJSONObject(params);
            if (isJsonObject) {
                JSONObject reqData = JSON.parseObject(params);
                param.forEach(reqData::putIfAbsent);
                log.set("requestParams", reqData.toJSONString());
            }
        } catch (Exception ignored) {

        }
        log.set("id", new SnowFlakeIDKeyGenerator().nextId());
        //保存到Redis队列里
        logProducer.saveLog(JSON.toJSONString(log.toUnderlineKeysMap(), JSONWriter.Feature.WriteMapNullValue));
    }
}
