package com.seer.teach.netty.im.config.rocketmq;
import com.seer.teach.common.cache.DeviceMetricsCacheService;
import com.seer.teach.common.cache.dto.DeviceMetricDTO;
import com.seer.teach.common.enums.im.ImMqTopicEnum;
import com.seer.teach.common.utils.HostIpResolver;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 抽象 RocketMQ 消费者
 * <p>
 * 提供通用的消息监听和处理逻辑
 */
@Slf4j
@Component
public abstract class AbstractRocketMQConsumer {

    @Value("${rocketmq.name-server}")
    protected String nameServer;

    @Value("${spring.profiles.active}")
    protected String consumerGroup;

    @Value("${rocketmq.consumer.thread-min:36}")
    private Integer consumeThreadMin;

    @Value("${rocketmq.consumer.thread-max:72}")
    private Integer consumeThreadMax;

    @Value("${rocketmq.consumer.batch-size:64}")
    private Integer consumeMessageBatchMaxSize;

    @Value("${rocketmq.consumer.consume-timeout:30}")
    private Integer consumeTimeout;

    @Value("${rocketmq.consumer.pull-batch-size:128}")
    private Integer pullBatchSize;

    @Value("${rocketmq.consumer.pull-interval:0}")
    private Integer pullInterval;

    private ExecutorService messageExecutor;

    @Autowired
    private DeviceMetricsCacheService deviceMetricsCacheService;

    private DefaultMQPushConsumer consumer;

    /**
     * 子类提供主题信息
     */
    protected abstract ImMqTopicEnum getTopic();

    /**
     * 子类处理具体消息逻辑
     */
    protected abstract void doHandleMessage(String tag, String device, String body, MessageExt message);

    /**
     * 启动消费者，设置广播模式
     */
    @PostConstruct
    public void startConsumer() throws Exception {
        messageExecutor = new ThreadPoolExecutor(
                // 核心线程数：
                consumeThreadMin,
                // 最大线程数：
                consumeThreadMax,
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(2000),
                new ThreadFactory() {
                    private final AtomicInteger threadIndex = new AtomicInteger(0);

                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r, "RocketMQConsumer_" + getTopic().getTopic() + "_" + this.threadIndex.incrementAndGet());
                        thread.setDaemon(false);
                        return thread;
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        consumer = new DefaultMQPushConsumer(getTopic().getTopic() + "-" + consumerGroup);
        consumer.setNamesrvAddr(nameServer);
        consumer.setInstanceName(UUID.randomUUID().toString());
        consumer.setMessageModel(MessageModel.BROADCASTING);

        consumer.setConsumeThreadMin(consumeThreadMin);
        consumer.setConsumeThreadMax(consumeThreadMax);
        consumer.setConsumeMessageBatchMaxSize(consumeMessageBatchMaxSize);
        consumer.setConsumeTimeout(consumeTimeout);
        consumer.setPullBatchSize(pullBatchSize);
        consumer.setPullInterval(pullInterval);
        consumer.setPullThresholdForQueue(2000);
        consumer.setPullThresholdSizeForQueue(200);

        consumer.subscribe(getTopic().getTopic() + "-" + consumerGroup, "*");
        consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
            CountDownLatch latch = new CountDownLatch(msgs.size());
            AtomicInteger errorCount = new AtomicInteger(0);

            for (MessageExt msg : msgs) {
                messageExecutor.execute(() -> {
                    try {
                        processMessage(msg);
                    } catch (Exception e) {
                        log.error("消息处理异常，msgId={}", msg.getMsgId(), e);
                        errorCount.incrementAndGet();
                    } finally {
                        latch.countDown();
                    }
                });
            }
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        });


        consumer.start();
        log.info("{} 启动成功，监听 topic={}", this.getClass().getSimpleName(), getTopic().getTopic());
    }

    private void processMessage(MessageExt msg) {
        String tag = msg.getTags();
        String device = msg.getKeys();
        // 读取设备对应的 Netty 宿主机 IP
        DeviceMetricDTO latestMetric = deviceMetricsCacheService.getLatestMetric(device);
        if (latestMetric == null) {
            log.error("Redis 未找到设备对应IP，device={}", device);
            return;
        }
        String ip = latestMetric.getNettyIp();
        // 判断是否为本机处理的消息
        if (!isLocalHost(ip)) {
            log.trace("非本机Channel，msgId={}", msg.getMsgId());
            return;
        }
        String body = new String(msg.getBody(), StandardCharsets.UTF_8);
        // 交由子类处理
        doHandleMessage(tag, device, body, msg);
    }

    /**
     * 判断消息是否应由本机处理
     */
    private boolean isLocalHost(String ip) {
        try {
            return HostIpResolver.getHostIp().equals(ip);
        } catch (Exception e) {
            log.error("获取本机 IP 出错", e);
            return false;
        }
    }

    @PreDestroy
    private void destroy() {
        // 关闭线程池
        if (messageExecutor != null) {
            messageExecutor.shutdown();
            try {
                if (!messageExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
                    List<Runnable> droppedTasks = messageExecutor.shutdownNow();
                    log.warn("RocketMQ消费者线程池关闭超时，丢弃了{}个未完成任务", droppedTasks.size());
                }
            } catch (InterruptedException e) {
                messageExecutor.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }

        if (consumer != null) {
            log.info("{} 销毁", this.getClass().getSimpleName());
            consumer.shutdown();
        }
    }

    protected void shutdownConsumer() {
        if (consumer != null) {
            consumer.shutdown();
        }
    }
}
