package cn.axzo.msg.center.notices.manager;

import cn.axzo.msg.center.dal.MNSBatchMessageDao;
import cn.axzo.msg.center.dal.MNSChannelMessageTemplateDao;
import cn.axzo.msg.center.dal.MNSMessageDao;
import cn.axzo.msg.center.domain.entity.MNSBatchMessage;
import cn.axzo.msg.center.domain.entity.MNSChannelMessageTemplate;
import cn.axzo.msg.center.notices.common.domain.BatchMessageCountContext;
import cn.axzo.msg.center.notices.common.enums.*;
import cn.axzo.msg.center.notices.common.exception.BizException;
import cn.axzo.msg.center.notices.integration.client.ChuangLanSmsClient;
import cn.axzo.msg.center.notices.integration.client.DingDingClient;
import cn.axzo.msg.center.notices.integration.dto.request.MessageRequest;
import cn.axzo.msg.center.notices.integration.dto.response.ChuangLanSmsResponse;
import cn.axzo.msg.center.notices.manager.api.SmsSendManager;
import cn.axzo.msg.center.notices.manager.api.dto.request.BatchMessageSendRequestDto;
import cn.axzo.msg.center.notices.manager.api.dto.request.MessageSendRequestDto;
import cn.axzo.msg.center.notices.manager.api.dto.response.BatchMessageSendResponseDto;
import cn.axzo.msg.center.notices.manager.api.dto.response.SendSmsCommonResponseDto;
import cn.axzo.msg.center.notices.manager.support.ChuangLanMessageContentSupport;
import cn.azxo.framework.common.utils.LogUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;

/**
 * 创蓝短信发送 Manager
 *
 * @author zhaoyong
 * @see ChuangLanSmsSendManagerImpl
 * @since 2021-08-07 19:06
 */
@Slf4j
@Service("chuangLanSmsSendManager")
public class ChuangLanSmsSendManagerImpl implements SmsSendManager {

  @Resource(name = "chuangLanSmsClient")
  private ChuangLanSmsClient chuangLanSmsClient;

  @Resource
  private MNSChannelMessageTemplateDao mnsChannelMessageTemplateDao;

  @Resource
  private MNSMessageDao mnsMessageDao;

  @Resource
  private MNSBatchMessageDao mnsBatchMessageDao;

  @Resource(name = "dingDingClient")
  private DingDingClient dingDingClient;

  @Resource(name = "chuangLanMessageContentSupport")
  private ChuangLanMessageContentSupport chuangLanMessageContentSupport;

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

  @Resource(name = "genericConversionService")
  private GenericConversionService genericConversionService;

  /** 相同内容群发接口 URL */
  private final static String COMMON_URL = "http://smssh1.253.com/msg/v1/send/json";

  /** 相同内容变量接口 URL */
  private final static String VARIABLE_URL = "http://smssh1.253.com/msg/variable/json";

  @Override
  public SendSmsCommonResponseDto sendMessage(MessageSendRequestDto requestDto) {
    String channelCode = MessageChannelEnum.CHUANG_LAN.getCode();
    // 获取渠道模板
    MNSChannelMessageTemplate template =
        mnsChannelMessageTemplateDao.queryByTemplateNo(requestDto.getInnerTemplateNo(), channelCode);
    BizException.error(template != null, ReturnCodeEnum.SYSTEM_ERROR, "渠道模板不能为空");

    MessageRequest request = new MessageRequest();
    boolean hasParam = template.hasParam();
    request.setHasParam(hasParam);
    if(hasParam) {
      request.setRequestUrl(VARIABLE_URL);
    } else {
      request.setRequestUrl(COMMON_URL);
    }
    String smsContent = chuangLanMessageContentSupport.getSingleSmsContent(requestDto, template, hasParam);
    request.setSmsContent(smsContent);
    MessageTemplateTypeEnum typeEnum = MessageTemplateTypeEnum.getByCode(template.getType());
    request.setMessageTemplateType(typeEnum);
    request.setTemplateContent(template.getTemplateContent());
    request.setRequestChannelNo(MessageSendTypeEnum.SINGLE.getCode());

    // 发送单条短信
    ChuangLanSmsResponse response = chuangLanSmsClient.sendMessage(request);
    SendSmsCommonResponseDto result = new SendSmsCommonResponseDto();
    result.setRequestId(response.getMsgId());
    result.setMessage(result.getMessage());
    return result;
  }

  @Override
  public BatchMessageSendResponseDto sendBatchMessage(BatchMessageSendRequestDto requestDto) {
    log.info("ChuangLanSmsManagerImpl.sendBatchMessage request param is {}", JSON.toJSONString(requestDto));
    String channelCode = MessageChannelEnum.CHUANG_LAN.getCode();
    String channelName = MessageChannelEnum.CHUANG_LAN.getMessage();

    // 获取渠道模板
    MNSChannelMessageTemplate template =
        mnsChannelMessageTemplateDao.queryByTemplateNo(requestDto.getInnerTemplateNo(), channelCode);
    BizException.error(template != null, ReturnCodeEnum.SYSTEM_ERROR, "渠道模板不能为空");

    // 修改批量短信渠道和渠道模板
    transactionTemplate.executeWithoutResult(status -> {
      mnsBatchMessageDao.updateChannelInfo(channelCode, template.getTemplateNo(), requestDto.getBatchNo());
      mnsMessageDao.updateBatchMessageChannelInfo(channelCode, channelName, requestDto.getBatchNo());
    });

    MessageRequest request = new MessageRequest();
    boolean hasParam = template.hasParam();
    request.setHasParam(hasParam);
    if(hasParam) {
      request.setRequestUrl(VARIABLE_URL);
    } else {
      request.setRequestUrl(COMMON_URL);
    }
    String smsContent = chuangLanMessageContentSupport.getBatchSmsContent(requestDto, template, hasParam);
    request.setSmsContent(smsContent);
    MessageTemplateTypeEnum typeEnum = MessageTemplateTypeEnum.getByCode(template.getType());
    request.setMessageTemplateType(typeEnum);
    request.setTemplateContent(template.getTemplateContent());
    request.setRequestChannelNo(MessageSendTypeEnum.BATCH.getCode());
    try {
      // 发送批量短信
      ChuangLanSmsResponse response = chuangLanSmsClient.sendMessage(request);

      transactionTemplate.executeWithoutResult(status -> {
        if(response.getFailNum() == requestDto.getSmsContentDtos().size()) {
          // 失败提交数为总条数
          updateBatchMessageToFail(requestDto, response);
        } else {
          // 失败提交数小于总条数
          updateAfterChannelResponse(requestDto, response);
        }
      });

      return genericConversionService.convert(response, BatchMessageSendResponseDto.class);
    } catch (BizException e) {
      LogUtil.error("ChuangLanSmsManagerImpl.sendBatchMessage is BizException.", e);

      transactionTemplate.executeWithoutResult(status -> {
        updateBatchMessageToFail(requestDto, e);
      });
      throw e;
    }
  }

