package com.mortals.xhx.queue.rabbitmq;

import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gson.Gson;
import com.mortals.xhx.queue.*;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

/**
 * rabbmit 消息生产模板
 *
 * @author: zxfei
 * @date: 2022/4/25 16:10
 */
@Slf4j
public class TbRabbitMqProducerTemplate<T extends TbQueueMsg> implements TbQueueProducer<T> {
    /**
     * defaultTopic
     */
    private String defaultTopic;
    /**
     * rabbmit设置
     */
    private TbRabbitMqSettings rabbitMqSettings;
    /**
     * 线程执行器
     */
    private ListeningExecutorService producerExecutor;
    /**
     * 通道
     */
    private Channel channel;
    /**
     * 连接器
     */
    private Connection connection;
    /**
     * topic组
     */
    private final Set<TopicPartitionInfo> topics = ConcurrentHashMap.newKeySet();

    public TbRabbitMqProducerTemplate(TbRabbitMqSettings rabbitMqSettings, String defaultTopic) {
        this.defaultTopic = defaultTopic;
        this.rabbitMqSettings = rabbitMqSettings;
        producerExecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
        try {
            connection = rabbitMqSettings.getConnectionFactory().newConnection();
            channel = connection.createChannel();
        } catch (IOException | TimeoutException e) {
            log.error("rabbmit创建连接失败！", e);
        }

    }

    @Override
    public void init() {

    }

    @Override
    public String getDefaultTopic() {
        return defaultTopic;
    }

    @Override
    public void send(TopicPartitionInfo tpi, T msg, TbQueueCallback callback) {
        Boolean topicIfNotExist = createTopicIfNotExist(tpi);
        AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration(rabbitMqSettings.getMessageTtl()).build();
        try {
            if (!topicIfNotExist) {
                //topic不存在创建通道队列
                log.info("sendQueueDeclareAdd==>exchangeName:{} topic:{}",tpi.getExchangeName(),tpi.getTopic());
                Map<String, Object> args = new HashMap<>();
                args.put("x-message-ttl", Integer.parseInt(rabbitMqSettings.getMessageTtl()));
                channel.queueDeclare(tpi.getTopic(), true, false, false, args);
            }
            if (!innerExists(tpi.getExchangeName(), channel)) {
                //判断交换机是否存在，如果不存在则创建后与新加入的队列绑定
                channel = connection.createChannel();
                channel.exchangeDeclare(tpi.getExchangeName(), BuiltinExchangeType.DIRECT, true, false, null);
            }
            //channel.queueBind(tpi.getTopic(), tpi.getExchangeName(), tpi.getTopic());
            channel.basicPublish(tpi.getExchangeName(), tpi.getTopic(), properties, JSON.toJSONString(new DefaultTbQueueMsg(msg)).getBytes());
            if (callback != null) {
                callback.onSuccess(new RabbitQueueMsgMetadata(msg.getKey()));
            }
        } catch (IOException e) {
            log.error("Failed publish message: [{}].", msg, e);
            if (callback != null) {
                callback.onFailure(e);
            }
        }

    }

    @Override
    public void stop() {
        if (producerExecutor != null) {
            producerExecutor.shutdownNow();
        }
        if (channel != null) {
            try {
                channel.close();
            } catch (IOException | TimeoutException e) {
                log.error("Failed to close the channel.");
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (IOException e) {
                log.error("Failed to close the connection.");
            }
        }
    }

    @Override
    public void queueDeclare(TopicPartitionInfo tpi, TbQueueCallback callback) {
        Boolean topicIfNotExist = createTopicIfNotExist(tpi);
        try {
            if (!innerExists(tpi.getExchangeName(), channel)) {
                //判断交换机是否存在，如果不存在则创建后与新加入的队列绑定
                channel = connection.createChannel();
                channel.exchangeDeclare(tpi.getExchangeName(), BuiltinExchangeType.DIRECT, true, false, null);
            }
            if (!topicIfNotExist) {
                //topic不存在创建通道队列
                log.info("queueDeclareAdd==>exchangeName:{} topic:{}",tpi.getExchangeName(),tpi.getTopic());
                Map<String, Object> args = new HashMap<>();
                args.put("x-message-ttl", Integer.parseInt(rabbitMqSettings.getMessageTtl()));
                channel.queueDeclare(tpi.getTopic(), true, false, false, args);
                channel.queueBind(tpi.getTopic(), tpi.getExchangeName(), tpi.getTopic());
            }

            // callback.onSuccess(new RabbitQueueMsgMetadata());
        } catch (IOException e) {
            log.error("Failed publish message: {}.", e);
            if (callback != null) {
                callback.onFailure(e);
            }
        }
    }

    @Override
    public void queueDel(String queue, TbQueueCallback callback) {
        try {
            channel.queueDelete(queue);
            //删除topic
            TopicPartitionInfo topicPartitionInfo = this.topics.stream().filter(f -> f.getTopic().equals(queue)).findFirst().orElseGet(() -> null);
            if (!ObjectUtils.isEmpty(topicPartitionInfo)) {
                this.topics.remove(topicPartitionInfo);
            }
        } catch (IOException e) {
            log.error("Failed publish message: {}.", e);
        }
    }

    private Boolean createTopicIfNotExist(TopicPartitionInfo tpi) {
        //判断绑定的交换机与队列是否相同，如果不同返回false
        TopicPartitionInfo topicPartitionInfo = topics.stream()
                .filter(f -> f.getTopic().equals(tpi.getTopic())
                        && f.getExchangeName().equals(tpi.getExchangeName()))
                .findAny().orElseGet(() -> null);
        if (ObjectUtils.isEmpty(topicPartitionInfo)) {
            //exchange queue都不同
            topics.add(tpi);
            return false;
        }else{
            //queue相同 exchange不同
            TopicPartitionInfo temp = topics.stream()
                    .filter(f -> f.getTopic().equals(tpi.getTopic()))
                    .findAny().orElseGet(() -> null);
            if(!ObjectUtils.isEmpty(temp)){
                //queue相同 exchange不同 需重新绑定
                topics.remove(temp);
                topics.add(tpi);
                return false;
            }else{
                return true;
            }

        }
//
//        if (topics.contains(tpi)) {
//            //topic相同，exchange可能不同
//            TopicPartitionInfo topicPartitionInfo = topics.stream().filter(f -> f.getTopic().equals(tpi.getTopic()) && f.getExchangeName().equals(tpi.getExchangeName())).findAny().orElseGet(() -> null);
//            if (ObjectUtils.isEmpty(topicPartitionInfo)) {
//                //exchange不同
//                topics.add(tpi);
//                //topics.remove()
//                return true;
//            }
//
//            log.debug("contains topc:{}", tpi.getTopic());
//            return true;
//        }
//        topics.add(tpi);
//        return false;
    }

    private boolean innerExists(String exchangeName, Channel outerChannel) {
        boolean result = true;
        try {
            outerChannel.exchangeDeclarePassive(exchangeName);
        } catch (IOException e) {
            result = false;
        }
        return result;
    }
}
