package com.citygis.web.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.citygis.common.core.domain.AjaxResult;
import com.citygis.web.domain.Kafka;
import com.citygis.web.mapper.KafkaMonitorMapper;
import com.citygis.web.service.KafkaConsumerServer;
import com.citygis.web.utils.KafkaUtil;
import org.apache.kafka.clients.admin.*;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
import org.apache.kafka.common.TopicPartition;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * @Package com.shcd.web.impl.kafka
 * @Author zyl
 * @Date 2024/5/29 0029 15:28
 * @description: kafka消费信息
 */
@Service
public class KafkaConsumerServiceImpl implements KafkaConsumerServer {

    @Value("${kafka.ip}")
    private String ip;

    @Resource
    private AdminClient adminClient;

    @Resource
    KafkaMonitorMapper kafkaMonitorMapper;

    @Resource
    KafkaUtil kafkaUtil;

    public KafkaConsumer<String, String> getConsumer(Collection<String> topics, String group, String offset) {
        Properties props = this.getCommonProperties();
        props.setProperty("group.id", group);
        props.setProperty("auto.offset.reset", offset);
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(topics);
        return consumer;
    }

    public Properties getCommonProperties() {
        Properties prop = new Properties();
        prop.setProperty(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, ip);
        prop.setProperty(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, "2000");
        prop.setProperty(AdminClientConfig.DEFAULT_API_TIMEOUT_MS_CONFIG, "2000");
        prop.setProperty("enable.auto.commit", "true");
        prop.setProperty("auto.commit.interval.ms", "1000");
        prop.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        prop.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        return prop;
    }

    @Override
    public AjaxResult getAllGroupId() throws ExecutionException, InterruptedException {
            List<JSONObject> groupId = this.getGroupId();
            // 返回结果
            return AjaxResult.success("groupId");
    }

    public List<JSONObject> getGroupId() throws ExecutionException, InterruptedException {
        Collection<ConsumerGroupListing> consumerGroups = adminClient.listConsumerGroups().all().get();
        List<JSONObject> result = new ArrayList<>();
        for (ConsumerGroupListing consumerGroup : consumerGroups) {
            String groupId = consumerGroup.groupId();

            // 获取消费组的分区信息
            ListConsumerGroupOffsetsResult listConsumerGroupOffsetsResult = adminClient.listConsumerGroupOffsets(groupId);
            Map<TopicPartition, OffsetAndMetadata> topicPartitionOffsetAndMetadataMap = listConsumerGroupOffsetsResult.partitionsToOffsetAndMetadata().get();

            // 计算分区数量
            int partitionCount = topicPartitionOffsetAndMetadataMap.size();

            // 获取消费组消费的主题数量
            Set<String> topics = new HashSet<>();
            for (TopicPartition topicPartition : topicPartitionOffsetAndMetadataMap.keySet()) {
                topics.add(topicPartition.topic());
            }
            int topicCount = topics.size();

            // 创建 JSON 对象表示消费者组及其分区和主题数量
            JSONObject groupInfo = new JSONObject();
            groupInfo.put("groupId", groupId);
            groupInfo.put("partition", partitionCount);
            groupInfo.put("topic", topicCount);

            // 将此消费者组的信息添加到结果列表中
            result.add(groupInfo);
        }
        return result;
    }

    @Override
    public AjaxResult getConsumerGroupDetails(String groupIds) throws ExecutionException, InterruptedException {
            List<JSONObject> consumerMsg = this.getConsumerMsg(groupIds);
            // 返回查询结果
            return AjaxResult.success(consumerMsg);

    }

    public List<JSONObject> getConsumerMsg(String groupIds) throws ExecutionException, InterruptedException {
        // 调用 KafkaAdminClient 的 listConsumerGroupOffsets 方法，获取消费进度
        ListConsumerGroupOffsetsResult listConsumerGroupOffsetsResult = adminClient.listConsumerGroupOffsets(groupIds);

        // 遍历 topicPartitionOffsetAndMetadataMap，获取所有主题和分区
        Map<TopicPartition, OffsetAndMetadata> topicPartitionOffsetAndMetadataMap = listConsumerGroupOffsetsResult.partitionsToOffsetAndMetadata().get();
        Set<TopicPartition> topicPartitions = topicPartitionOffsetAndMetadataMap.keySet();
        Set<String> topics = topicPartitions.stream().map(t -> t.topic()).collect(Collectors.toSet());
        KafkaConsumer<String, String> consumer = getConsumer(topics, groupIds, "earliest");
        Map<TopicPartition, Long> endOffsets = consumer.endOffsets(topicPartitions);
        List<JSONObject> collect = topicPartitions.stream().map(t -> {
                    // 获取当前主题和分区的偏移量和元数据
                    OffsetAndMetadata offsetAndMetadata = topicPartitionOffsetAndMetadataMap.get(t);
                    long offset = offsetAndMetadata.offset();
                    // 创建一个 JSONObject 对象
                    JSONObject jsonObject = new JSONObject();
                    // 向 JSONObject 中添加主题、分区、偏移量、结束偏移量和 Lag 信息
                    jsonObject.put("topic", t.topic());
                    jsonObject.put("partition", t.partition());
                    jsonObject.put("offset", offset);
                    TopicPartition topicPartition = new TopicPartition(t.topic(), t.partition());
                    Long endOffset = endOffsets.get(topicPartition);
                    jsonObject.put("endOffset", endOffset);
                    jsonObject.put("lag", endOffset - offset);
                    return jsonObject;
                }).sorted(Comparator.comparing(KafkaUtil::comparingByName).thenComparing(KafkaUtil::comparingByPartition))
                .collect(Collectors.toList());
        return collect;
    }

