package net.northking.cctp.executePlan.consumer;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import net.northking.cctp.executePlan.api.service.AtuPlanBatchApiService;
import net.northking.cctp.executePlan.constants.PlanConstant;
import net.northking.cctp.executePlan.constants.RabbitConstant;
import net.northking.cctp.executePlan.db.service.AtuPlanBatchDeviceLinkService;
import net.northking.cctp.executePlan.db.service.AtuPlanTaskService;
import net.northking.cctp.executePlan.dto.planEntrust.AtuEntrustInfoDto;
import net.northking.cctp.executePlan.dto.planEntrust.AtuEntrustMsgDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;

/**
 * <p>Title: PublishEntrustConsumer</p>
 * <p>Description: 发布委托</p>
 *
 * @Author chenjianfeng
 * @Version V1.0
 * @Date 2022/12/09
 * <p>Copyright (c) 京北方信息技术股份有限公司 Corporation 2022 . All rights reserved.</p>
 */
@Component
@RefreshScope
public class PublishEntrustConsumer {

    private final static Logger logger = LoggerFactory.getLogger(PublishEntrustConsumer.class);

    /**
     * 最大重试次数
     */
    @Value("${atu.plan.entrust.maxRetryNum}")
    public static final int MAX_RETRY_NUM = 5;

    /**
     * 重试等待时间
     */
    @Value("${atu.plan.entrust.retryWaitTime}")
    public static final int RETRY_WAIT_TIME = 5000;

    @Autowired
    private AtuPlanTaskService atuPlanTaskService;

    @Autowired
    private AmqpAdmin amqpAdmin;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private AtuPlanBatchApiService planBatchApiService;

    @Autowired
    private AtuPlanBatchDeviceLinkService planBatchDeviceLinkService;

    @PostConstruct
    public void init(){
        // 初始化队列并绑定到交换机
        Exchange exchange = new DirectExchange(RabbitConstant.PROJECT_EXCHANGE_KEY);
        amqpAdmin.declareExchange(exchange);
        Queue queue = new Queue(RabbitConstant.PLAN_BATCH_ENTRUST);
        amqpAdmin.declareQueue(queue);
        Binding binding = BindingBuilder.bind(queue).to(exchange)
                .with(RabbitConstant.PLAN_BATCH_ENTRUST).noargs();
        amqpAdmin.declareBinding(binding);
    }

    /**
     * 发布委托
     * @param message 对象消息
     */
    @RabbitListener(queues = RabbitConstant.PLAN_BATCH_ENTRUST)
    public void receive(Message message){
        try {
            logger.info("发布委托信息 ==> " + new String(message.getBody()));
            JSONObject event = JSONUtil.parseObj(new String(message.getBody()));
            AtuEntrustMsgDto entrustMsg = JSONUtil.toBean(event, AtuEntrustMsgDto.class);
            // 判断该批次任务是否执行完成
            boolean isFinish = atuPlanTaskService.checkBatchFinish(entrustMsg.getBatchId());
            if (isFinish && entrustMsg.getEntrustNum() > 0) {
                // 批次任务已全部执行完成,无需委托
                return;
            }
            if (entrustMsg.getEntrustNum() >= MAX_RETRY_NUM) {
                logger.error("委托失败重试" + MAX_RETRY_NUM + "次还未成功");
                long lastOfflineTime = System.currentTimeMillis() - MAX_RETRY_NUM * RETRY_WAIT_TIME;
                planBatchApiService.processBatchTaskTimeout(entrustMsg.getBatchId(), lastOfflineTime);
                return;
            }

            // 发送委托至引擎
            String url = "http://" + entrustMsg.getIp() + ":" + entrustMsg.getPort() + PlanConstant.PLAN_BATCH_ENTRUST_URL;
            logger.info("委托地址 => " + url);
            AtuEntrustInfoDto entrustInfo = new AtuEntrustInfoDto();
            BeanUtil.copyProperties(entrustMsg, entrustInfo);
            logger.info("委托参数 => " + JSONUtil.toJsonStr(entrustInfo));

            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<AtuEntrustInfoDto> requestEntity = new HttpEntity<>(entrustInfo, headers);
            try {
                ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
                logger.info("委托结果 => " + JSONUtil.toJsonStr(response.getBody()));
                if (response.getStatusCode().is2xxSuccessful()) {
                    return;
                }
            } catch (Exception e) {
                logger.error("引擎[" + entrustMsg.getIp() + ":" + entrustMsg.getPort() + "]委托失败", e);
            }
            // 将委托信息放至队列尾部，等待下一次委托
            logger.debug("委托处理异常，休眠 " + RETRY_WAIT_TIME + "毫秒");
            ThreadUtil.safeSleep(RETRY_WAIT_TIME);
            // TODO: 2022/12/12 若设备离线后20分钟内，队列委托信息少，会频繁执行这一步
            entrustMsg.setEntrustNum(entrustMsg.getEntrustNum() + 1);
            amqpTemplate.convertAndSend(RabbitConstant.PLAN_BATCH_ENTRUST, JSONUtil.toJsonStr(entrustMsg));
        }catch (Exception e){
            logger.error("发布委托失败", e);
        }
    }
}
