package cn.axzo.msg.center.notices.service.impl;

import cn.axzo.msg.center.api.request.AliMessageCallbackRequestDto;
import cn.axzo.msg.center.api.response.AliCallbackResponseDto;
import cn.axzo.msg.center.dal.MNSBatchMessageDao;
import cn.axzo.msg.center.dal.MNSMessageChannelLogDao;
import cn.axzo.msg.center.dal.MNSMessageDao;
import cn.axzo.msg.center.domain.entity.MNSBatchMessage;
import cn.axzo.msg.center.domain.entity.MNSMessage;
import cn.axzo.msg.center.domain.entity.MNSMessageRedo;
import cn.axzo.msg.center.notices.common.constans.ChuangLanConstants;
import cn.axzo.msg.center.notices.common.domain.BatchMessageCountContext;
import cn.axzo.msg.center.notices.common.enums.BatchMessageStatusEnum;
import cn.axzo.msg.center.notices.common.enums.MessageSendTypeEnum;
import cn.axzo.msg.center.notices.common.enums.MessageStatusEnum;
import cn.axzo.msg.center.notices.manager.api.MessageManager;
import cn.axzo.msg.center.notices.manager.api.MessageRedoManger;
import cn.axzo.msg.center.notices.manager.api.dto.request.ChuangLanSmsReportRequestDto;
import cn.axzo.msg.center.notices.manager.api.dto.response.ChuangLanCallbackResponseDto;
import cn.axzo.msg.center.notices.service.api.MessageCallbackService;
import cn.azxo.framework.common.utils.LogUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

/**
 * @author: szg
 * @Date: 2021/5/28 15:49
 * @Description: 处理回执消息
 */
@Slf4j
@Service("messageCallbackService")
public class MessageCallbackServiceImpl implements MessageCallbackService {

  @Resource(name = "messageManager")
  private MessageManager messageManager;

  @Resource(name = "messageRedoManger")
  private MessageRedoManger messageRedoManger;

  @Resource
  private MNSMessageDao mnsMessageDao;

  @Resource
  private MNSMessageChannelLogDao mnsMessageChannelLogDao;

  @Resource
  private MNSBatchMessageDao mnsBatchMessageDao;

  @Resource(name = "transactionTemplate")
  private TransactionTemplate transactionTemplate;

  @Override
  public AliCallbackResponseDto aliCallbackHandle(List<AliMessageCallbackRequestDto> request) {
    log.info("MessageCallbackServiceImpl.aliCallbackHandle callback request {}", JSON.toJSONString(request));
    if (CollectionUtils.isEmpty(request)) {
      log.info("MessageCallbackServiceImpl.aliCallbackHandle callback request is null");
      return AliCallbackResponseDto.success();
    }

    for (AliMessageCallbackRequestDto messageInfo : request) {
      if (Objects.isNull(messageInfo)) {
        log.info("MessageCallbackServiceImpl.aliCallbackHandle callback message is null");
        continue;
      }
      log.info("MessageCallbackServiceImpl.aliCallbackHandle callback message is {}", JSON.toJSONString(messageInfo));
      String messageStatus = MessageStatusEnum.getSuccessCode(messageInfo.getSuccess());

      try {
        transactionTemplate.executeWithoutResult(status -> {
          MNSMessage message =
              messageManager.queryMessageByMessageOrderNoAndPhoneNumber(
                  messageInfo.getOut_id(), messageInfo.getBiz_id(), messageInfo.getPhone_number());
          if (Objects.nonNull(message)) {
            // 第一次回调
            log.info("MessageCallbackServiceImpl.aliCallbackHandle callback message is first");
            messageManager.updateProcessingMessageStatusById(message.getId(), messageStatus);
          } else {
            log.info("MessageCallbackServiceImpl.aliCallbackHandle callback message is retry");
            // 重试
            sendRetryHandle(messageInfo, messageStatus);
          }
        });
      } catch (Exception e) {
        LogUtil.error("MessageCallbackServiceImpl.aliCallbackHandle is error", e);
      }
    }
    return AliCallbackResponseDto.success();
  }