  /**
   * 提交渠道成功
   * @param requestDto
   * @param response
   */
  private void updateAfterChannelResponse(BatchMessageSendRequestDto requestDto, ChuangLanSmsResponse response) {
    MNSBatchMessage batchMessage = new MNSBatchMessage();
    batchMessage.setChannelReturnCode(response.getCode());
    batchMessage.setChannelErrorMsg(response.getErrorMsg());
    batchMessage.setChannelMsgId(response.getMsgId());
    batchMessage.setFailCount(response.getFailNum());
    batchMessage.setSubmitFailNum(response.getFailNum());
    batchMessage.setSubmitSuccessNum(response.getSuccessNum());
    mnsBatchMessageDao.updateBatchMessage(batchMessage, requestDto.getBatchNo());
  }

  /**
   * 提交渠道全部失败
   * @param requestDto
   * @param response
   */
  private void updateBatchMessageToFail(BatchMessageSendRequestDto requestDto, ChuangLanSmsResponse response){
    // 更新 batch_message 表
    MNSBatchMessage batchMessage = new MNSBatchMessage();
    batchMessage.setStatus(BatchMessageStatusEnum.ALL_FAILED.getCode());
    batchMessage.setSubmitFailNum(response.getFailNum());
    batchMessage.setSubmitSuccessNum(response.getSuccessNum());
    batchMessage.setChannelReturnCode(response.getCode());
    batchMessage.setChannelErrorMsg(response.getErrorMsg());
    batchMessage.setChannelMsgId(response.getMsgId());
    mnsBatchMessageDao.updateBatchMessage(batchMessage, requestDto.getBatchNo());

    // 更新 message 表
    mnsMessageDao.updateAllBatchMessageFail(response.getErrorMsg(), requestDto.getBatchNo());

    // 事务提交后操作
    doAfterCommit(requestDto, response, null);
  }

  /**
   * 提交渠道异常
   * @param requestDto
   * @param e
   */
  private void updateBatchMessageToFail(BatchMessageSendRequestDto requestDto, BizException e) {
    MNSBatchMessage batchMessage = new MNSBatchMessage();
    batchMessage.setChannelReturnCode(e.getBizCode() + "");
    batchMessage.setChannelErrorMsg(e.getMessage());
    batchMessage.setStatus(BatchMessageStatusEnum.ALL_FAILED.getCode());
    int batchSize = requestDto.getSmsContentDtos().size();
    batchMessage.setFailCount(batchSize);
    batchMessage.setSubmitFailNum(batchSize);
    batchMessage.setChannelErrorMsg(e.getMessage());
    mnsBatchMessageDao.updateBatchMessage(batchMessage, requestDto.getBatchNo());

    // 更新 message 表
    mnsMessageDao.updateAllBatchMessageFail(e.getMessage(), requestDto.getBatchNo());

    // 事务提交后操作
    doAfterCommit(requestDto, null, e);
  }

  /**
   * 事务提交后操作(必须依赖事务)
   * @param request
   */
  private void doAfterCommit(BatchMessageSendRequestDto request
      , ChuangLanSmsResponse response, BizException e) {
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
      @Override
      public void afterCommit() {
        // 获取消息内容
        BatchMessageCountContext context = getMessageContext(request, response, e);

        // remove rocketmq

        // 发送钉钉消息
        dingDingClient.notifyBatchMessage(request.getInnerTemplateNo(), request.getBatchNo(), getErrorMsg(response, e));
      }
    });
  }

  /**
   * 获取消息上下文
   * @param request
   * @param response
   * @param e
   * @return
   */
  private BatchMessageCountContext getMessageContext(BatchMessageSendRequestDto request
      , ChuangLanSmsResponse response, BizException e){
    boolean hasException = false;
    if(e != null) {
      hasException = true;
    }
    BatchMessageCountContext context = new BatchMessageCountContext();
    context.setAppCode(request.getAppCode());
    context.setAppRequestNo(request.getAppRequestNo());
    context.setBatchNo(request.getBatchNo());
    if(hasException) {
      context.setFailNum(request.getSmsContentDtos().size());
    } else {
      context.setFailNum(response.getFailNum());
    }
    return context;
  }

  /**
   * 获取错误信息
   * @return
   */
  private String getErrorMsg(ChuangLanSmsResponse response, BizException e) {
    if(e != null) {
      return e.getMessage();
    } else {
      return response.getErrorMsg();
    }
  }

}
