package com.cico.nest.lcdp.boot.web.errorcode.core.loader;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import com.cico.nest.lcdp.boot.api.system.errorcode.ErrorCodeApi;
import com.cico.nest.lcdp.boot.api.system.errorcode.dto.ErrorCodeRespDTO;
import com.cico.nest.lcdp.framework.common.util.date.DateUtils;
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 org.springframework.scheduling.annotation.Scheduled;

import java.time.LocalDateTime;
import java.util.List;

/**
 * ErrorCodeLoader 的实现类，从 infra 的数据库中，加载错误码。
 *
 * 考虑到错误码会刷新，所以按照 {@link #REFRESH_ERROR_CODE_PERIOD} 频率，增量加载错误码。
 *
 * @author zws
 */
@RequiredArgsConstructor
@Slf4j
public class ErrorCodeLoaderImpl implements ErrorCodeLoader {

    /**
     * 刷新错误码的频率，单位：毫秒
     */
    private static final int REFRESH_ERROR_CODE_PERIOD = 60 * 1000;

    /**
     * 应用分组
     */
    private final String applicationName;
    /**
     * 错误码 Api
     */
    private final ErrorCodeApi errorCodeApi;

    /**
     * 缓存错误码的最大更新时间，用于后续的增量轮询，判断是否有更新
     */
    private LocalDateTime maxUpdateTime;

    @Override
    @EventListener(ApplicationReadyEvent.class)
    @Async // 异步，保证项目的启动过程，毕竟非关键流程
    public void loadErrorCodes() {
        loadErrorCodes0();
    }

    @Override
    @Scheduled(fixedDelay = REFRESH_ERROR_CODE_PERIOD, initialDelay = REFRESH_ERROR_CODE_PERIOD)
    public void refreshErrorCodes() {
        loadErrorCodes0();
    }

    private void loadErrorCodes0() {
        try {
            // 加载错误码
            List<ErrorCodeRespDTO> errorCodeRespDTOs = errorCodeApi.getErrorCodeList(applicationName, maxUpdateTime);
            if (CollUtil.isEmpty(errorCodeRespDTOs)) {
                return;
            }
            log.info("[loadErrorCodes0][加载到 ({}) 个错误码]", errorCodeRespDTOs.size());

            // 刷新错误码的缓存
            errorCodeRespDTOs.forEach(errorCodeRespDTO -> {
                // 写入到错误码的缓存
                putErrorCode(errorCodeRespDTO.getCode(), errorCodeRespDTO.getMessage());
                // 记录下更新时间，方便增量更新
                maxUpdateTime = DateUtils.max(maxUpdateTime, errorCodeRespDTO.getUpdateTime());
            });
        } catch (Exception ex) {
            log.error("[loadErrorCodes0][加载错误码失败({})]", ExceptionUtil.getRootCauseMessage(ex));
        }
    }

}