    @Override
    public AjaxResult resetOffeSet(Kafka kafka) throws ExecutionException, InterruptedException {
            Map<TopicPartition, Long> offsets = new HashMap<>();
            TopicPartition topicPartition = new TopicPartition(kafka.getTopic(), kafka.getPartition());
            // 设置offset为0，表示最早的可用位置
            offsets.put(topicPartition, kafka.getOffset());

            Map<TopicPartition, OffsetAndMetadata> offsetMap = new HashMap<>();
            for (Map.Entry<TopicPartition, Long> entry : offsets.entrySet()) {
                offsetMap.put(entry.getKey(), new OffsetAndMetadata(entry.getValue()));
            }

            AlterConsumerGroupOffsetsOptions options = new AlterConsumerGroupOffsetsOptions();

            AlterConsumerGroupOffsetsResult alterOffsetsResult = adminClient.alterConsumerGroupOffsets(kafka.getGroupId(), offsetMap, options);
            alterOffsetsResult.all().get(); // 执行offset重置操作
            return AjaxResult.success("重置成功");
    }

    @Override
    public Map<String, Integer> getTopicAndPartition() throws Exception {
        Map<String, Integer> map = new HashMap<>();
        ListTopicsResult result = adminClient.listTopics();
        // 处理result，比如打印所有的主题和partition
        for (String s : result.names().get()) {
            List<String> topics = Collections.singletonList(s);
            DescribeTopicsResult describeResult = adminClient.describeTopics(topics);
            Map<String, TopicDescription> topicDescriptionMap = describeResult.all().get();

            for (Map.Entry<String, TopicDescription> entry : topicDescriptionMap.entrySet()) {
                String topicName = entry.getKey();
                TopicDescription topicDescription = entry.getValue();
                int numPartitions = topicDescription.partitions().size();
                System.out.println("Topic: " + topicName + ", Num Partitions: " + numPartitions);
                map.put(topicName, numPartitions);
            }
        }
        return map;
    }

    @Override
    public Map<String, Integer> getTopicAndPartitionByIp(String bootStrapServer) throws Exception {
        Properties properties = new Properties();
        properties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootStrapServer);
        AdminClient adminClient1 = AdminClient.create(properties);

        Map<String, Integer> map = new HashMap<>();
        ListTopicsResult result = adminClient1.listTopics();

        for (String s : result.names().get()) {
            List<String> topics = Collections.singletonList(s);
            DescribeTopicsResult describeResult = adminClient1.describeTopics(topics);
            Map<String, TopicDescription> topicDescriptionMap = describeResult.all().get();

            for (Map.Entry<String, TopicDescription> entry : topicDescriptionMap.entrySet()) {
                String topicName = entry.getKey();
                TopicDescription topicDescription = entry.getValue();
                int numPartitions = topicDescription.partitions().size();
                System.out.println("Topic: " + topicName + ", Num Partitions: " + numPartitions);
                map.put(topicName, numPartitions);
            }
        }
        return map;
    }
    @Override
    public void kafkaListenerConsumer(String groupId, String topic) {
        Properties commonProperties = getCommonProperties();
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(commonProperties);
        // 订阅topic
        consumer.subscribe(Arrays.asList(topic));
        try {
            while (true) {
                ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
                for (ConsumerRecord<String, String> record : records) {
                    System.out.println("Topic: " + record.topic() +
                            ", Partition: " + record.partition() +
                            ", Offset: " + record.offset() +
                            ", Key: " + record.key() +
                            ", Value: " + record.value());
                }
                consumer.commitAsync(); // 手动提交偏移量
            }
        } finally {
            consumer.close();
        }
    }

    @Override
    public AjaxResult deleteConsumerGroups(List<String> groupId) throws ExecutionException, InterruptedException {
            DeleteConsumerGroupsResult deleteConsumerGroupsResult = adminClient.deleteConsumerGroups(groupId);
            deleteConsumerGroupsResult.all().get();
            return AjaxResult.success("删除成功");

    }

    //   @Scheduled(cron = "0 */5 * * * ?")
    @Override
    public AjaxResult getAllLagMessage() {
        try {

            KafkaConsumerServiceImpl kafkaConsumerService = new KafkaConsumerServiceImpl();
            List<JSONObject> groupId = this.getGroupId();
            for (JSONObject jsonObject : groupId) {
                String stringGroupId = jsonObject.get("groupId").toString();
                List<JSONObject> consumerMsg = this.getConsumerMsg(stringGroupId);
                for (JSONObject consumerMsg1 : consumerMsg) {
                    String topic = consumerMsg1.get("topic").toString();
                    String lag = consumerMsg1.get("lag").toString();
                    String endOffset = consumerMsg1.get("endOffset").toString();
                    String uuId = UUID.randomUUID().toString();

                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
                    String date = simpleDateFormat.format(new Date());

                    kafkaMonitorMapper.addLagMsg(uuId, date, Integer.parseInt(lag), topic, Integer.parseInt(endOffset));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return AjaxResult.success();
    }

}


