package cn.axzo.msg.center.notices.integration.client.impl;

import cn.axzo.msg.center.notices.common.constans.AliyunConstants;
import cn.axzo.msg.center.notices.common.domain.ServiceContextHolder;
import cn.axzo.msg.center.notices.common.enums.ReturnCodeEnum;
import cn.axzo.msg.center.notices.common.enums.SendDetailsStatusEnum;
import cn.axzo.msg.center.notices.common.exception.BizException;
import cn.axzo.msg.center.notices.integration.client.AliYunSmsClient;
import cn.axzo.msg.center.notices.integration.client.AliyunSmsClientFactory;
import cn.axzo.msg.center.notices.integration.dto.request.SendSmsCommonRequest;
import cn.axzo.msg.center.notices.integration.dto.response.SendSmsCommonResponse;
import cn.azxo.framework.common.utils.LogUtil;
import com.alibaba.fastjson.JSON;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.QuerySendDetailsRequest;
import com.aliyun.dysmsapi20170525.models.QuerySendDetailsResponse;
import com.aliyun.dysmsapi20170525.models.QuerySendDetailsResponseBody;
import com.aliyun.dysmsapi20170525.models.QuerySendDetailsResponseBody.QuerySendDetailsResponseBodySmsSendDetailDTOs;
import com.aliyun.dysmsapi20170525.models.QuerySendDetailsResponseBody.QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.dysmsapi20170525.models.SendSmsResponseBody;
import com.aliyun.teautil.models.RuntimeOptions;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

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

/**
 * 阿里云短信服务
 *
 * @author zhaoyong_sh
 * @see AliYunSmsClientImpl
 * @since 2021-05-19 12:39
 */
@Slf4j
@Service("aliYunSmsClient")
public class AliYunSmsClientImpl implements AliYunSmsClient {

  @Resource(name = "aliyunSmsClientFactory")
  private AliyunSmsClientFactory aliyunSmsClientFactory;

  @Value("${sms.aliyun.accessKeyId}")
  private String accessKeyId;

  @Value("${sms.aliyun.accessSecret}")
  private String accessSecret;

  @Value("${sms.aliyun.signName}")
  private String signName;

