package com.cf.imes.framework.errorcode.core.generator;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.cf.imes.framework.common.exception.ErrorCode;
import com.cf.imes.module.system.api.errorcode.ErrorCodeApi;
import com.cf.imes.module.system.api.errorcode.dto.ErrorCodeAutoGenerateReqDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * ErrorCodeAutoGenerator 的实现类
 * 目的是，扫描指定的 {@link #constantsClassList} 类，写入到 system 服务中
 *
 * @author dylan
 */
@RequiredArgsConstructor
@Slf4j
public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {

    /**
     * 应用分组
     */
    private final String applicationName;
    /**
     * 错误码枚举类
     */
    private final List<String> constantsClassList;
    /**
     * 错误码 Api
     */
    private final ErrorCodeApi errorCodeApi;

    @Override
    @EventListener(ApplicationReadyEvent.class)
    @Async // 异步，保证项目的启动过程，毕竟非关键流程
    public void execute() {
        List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = parseErrorCode();
        log.info("[execute][解析到错误码数量为 ({}) 个]", autoGenerateDTOs.size());

        // 手动重试配置
        int maxAttempts = 5;
        int attempt = 0;
        boolean success = false;
        long initialDelay = 1000; // 初始延迟1秒
        long maxDelay = 10000; // 最大延迟10秒

        while (attempt < maxAttempts && !success) {
            attempt++;
            try {
                errorCodeApi.autoGenerateErrorCodeList(autoGenerateDTOs).checkError();
                log.info("[execute][第 {} 次尝试 - 写入到 system 组件成功]", attempt);
                success = true;
            } catch (Exception ex) {
                // 特别处理UnknownHostException
                if (ExceptionUtil.getRootCause(ex) instanceof UnknownHostException) {
                    log.warn("[execute][第 {} 次尝试 - DNS解析失败，等待重试]", attempt);
                } else {
                    log.warn("[execute][第 {} 次尝试 - 请求失败({})]",
                            attempt, ExceptionUtil.getRootCauseMessage(ex));
                }

                if (attempt >= maxAttempts) {
                    log.error("[execute][写入到 system 组件失败，已达到最大重试次数({})]",
                            ExceptionUtil.getRootCauseMessage(ex));
                    break;
                }

                // 每次重试间隔时间指数增长：1s, 2s, 4s, 8s, 10s（最大10s）
                long delay = Math.min(
                        (long) (initialDelay * Math.pow(2, attempt - 1)),
                        maxDelay
                );

                try {
                    log.info("[execute][等待 {}ms 后进行第 {} 次重试]", delay, attempt + 1);
                    Thread.sleep(delay);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    log.error("[execute][重试过程被中断]");
                    break;
                }
            }
        }
    }

    /**
     * 解析 constantsClassList 变量，转换成错误码数组
     *
     * @return 错误码数组
     */
    private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode() {
        // 校验 errorCodeConstantsClass 参数
        if (CollUtil.isEmpty(constantsClassList)) {
            log.info("[execute][未配置 chenfeng.error-code.constants-class-list 配置项，不进行自动写入到 system 服务中]");
            return new ArrayList<>();
        }

        // 解析错误码
        List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>();
        constantsClassList.forEach(constantsClass -> {
            try {
                // 解析错误码枚举类
                Class<?> errorCodeConstantsClazz = ClassUtil.loadClass(constantsClass);
                // 解析错误码
                autoGenerateDTOs.addAll(parseErrorCode(errorCodeConstantsClazz));
            } catch (Exception ex) {
                log.warn("[parseErrorCode][constantsClass({}) 加载失败({})]", constantsClass,
                        ExceptionUtil.getRootCauseMessage(ex));
            }
        });
        return autoGenerateDTOs;
    }

    /**
     * 解析错误码类，获得错误码数组
     *
     * @return 错误码数组
     */
    private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode(Class<?> constantsClass) {
        List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>();
        Arrays.stream(constantsClass.getFields()).forEach(field -> {
            if (field.getType() != ErrorCode.class) {
                return;
            }
            // 转换成 ErrorCodeAutoGenerateReqDTO 对象
            ErrorCode errorCode = (ErrorCode) ReflectUtil.getFieldValue(constantsClass, field);
            autoGenerateDTOs.add(new ErrorCodeAutoGenerateReqDTO().setApplicationName(applicationName)
                    .setCode(errorCode.getCode()).setMessage(errorCode.getMsg()));
        });
        return autoGenerateDTOs;
    }

}