  @Override
  public ChuangLanCallbackResponseDto chuangLanCallbackHandle(
      ChuangLanSmsReportRequestDto request) {
    if (Objects.isNull(request)) {
      log.info("MessageCallbackServiceImpl.chuangLanCallbackHandle request is null");
      return ChuangLanCallbackResponseDto.success();
    }
    log.info("MessageCallbackServiceImpl.chuangLanCallbackHandle request is {}", JSON.toJSONString(request));

    try {
      if(MessageSendTypeEnum.SINGLE.getCode().equals(request.getUid())) {
        transactionTemplate.executeWithoutResult(status -> {
          MNSMessage message = mnsMessageDao
              .getByChannelMsgId(request.getMsgid(), request.getMobile());
          if (Objects.isNull(message)) {
            log.info("创蓝短信回调 MessageCallbackServiceImpl.chuangLanCallbackHandle message is null");
            return;
          }
          String messageStatus = MessageStatusEnum.FAIL.getCode();
          if (Objects.equals(request.getStatus(), ChuangLanConstants.MESSAGE_CALLBACK_SUCCESS_STATUS)) {
            messageStatus = MessageStatusEnum.SUCCESS.getCode();
          }
          mnsMessageDao
              .updateStatusAndRemarkById(message.getId(), messageStatus, request.getStatusDesc());

          mnsMessageChannelLogDao.updateCallbackDate(JSON.toJSONString(request), message.getMessageOrderNo());
        });
      } else {
        transactionTemplate.executeWithoutResult(status -> {
          MNSBatchMessage batchMessage = mnsBatchMessageDao.getByChannelMsgIdAndLock(request.getMsgid());
          if (Objects.isNull(batchMessage)) {
            log.info("MessageCallbackServiceImpl.chuangLanCallbackHandle batchMessage is null");
            return;
          }
          log.info("MessageCallbackServiceImpl.chuangLanCallbackHandle batchMessage is {}",
              JSON.toJSONString(batchMessage));
          //回调成功处理
          MNSMessage message = mnsMessageDao
              .getByBatchNoAndPhone(batchMessage.getBatchNo(), request.getMobile());
          if (Objects.isNull(message)) {
            log.info("创蓝短信回调 MessageCallbackServiceImpl.chuangLanCallbackHandle message is null");
            return;
          }
          //更新message 和 batchMessage 表
          updateMessageAndBatchMessage(request, message, batchMessage);
        });
      }
    } catch (Exception e) {
      LogUtil.error("MessageCallbackServiceImpl.chuangLanCallbackHandle is error", e);
    }

    return ChuangLanCallbackResponseDto.success();
  }

  /**
   * 修改 batch_message 和 message
   * @param request
   * @param message
   * @param batchMessage
   */
  private void updateMessageAndBatchMessage(ChuangLanSmsReportRequestDto request, MNSMessage message,
      MNSBatchMessage batchMessage) {
    log.info("MessageCallbackServiceImpl.updateMessageAndBatchMessage batchMessage is {}",
        batchMessage);
    log.info("MessageCallbackServiceImpl.updateMessageAndBatchMessage message is {}", message);
    //处理message相关
    handleMessageAndBatchMessageInfo(request, message, batchMessage);
    //当前批次已经全部回调完成发送消息更新上游状态
    if (Objects.equals(batchMessage.getBatchSize(),
        batchMessage.getSuccessCount() + batchMessage.getFailCount())) {
      //事务提交后发送消息
      log.info(
          "MessageCallbackServiceImpl.updateMessageAndBatchMessage 消息批次处理完成发送mq消息 batchMessage is {}",
          JSON.toJSONString(batchMessage));
      doAfterCommit(batchMessage);
    }
  }

