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 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;

/**
 * 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不存在创建通道队列
                Map<String, Object> args = new HashMap<>();
                log.info("x-message-ttl:{}",rabbitMqSettings.getMessageTtl());
                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(null);
            }
        } 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 (!topicIfNotExist) {
                //topic不存在创建通道队列
                Map<String, Object> args = new HashMap<>();
                log.info("x-message-ttl:{}",rabbitMqSettings.getMessageTtl());
                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);
            }
            callback.onSuccess(null);
        } 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);
        } catch (IOException e) {
            log.error("Failed publish message: {}.", e);
        }
    }

    private Boolean createTopicIfNotExist(TopicPartitionInfo tpi) {
        if (topics.contains(tpi)) {
            log.info("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;
    }
}
