package org.jeecg.boot.starter.rabbitmq.client;

import cn.hutool.core.util.ObjectUtil;
import org.jeecg.boot.starter.rabbitmq.exchange.DelayExchangeBuilder;
import org.jeecg.common.annotation.RabbitComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class RabbitMqClient {
    private static final Logger log = LoggerFactory.getLogger(RabbitMqClient.class);
    private static final Logger logger = LoggerFactory.getLogger(RabbitMqClient.class);
    private final RabbitAdmin rabbitAdmin;
    private final RabbitTemplate rabbitTemplate;
    @Resource
    private SimpleMessageListenerContainer messageListenerContainer;
    @Resource
    private ApplicationEventPublisher publisher;
    @Resource
    private ApplicationContext applicationContext;
    private Map sentObj = new HashMap();

    @Bean
    public void initQueue() {
        Map<String, Object> beansWithRqbbitComponentMap = this.applicationContext.getBeansWithAnnotation(RabbitComponent.class);
        Class<?> clazz = null;
        for (Map.Entry<String, Object> entry : beansWithRqbbitComponentMap.entrySet()) {
            clazz = entry.getValue().getClass();
            Method[] methods = clazz.getMethods();
            RabbitListener rabbitListener = clazz.getAnnotation(RabbitListener.class);
            if (ObjectUtil.isNotEmpty(rabbitListener)) {
                log.info("初始化队列....class........rabbitListener=" + rabbitListener);
                this.createQueue(rabbitListener);
            }
            for (Method method : methods) {
                RabbitListener methodRabbitListener = method.getAnnotation(RabbitListener.class);
                if (!ObjectUtil.isNotEmpty(methodRabbitListener)) continue;
                log.info("初始化队列...method.........methodRabbitListener=" + methodRabbitListener);
                this.createQueue(methodRabbitListener);
            }
        }
    }

    private void createQueue(RabbitListener rabbitListener) {
        String[] queues = rabbitListener.queues();
        DirectExchange directExchange = this.createExchange("jeecg.direct.exchange");
        this.rabbitAdmin.declareExchange(directExchange);
        if (ObjectUtil.isNotEmpty(queues)) {
            for (String queueName : queues) {
                Properties result = this.rabbitAdmin.getQueueProperties(queueName);
                if (ObjectUtil.isEmpty(result)) {
                    Queue queue = new Queue(queueName);
                    this.addQueue(queue);
                    Binding binding = BindingBuilder.bind((Queue) queue).to(directExchange).with(queueName);
                    this.rabbitAdmin.declareBinding(binding);
                    log.info("创建队列:" + queueName);
                    continue;
                }
                log.info("已有队列:" + queueName);
            }
        }
    }

    public boolean createQueue(String queueName) {
        DirectExchange directExchange = this.createExchange("jeecg.direct.exchange");
        this.rabbitAdmin.declareExchange(directExchange);
        Properties result = this.rabbitAdmin.getQueueProperties(queueName);
        if (ObjectUtil.isEmpty(result)) {
            Queue queue = new Queue(queueName);
            this.addQueue(queue);
            Binding binding = BindingBuilder.bind(queue).to(directExchange).with(queueName);
            this.rabbitAdmin.declareBinding(binding);
            log.info("创建队列:" + queueName);
            return true;
        }
        log.info("已有队列:" + queueName);
        return false;
    }

    @Autowired
    public RabbitMqClient(RabbitAdmin rabbitAdmin, RabbitTemplate rabbitTemplate) {
        this.rabbitAdmin = rabbitAdmin;
        this.rabbitTemplate = rabbitTemplate;
    }

    public Message getMessage(String messageType, Object msg) {
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(messageType);
        Message message = new Message(msg.toString().getBytes(), messageProperties);
        return message;
    }

    public void sendMessageToExchange(TopicExchange topicExchange, String routingKey, Object msg) {
        Message message = this.getMessage("application/json", msg);
        this.rabbitTemplate.send(topicExchange.getName(), routingKey, message);
    }

    public void sendMessageToExchange(TopicExchange topicExchange, AbstractExchange exchange, String msg) {
        this.addExchange(exchange);
        logger.info("RabbitMQ send " + exchange.getName() + "->" + msg);
        this.rabbitTemplate.convertAndSend(topicExchange.getName(), msg);
    }

    public void sendMessage(String queueName, Object params) {
        log.info("发送消息到mq");
        try {
            this.rabbitTemplate.convertAndSend("jeecg.direct.exchange", queueName, params, message -> message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void sendMessage(String queueName) {
        this.send(queueName, this.sentObj, 0);
        this.sentObj.clear();
    }

    public RabbitMqClient put(String key, Object value) {
        this.sentObj.put(key, value);
        return this;
    }

    public void sendMessage(String queueName, Object params, Integer expiration) {
        this.send(queueName, params, expiration);
    }

    private void send(String queueName, Object params, Integer expiration) {
        Queue queue = new Queue(queueName);
        this.addQueue(queue);
        CustomExchange customExchange = DelayExchangeBuilder.buildExchange();
        this.rabbitAdmin.declareExchange(customExchange);
        Binding binding = BindingBuilder.bind(queue).to(customExchange).with(queueName).noargs();
        this.rabbitAdmin.declareBinding(binding);
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        log.debug("发送时间：" + sf.format(new Date()));
        this.rabbitTemplate.convertAndSend("jeecg.delayed.exchange", queueName, params, message -> {
            if (expiration != null && expiration > 0) {
                message.getMessageProperties().setHeader("x-delay", (Object) expiration);
            }
            return message;
        });
    }

    public String receiveFromQueue(String queueName) {
        return this.receiveFromQueue(DirectExchange.DEFAULT, queueName);
    }

    public String receiveFromQueue(DirectExchange directExchange, String queueName) {
        Queue queue = new Queue(queueName);
        this.addQueue(queue);
        Binding binding = BindingBuilder.bind(queue).to(directExchange).withQueueName();
        this.rabbitAdmin.declareBinding(binding);
        String messages = (String) this.rabbitTemplate.receiveAndConvert(queueName);
        System.out.println("Receive:" + messages);
        return messages;
    }

    public void addExchange(AbstractExchange exchange) {
        this.rabbitAdmin.declareExchange(exchange);
    }

    public boolean deleteExchange(String exchangeName) {
        return this.rabbitAdmin.deleteExchange(exchangeName);
    }

    public Queue addQueue() {
        return this.rabbitAdmin.declareQueue();
    }

    public String addQueue(Queue queue) {
        return this.rabbitAdmin.declareQueue(queue);
    }

    public void deleteQueue(String queueName, boolean unused, boolean empty) {
        this.rabbitAdmin.deleteQueue(queueName, unused, empty);
    }

    public boolean deleteQueue(String queueName) {
        return this.rabbitAdmin.deleteQueue(queueName);
    }

    public void addBinding(Queue queue, TopicExchange exchange, String routingKey) {
        Binding binding = BindingBuilder.bind(queue).to(exchange).with(routingKey);
        this.rabbitAdmin.declareBinding(binding);
    }

    public void addBinding(Exchange exchange, TopicExchange topicExchange, String routingKey) {
        Binding binding = BindingBuilder.bind(exchange).to(topicExchange).with(routingKey);
        this.rabbitAdmin.declareBinding(binding);
    }

    public void removeBinding(Binding binding) {
        this.rabbitAdmin.removeBinding(binding);
    }

    public DirectExchange createExchange(String exchangeName) {
        return new DirectExchange(exchangeName, true, false);
    }
}