  /**
   * 事务提交后操作(必须依赖事务)
   *
   * @param batchMessage
   */
  private void doAfterCommit(MNSBatchMessage batchMessage) {
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
      @Override
      public void afterCommit() {
        sendCountMessageToMq(batchMessage);
      }
    });
  }

  /**
   * 发送消息到 MQ
   * @param batchMessage
   */
  private void sendCountMessageToMq(MNSBatchMessage batchMessage) {
    BatchMessageCountContext context = new BatchMessageCountContext();
    context.setAppCode(batchMessage.getAppNo());
    context.setAppRequestNo(batchMessage.getRequestNo());
    context.setBatchNo(batchMessage.getBatchNo());
    context.setSuccessNum(batchMessage.getSuccessCount());
    context.setFailNum(batchMessage.getFailCount());
    // remove rocketmq
  }

  /**
   * 处理 batch_message 和 message
   * @param request
   * @param message
   * @param batchMessage
   */
  private void handleMessageAndBatchMessageInfo(ChuangLanSmsReportRequestDto request,
      MNSMessage message,
      MNSBatchMessage batchMessage) {
    Integer successCount = 0;
    Integer failCount = 1;
    String messageStatus = MessageStatusEnum.FAIL.getCode();
    if (Objects.equals(request.getStatus(), ChuangLanConstants.MESSAGE_CALLBACK_SUCCESS_STATUS)) {
      messageStatus = MessageStatusEnum.SUCCESS.getCode();
      successCount = 1;
      failCount = 0;
    }

    successCount = batchMessage.getSuccessCount() + successCount;
    failCount = batchMessage.getFailCount() + failCount;

    //上层发送mq消息时会用到
    batchMessage.setSuccessCount(successCount);
    batchMessage.setFailCount(failCount);

    //处理BatchMessage状态
    Integer batchMessageStatus = handBatchMessageStatus(batchMessage, failCount,successCount);
    boolean updateRes = mnsMessageDao
        .updateStatusAndRemarkById(message.getId(), messageStatus, request.getStatusDesc());
    //更新BachMessage
    if (updateRes) {
      mnsBatchMessageDao
          .updateBachMessageInfoById(batchMessage.getId(), batchMessageStatus, successCount,
              failCount);
    }
  }

  /**
   * 修改 batch_message 状态
   * @param batchMessage
   * @param failCount
   * @param successCount
   * @return
   */
  private Integer handBatchMessageStatus(MNSBatchMessage batchMessage, Integer failCount,
                                         Integer successCount) {

    Integer batchMessageStatus = BatchMessageStatusEnum.PARTIAL_SUCCESS.getCode();
    if (Objects.equals(successCount, batchMessage.getBatchSize())) {
      //全部成功
      batchMessageStatus = BatchMessageStatusEnum.SEND_SUCCESS.getCode();
    } else if (Objects.equals(failCount, batchMessage.getBatchSize())) {
      //还未全部完成回调 部分成功
      batchMessageStatus = BatchMessageStatusEnum.ALL_FAILED.getCode();
    }
    return batchMessageStatus;
  }

  /**
   * 消息重试
   * @param messageInfo
   * @param status
   */
  private void sendRetryHandle(AliMessageCallbackRequestDto messageInfo, String status) {
    MNSMessageRedo messageRedo =
        messageRedoManger.queryMessageRedoByRedoOrderNoAndPhoneNumber(
            messageInfo.getOut_id(), messageInfo.getBiz_id(), messageInfo.getPhone_number());
    if (Objects.nonNull(messageRedo)) {
      messageRedoManger
          .updateMessageRedoStatusById(messageRedo.getId(), MessageStatusEnum.EXCEPTION.getCode(),
              status);
      messageManager.updateMessageStatusByMessageOrderNo(messageRedo.getMessageOrderNo(), status);
    }
  }

}