  @Override
  public SendDetailsStatusEnum queryExceptionSendDetails(String cellphone, String sendDate,
      String outId) {
    RuntimeOptions runtime = new RuntimeOptions();
    QuerySendDetailsRequest request = new QuerySendDetailsRequest();
    request.setCurrentPage(1L);
    request.setPageSize(50L);
    request.setPhoneNumber(cellphone);
    request.setSendDate(sendDate);
    QuerySendDetailsResponse response;
    try {
      log.info("AliyunSmsService#queryExceptionSendDetails request param is {}", cellphone);
      response = getClient().querySendDetailsWithOptions(request, runtime);
      log.info("AliyunSmsService#queryExceptionSendDetails response is {}",
          JSON.toJSONString(response));
    } catch (Exception e) {
      LogUtil.error("AliyunSmsService#queryExceptionSendDetails is error", e);
      throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, "query send details fail");
    }
    List<QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO> sendDetailDTOS = checkResponse(
        response);
    if (CollectionUtils.isEmpty(sendDetailDTOS)) {
      return null;
    }
    Optional<QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO> matchOutIdOptional =
        sendDetailDTOS.stream().filter(v -> outId.equals(v.getOutId())).findFirst();
    if (!matchOutIdOptional.isPresent()) {
      return null;
    }
    QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO dto = matchOutIdOptional.get();
    SendDetailsStatusEnum sendStatusEnum = SendDetailsStatusEnum.getByCode(dto.getSendStatus());
    BizException.error(sendStatusEnum != null, ReturnCodeEnum.SYSTEM_ERROR,
        "非法的响应值:" + dto.getSendStatus());
    return sendStatusEnum;
  }

  @Override
  public SendDetailsStatusEnum queryProcessingSendDetail(String cellphone, String sendDate,
      String bizId) {
    RuntimeOptions runtime = new RuntimeOptions();
    QuerySendDetailsRequest request = new QuerySendDetailsRequest();
    request.setCurrentPage(1L);
    request.setPageSize(50L);
    request.setPhoneNumber(cellphone);
    request.setSendDate(sendDate);
    request.setBizId(bizId);
    QuerySendDetailsResponse response;
    try {
      log.info("AliyunSmsService#queryExceptionSendDetails request param is {}", cellphone);
      response = getClient().querySendDetailsWithOptions(request, runtime);
      log.info("AliyunSmsService#queryExceptionSendDetails response is {}",
          JSON.toJSONString(response));
    } catch (Exception e) {
      LogUtil.error("AliyunSmsService#queryExceptionSendDetails is error", e);
      throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, "query send details fail");
    }
    List<QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO> sendDetailDTOS = checkResponse(
        response);
    if (CollectionUtils.isEmpty(sendDetailDTOS)) {
      return null;
    }
    QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO dto = sendDetailDTOS.get(0);
    SendDetailsStatusEnum sendStatusEnum = SendDetailsStatusEnum.getByCode(dto.getSendStatus());
    BizException.error(sendStatusEnum != null, ReturnCodeEnum.SYSTEM_ERROR,
        "非法的响应值:" + dto.getSendStatus());
    return sendStatusEnum;
  }

  @Override
  public SendSmsCommonResponse sendSms(SendSmsCommonRequest request) {
    log.info("[发送sms方法调用] -> {}", JSON.toJSONString(request));
    Client client = getClient();
    SendSmsRequest aliyunRequest = new SendSmsRequest();
    aliyunRequest.setPhoneNumbers(request.getPhoneNo());
    aliyunRequest.setOutId(request.getRequestChannelNo());
    aliyunRequest.setSignName(signName);
    aliyunRequest.setTemplateCode(request.getTemplateCode());
    aliyunRequest.setTemplateParam(JSON.toJSONString(request.getTemplateMap()));
    log.info("[mns <- aliyun]AliyunSmsService.sendSms response is {}", JSON.toJSONString(aliyunRequest));
    SendSmsResponse response;
    try {
      response = client.sendSms(aliyunRequest);
      log.info("[mns <- aliyun]AliyunSmsService.sendSms response is {}", JSON.toJSONString(response));
    } catch (Exception e) {
      LogUtil.error("[mns <- aliyun]AliyunSmsService.sendSms is fail", e);
      throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, "invoke aliyun fail");
    }

    // 添加响应到上下文
    ServiceContextHolder.get().setChannelResponse(response);

    // 检查响应
    SendSmsResponseBody responseBody = checkSendResponse(aliyunRequest, response);

    // 响应转换
    SendSmsCommonResponse smsResponse = new SendSmsCommonResponse();
    smsResponse.setRequestId(responseBody.getRequestId());
    smsResponse.setMessage(responseBody.getMessage());
    smsResponse.setBizId(responseBody.getBizId());
    smsResponse.setCode(responseBody.getCode());
    return smsResponse;
  }

  /**
   * 获取客户端
   *
   * @return
   */
  private Client getClient() {
    return aliyunSmsClientFactory.getClient(accessKeyId, accessSecret);
  }

  /**
   * 检查发送短信结果
   *
   * @param request
   * @param response
   * @return
   */
  private SendSmsResponseBody checkSendResponse(SendSmsRequest request, SendSmsResponse response) {
    if (response == null) {
      log.info("AliyunSmsService#checkSendResponse response is null");
      throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, "aliyun response is null");
    }
    SendSmsResponseBody responseBody = response.getBody();
    if (responseBody == null) {
      log.info("AliyunSmsService#checkSendResponse response body is null");
      throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, "aliyun response body is null");
    }
    if (!AliyunConstants.SUCCESS_CODE.equals(responseBody.getCode())) {
      String message = responseBody.getMessage();
      boolean isRateLimited = message != null && message.contains("触发") && message.contains("流控");
      log.warn("AliyunSmsService#checkResponse is fail, error message  : {}", message);
      message = String.format("%s. templateCode=%s, phones=%s, params=%s", message,
              request.getTemplateCode(), request.getPhoneNumbers(), request.getTemplateParam());
      if (isRateLimited) {
        throw new RateLimitException(ReturnCodeEnum.SYSTEM_ERROR, message);
      } else {
        throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, message);
      }
    }
    return responseBody;
  }

  /**
   * 检查查询短信发送详情响应
   *
   * @param response
   * @return
   */
  private List<QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO> checkResponse(
      QuerySendDetailsResponse response) {
    if (response == null) {
      log.info("AliyunSmsService#checkResponse response is null");
      throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, "aliyun response is null");
    }
    QuerySendDetailsResponseBody responseBody = response.getBody();
    if (responseBody == null) {
      log.info("AliyunSmsService#checkResponse response body is null");
      throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, "aliyun response body is null");
    }
    if (!AliyunConstants.SUCCESS_CODE.equals(responseBody.getCode())) {
      String message = responseBody.getMessage();
      log.info("AliyunSmsService#checkResponse is fail, error message  : {}", message);
      throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, message);
    }
    QuerySendDetailsResponseBodySmsSendDetailDTOs smsSendDetailDTOs = responseBody
        .getSmsSendDetailDTOs();
    if (smsSendDetailDTOs == null) {
      log.info("AliyunSmsService#checkResponse sms send details is null");
      throw new BizException(ReturnCodeEnum.SYSTEM_ERROR, "sms send details is null");
    }
    return smsSendDetailDTOs.getSmsSendDetailDTO();
  }

}
