package com.inspect.analysis.service.impl;

import com.alibaba.nacos.shaded.com.google.gson.Gson;
import com.inspect.analysis.constant.AnalyseConstants;
import com.inspect.base.redis.service.RedisService;
import com.inspect.partrolresult.domain.AnalyseRequest;
import com.inspect.partrolresult.service.IAnalyseRequestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@Component
public class AlgorithmRequestRetryableConsumerManager {

    @Value("${server.port}")
    private String port;

    @Value("${task.redis.request.permits-per-second:0.5}")
    private double permitsPerSecond;

    @Value("${task.redis.request.idle-sleep-ms:500}")
    private long idleSleepMs;

    @Value("${task.test-mode:false}")
    private boolean testMode;

    private RedisQueueRetryableConsumerAsync<AnalyseRequest> consumer;

    @Resource
    private RedisService redisService;

    @Resource
    private IAnalyseRequestService analyseRequestService;

    private final ExecutorService executor = Executors.newFixedThreadPool(10);

    @PostConstruct
    public void initConsumer() {
        consumer = new RedisQueueRetryableConsumerAsync<AnalyseRequest>(
                redisService.redisTemplate,
                AnalyseConstants.ALGORITHM_REQUEST_QUEUE,
                AnalyseRequest.class,
                permitsPerSecond,
                idleSleepMs,
                request -> executor.submit(() -> processRequest(request)) // 交给线程池处理
        );

        consumer.start(); // 应用启动时即开始消费
    }

    private void processRequest(AnalyseRequest request) {
        try {
            log.info("ALGORITHM_REQUEST_RETRYABLE_CONSUME_MANAGE queueSize: {}, request: {}", getQueueSize(), request);
            analyseRequestService.sendRequest(request, request.getTypeList(), request.isFilter());
        } catch (IOException e) {
            // 可以记录失败次数，避免无限重试
            int retryCount = Optional.of(request.getRetryCount()).orElse(0);
            log.info("ALGORITHM_REQUEST_RETRYABLE_CONSUME_MANAGE failed, retryCount: {}, reqeust: {}, msg: {}", request.getRetryCount(), request, e.getMessage());
            final int TRIES_MAX = testMode ? 50 : 3;
            if (retryCount < TRIES_MAX) {
                request.setRetryCount(retryCount + 1);
                final String filterRedisKey = AnalyseConstants.ANALYSE_FILTER_REQUEST.concat(request.getRequestId());
                if(Boolean.FALSE.equals(redisService.redisTemplate.hasKey(filterRedisKey))) {
                    log.info("ALGORITHM_REQUEST_RETRYABLE_CONSUME_MANAGE NO_KEY_IN_REDIS, filterRedisKey: {}, retryCount: {}", filterRedisKey, retryCount);
                    redisService.redisTemplate.opsForList().leftPush(AnalyseConstants.ALGORITHM_REQUEST_QUEUE, new Gson().toJson(request));
                }
            } else {
                log.info("ALGORITHM_REQUEST_RETRYABLE_CONSUME_MANAGE compensate, retryCount: {},  request: {}", retryCount, request);
                analyseRequestService.sendCompensateRequest(request);
            }
        }
    }

    @PreDestroy
    public void stopConsumer() {
        if (consumer != null) {
            consumer.stop();
        }
    }

    public long getQueueSize() {
        return redisService.redisTemplate.opsForList().size(AnalyseConstants.ALGORITHM_REQUEST_QUEUE);
    }

    @Scheduled(fixedDelay = 5000)
    public void logThreadPoolStatus() {
        ThreadPoolExecutor executor = consumer.getExecutor();
        log.debug("AlgorithmRequestRetryableConsumerManager ThreadPool - active: {}, queue: {}, completed: {}",
                executor.getActiveCount(),
                executor.getQueue().size(),
                executor.getCompletedTaskCount());
    }
}

