package mqMgr

import (
	"context"
	"errors"
	"fmt"
	"time"

	amqp "github.com/rabbitmq/amqp091-go"
)

// Queue对象
type HSYQueue struct {
	conn    *amqp.Connection
	channel *amqp.Channel
	// 队列名称
	queueName string
	// 交换机
	exchange string
	// routing Key
	routingKey string
	//MQ链接字符串
	mqurl string
}

// 消息队列配置对象
type HSYQueueConfig struct {
	QueueName  string
	Exchange   string
	RoutingKey string
	Network    string
	Port       int
	UserName   string
	Password   string
}

// 创建新的Queue对象
func NewHSYQueue(queueName, exchange, routingKey, userName, password string, network string, port int) *HSYQueue {
	queueConfigObj := &HSYQueueConfig{
		QueueName:  queueName,
		Exchange:   exchange,
		RoutingKey: routingKey,
		UserName:   userName,
		Password:   password,
		Network:    network,
		Port:       port,
	}

	return NewHSYQueueByConfig(queueConfigObj)
}

// 通过队列配置对象创建新的Queue对象
func NewHSYQueueByConfig(queueConfigObj *HSYQueueConfig) *HSYQueue {
	queueObj := &HSYQueue{
		queueName:  queueConfigObj.QueueName,
		exchange:   queueConfigObj.Exchange,
		routingKey: queueConfigObj.RoutingKey,
		mqurl:      fmt.Sprintf("amqp://%s:%s@%s:%d/", queueConfigObj.UserName, queueConfigObj.Password, queueConfigObj.Network, queueConfigObj.Port),
	}

	if err := queueObj.GetQueueAttributes(); err != nil {
		return nil
	}

	return queueObj
}

// 连接RabbitMQ服务器
func (this *HSYQueue) GetQueueAttributes() (err error) {
	this.conn, err = amqp.Dial(this.mqurl)
	if err != nil {
		return
	}

	this.channel, err = this.conn.Channel()
	if err != nil {
		return
	}

	return
}

// 释放连接
func (this *HSYQueue) ReleaseRes() {
	this.conn.Close()
	this.channel.Close()
}

// SendMessage 发送单条消息
func (this *HSYQueue) SendMessage(message string, ex string) (err error) {
	// 声明队列
	_, err = this.channel.QueueDeclare(
		this.queueName, // 队列名
		true,           // 是否持久化
		false,          // 是否自动删除(前提是至少有一个消费者连接到这个队列，之后所有与这个队列连接的消费者都断开时，才会自动删除。注意：生产者客户端创建这个队列，或者没有消费者客户端与这个队列连接时，都不会自动删除这个队列)
		false,          // 是否为排他队列（排他的队列仅对“首次”声明的conn可见[一个conn中的其他channel也能访问该队列]，conn结束后队列删除）
		false,          // 是否阻塞
		nil,            // 额外属性
	)
	if err != nil {
		err = errors.New("声明队列失败")
		return
	}

	// 声明交换器
	err = this.channel.ExchangeDeclare(
		this.exchange, //交换器名
		ex,            //exchange type：一般用fanout、direct、topic
		true,          // 是否持久化
		false,         //是否自动删除（自动删除的前提是至少有一个队列或者交换器与这和交换器绑定，之后所有与这个交换器绑定的队列或者交换器都与此解绑）
		false,         //设置是否内置的。true表示是内置的交换器，客户端程序无法直接发送消息到这个交换器中，只能通过交换器路由到交换器这种方式
		false,         // 是否阻塞
		nil,           // 额外属性
	)
	if err != nil {
		err = errors.New("声明交换器失败")
		return
	}

	// Binding
	err = this.channel.QueueBind(
		this.queueName,  // 绑定的队列名称
		this.routingKey, // bindkey 用于消息路由分发的key
		this.exchange,   // 绑定的exchange名
		false,           // 是否阻塞
		nil,             // 额外属性
	)
	if err != nil {
		err = errors.New("绑定队列和交换器失败")
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	err = this.channel.PublishWithContext(ctx,
		this.exchange,
		this.routingKey,
		false,
		false,
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        []byte(message),
		})
	if err != nil {
		err = errors.New("发布消息失败")
		return
	}

	return
}

// ReceiveMessage 消费单条消息
func (this *HSYQueue) ReceiveMessage() (msgs <-chan amqp.Delivery, err error) {
	_, err = this.channel.QueueDeclare(
		this.queueName, // 队列名
		true,           // 是否持久化
		false,          // 是否自动删除(前提是至少有一个消费者连接到这个队列，之后所有与这个队列连接的消费者都断开时，才会自动删除。注意：生产者客户端创建这个队列，或者没有消费者客户端与这个队列连接时，都不会自动删除这个队列)
		false,          // 是否为排他队列（排他的队列仅对“首次”声明的conn可见[一个conn中的其他channel也能访问该队列]，conn结束后队列删除）
		false,          // 是否阻塞
		nil,            // 额外属性（我还不会用）
	)
	if err != nil {
		err = errors.New("声明队列失败")
		return
	}

	msgs, err = this.channel.Consume(
		this.queueName, // 队列名
		"",             // 消费者名，用来区分多个消费者，以实现公平分发或均等分发策略
		true,           // 是否自动应答
		false,          // 是否排他
		false,          // 是否接收只同一个连接中的消息，若为true，则只能接收别的conn中发送的消息
		true,           // 队列消费是否阻塞
		nil,            // 额外属性
	)
	if err != nil {
		err = errors.New("获取消息失败")
		return
	}

	return
}
