package io.wdd.agent.initialization.bootup;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.Channel;
import io.wdd.agent.config.beans.init.AgentServerInfo;
import io.wdd.agent.message.OMessageToServerSender;
import io.wdd.agent.message.handler.OctopusMessageHandler;
import io.wdd.common.beans.rabbitmq.OctopusMessage;
import io.wdd.common.handler.MyRuntimeException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
@Lazy
@Slf4j
public class OctopusAgentInitService {

    @Resource
    OMessageToServerSender oMessageToServerSender;

    @Autowired
    OctopusMessageHandler octopusMessageHandler;

    @Resource
    AgentServerInfo agentServerInfo;

    @Value("${octopus.message.init_ttl}")
    String defaultInitRegisterTimeOut;

    @Resource
    ObjectMapper objectMapper;

    public void SendInfoToServer(AgentServerInfo agentServerInfo) {

        oMessageToServerSender.sendInitInfo(agentServerInfo, defaultInitRegisterTimeOut);

    }

    /**
     * listen to the PassThroughTopicName queue from octopus server
     *
     * @param message 该方法不需要手动调用，Spring会自动运行这个监听方法
     *
     *                注意：如果该监听方法正常结束，那么Spring会自动确认消息
     *                如果出现异常，则Spring不会确认消息，该消息一直存在于消息队列中
     *
     * @RabbitListener : 用于标记当前方法是一个RabbitMQ的消息监听方法，可以持续性的自动接收消息
     */
    @SneakyThrows
    @RabbitHandler
    @RabbitListener(
            bindings =
            @QueueBinding(
                    value = @Queue(name = "${octopus.message.init_from_server}"),
                    exchange = @Exchange(name = "${octopus.message.init_exchange}", type = "direct"),
                    key = {"${octopus.message.init_from_server_key}"}
            )
            ,
            ackMode = "MANUAL"
    )
    public void ReceiveInitInfoFromServer(Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {

        OctopusMessage octopusMessage;
        try {

            octopusMessage = objectMapper.readValue(message.getBody(), OctopusMessage.class);

            // consider the multi-agents register situation
            // judge the machineID begin
            String[] split = octopusMessage.getUuid().split("-");
            if (!agentServerInfo.getMachineId().startsWith(split[3])) {
                String s = "INIT Message not for this agent !";
                log.error(s);
                throw new MyRuntimeException(s);
            }

            // response chain to handle all kind of type of octopus message
            if (!octopusMessageHandler.handle(octopusMessage)) {
                String s = "Handle Octopus Message Error !";
                log.error(s);
                throw new MyRuntimeException(s);
            }

        } catch (Exception e) {

            // 拒绝信息，然后重新发送回队列
            channel.basicNack(
                    deliveryTag,
                    false,
                    true
            );

            // long deliveryTag, boolean requeue
            // channel.basicReject(deliveryTag,true);
            log.error("Octopus Agent Initialization Error, please check !  Waiting for 5 seconds");

            // 等待5秒钟，让其他的主机能够找到自己的信息
            TimeUnit.SECONDS.sleep(5);

            // 需要返回 重新获取
            return;
        }

        // handle init message ok
        // ack the info
        channel.basicAck(deliveryTag, false);
    }


}
