/* (C) 2023 YiRing, Inc. */
package com.yiring.common.aspect;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.yiring.common.annotation.UptimePush;
import com.yiring.common.core.Redis;
import com.yiring.common.core.UptimeNotice;
import com.yiring.common.exception.UptimeException;
import com.yiring.common.utils.Uptime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * Uptime Push 监控项切面
 *
 * @author Jim
 * @version 0.1
 * 2023/12/19 15:17
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class UptimePushAspect {

    final Redis redis;

    @Around("@annotation(annotation)")
    public Object around(ProceedingJoinPoint point, UptimePush annotation) throws Throwable {
        // 获取 Push 监控项的 key 唯一标识
        String key = annotation.key();
        String redisKey = UptimePush.CACHE_PREFIX + key;
        int retryCount = annotation.retryCount();
        // 获取当前的重试次数
        Integer currentRetryCount = redis.get(redisKey, Integer.class);
        // 默认状态 UP
        Uptime.Status status = Uptime.Status.UP;

        Object result = null;
        String err = null;
        long start = System.currentTimeMillis();
        try {
            result = point.proceed();

            if (Objects.nonNull(result)) {
                // 针对有返回值的 Job 判断是否为 UptimeNotice 类实例
                if (result instanceof UptimeNotice value) {
                    result = value.getMsg();
                    status = value.getStatus();
                } else if ("com.xxl.job.core.biz.model.ReturnT".equals(result.getClass().getName())) {
                    // 针对 XxlJobUtil.success 和 XxlJobUtil.fail 返回值的 Job 判断是否为 UptimeNotice 类实例
                    Object content = ReflectUtil.getFieldValue(result, "content");
                    if (content instanceof UptimeNotice value) {
                        result = value.getMsg();
                        status = value.getStatus();
                    } else {
                        result = ReflectUtil.getFieldValue(result, "msg");
                    }
                }
            }

            // 开始重复计数
            redis.del(redisKey);
        } catch (Exception e) {
            err = e.getMessage();

            // 非指定 UptimeException 异常情况下才抛出
            if (!(e instanceof UptimeException) || retryCount <= 0) {
                throw e;
            }
        } finally {
            // 构建消息内容集合
            List<String> texts = new ArrayList<>();
            if (StrUtil.isNotBlank(annotation.group())) {
                texts.add("【" + annotation.group() + "】");
            }
            if (StrUtil.isNotBlank(annotation.name())) {
                texts.add(
                    annotation.name() +
                    (
                        retryCount > 0
                            ? "(" + Optional.ofNullable(currentRetryCount).orElse(0) + "/" + retryCount + ")"
                            : ""
                    ) +
                    "，"
                );
            }

            // 判断是否有异常消息
            if (StrUtil.isNotBlank(err)) {
                // 有异常时默认状态为 DOWN
                status = Uptime.Status.DOWN;

                if (retryCount > 0) {
                    // 判断重试次数是否符合条件，设置重试时的上报状态
                    if (Objects.isNull(currentRetryCount) || currentRetryCount < retryCount) {
                        status = annotation.retryStatus();

                        // 重试次数递增 1
                        Long count = redis.incr(redisKey, 1);

                        // 追加重试次数
                        texts.add(StrUtil.format("已启用重试模式，预计最多重试 {} 次 \n\n", retryCount));
                        log.info(
                            "[Uptime Push] key: {}, group: {}, name: {}, retry count: {}",
                            key,
                            annotation.group(),
                            annotation.name(),
                            count
                        );
                    } else {
                        if (annotation.retryLoop()) {
                            // 开始重复计数
                            redis.del(redisKey);
                        } else {
                            // 追加重试次数
                            redis.incr(redisKey, 1);
                            // 追加重试衰退提示
                            texts.add(
                                StrUtil.format(
                                    "重试超过 {} 次仍然未恢复，由于未启用循环重试模式，衰退至默认机制，请及时检查并处理 \n\n",
                                    retryCount
                                )
                            );
                        }
                    }
                }

                // 追加异常消息
                texts.add(err);
            } else {
                if (result instanceof String text) {
                    texts.add(text);
                } else {
                    texts.add("OK");
                }
            }

            // 构建上报的消息内容
            String msg = StrUtil.join(" ", texts);
            long ping = System.currentTimeMillis() - start;
            // 上报监控结果
            Uptime.notice(key, status, msg, ping);
        }

        return result;
    }
}
