/**
 * 
 * 该sdk对应主工程4.x.x版本，业务逻辑详见业务流程图
 */

var MD5 = require("/utils/md5.min.js");

//base url
var BASE_URL = "https://paytest.miyapay.com/miya/miyapay_response.action";

//条码支付（被扫）
var TYPE_BARCODE_PAY = 0;

//刷脸支付
var TYPE_FACE_PAY = 1;

//轮询最大次数
var REQUEST_MAX_SIZE = 12;

/**
 * 支付渠道
 */
var PAY_WAY = [
  { key: "1", value: "微信" },
  { key: "3", value: "支付宝" },
  { key: "4", value: "百度" },
  { key: "5", value: "翼支付" },
  { key: "6", value: "QQ钱包" },
  { key: "9", value: "微众有折" },
  { key: "C", value: "京东" },
  { key: "K", value: "工行" },
  { key: "N", value: "银联钱包" },
  { key: "U", value: "移动和包" },
];

/**
 * 获取支付渠道
 */
function getPayWay(payWayCode) {
  for (var i = 0; i < PAY_WAY.length; i++) {
    if (payWayCode == PAY_WAY[i].key) {
      return PAY_WAY[i].value;
    }
  }
  return "未知";
}

/**
 * 查单
 */
function checkOrderState({ baseUrl, saasid, marketId, posId, cashierCode, version, orderNo, signKey, requestCount, callback }) {
  //首先检查baseUrl
  if (baseUrl == null || baseUrl.trim().length == 0) {
    //直接失败
    var response = { code: {}, data: {} };
    //-1代表支付失败
    response.code = -1;
    response.data = "支付地址未配置";
    callback(response);
    return;
  }
  //如果超过轮询次数
  if (requestCount != null) {
    if (requestCount <= 0) {
      if (callback != null) {
        var response = { code: {}, data: {} };
        response.code = -1;
        response.data = "超过轮询次数";
        callback(response);
        //撤单一次，不管结果
        orderCancel({ saasid, marketId, posId, cashierCode, version, orderNo, signKey, callback: null });
        return;
      }
    }
  } else {
    //没有传入请求次数时，自造一个
    requestCount = 0;
  }

  var checkOrderRequest = {
    request: {
    },
    data: {
    }
  };
  //A1 固定传“A”
  checkOrderRequest.request.A1 = "A";
  //A2 saasid
  if (saasid != null) {
    checkOrderRequest.request.A2 = saasid;
  }
  //A3 marketId
  if (marketId != null) {
    checkOrderRequest.request.A3 = marketId;
  }
  //A4 posId
  if (posId != null) {
    checkOrderRequest.request.A4 = posId;
  }
  //A5 cashierCode
  if (cashierCode != null) {
    checkOrderRequest.request.A5 = cashierCode;
  }
  //A6 固定传“B”
  checkOrderRequest.request.A6 = "B";
  //A7 默认传“1.5”
  if (version != null) {
    checkOrderRequest.request.A7 = version;
  } else {
    checkOrderRequest.request.A7 = "1.5";
  }

  //B1 orderNo
  if (orderNo != null) {
    checkOrderRequest.data.B1 = orderNo;
  }

  //A8 加签
  checkOrderRequest.request.A8 = getSign(checkOrderRequest, signKey);

  //请求
  var checkOrderRequestXmlStr = "<xml>" + json2xml(checkOrderRequest) + "</xml>";
  console.log("checkOrderRequestXmlStr==>" + checkOrderRequestXmlStr);
  my.request({
    headers: {
      'content-type': 'application/xml'
    },
    method: 'POST',
    url: baseUrl,
    dataType: 'text',
    timeout: 15000,
    data: checkOrderRequestXmlStr,
    success: (res) => {
      console.log("轮询查单：request success==>" + JSON.stringify(res));
      //解析
      var payResponse = change2PayResponse(res.data);
      if (payResponse.C1 == "SUCCESS" && payResponse.C2 == "PAYSUCCESS") {
        //支付成功
        if (callback != null) {
          var response = { code: {}, data: {} };
          response.code = 0;
          response.data = payResponse;
          callback(response);
        }
      } else if (payResponse.C1 == "SUCCESS" && payResponse.C2 == "PAYWAIT") {
        //继续轮询
        console.log("轮询查单：支付等待中，轮询剩余count==>" + (requestCount - 1));
        //失败后轮询查单
        sleep(5000);
        requestCount = requestCount - 1;
        checkOrderState({
          baseUrl, saasid, marketId, posId, cashierCode, version, orderNo, signKey, requestCount, callback
        });
      } else {
        console.log("轮询查单：支付失败==>" + payResponse.C4);
        //直接提示失败
        if (callback != null) {
          var response = { code: {}, data: {} };
          response.code = -1;
          response.data = payResponse.C4 + "";
          callback(response);
        }
      }
    },
    fail: (res) => {
      console.log("轮询查单：request fail==>" + JSON.stringify(res) + ";剩余请求count==>" + (requestCount - 1));
      //失败后轮询
      sleep(5000);
      requestCount = requestCount - 1;
      checkOrderState({
        baseUrl, saasid, marketId, posId, cashierCode, version, orderNo, signKey, requestCount, callback
      });
    }
  });
}

/**
 * 撤单
 */
function orderCancel({ baseUrl, saasid, marketId, posId, cashierCode, version, orderNo, signKey, callback }) {
  //首先检查baseUrl
  if (baseUrl == null || baseUrl.trim().length == 0) {
    //直接失败
    var response = { code: {}, data: {} };
    //-1代表支付失败
    response.code = -1;
    response.data = "支付地址未配置";
    callback(response);
    return;
  }
  var cancelRequest = {
    request: {
    },
    data: {
    }
  };
  //A1 固定传“A”
  cancelRequest.request.A1 = "A";
  //A2 saasid
  if (saasid != null) {
    cancelRequest.request.A2 = saasid;
  }
  //A3 marketId
  if (marketId != null) {
    cancelRequest.request.A3 = marketId;
  }
  //A4 posId
  if (posId != null) {
    cancelRequest.request.A4 = posId;
  }
  //A5 cashierCode
  if (cashierCode != null) {
    cancelRequest.request.A5 = cashierCode;
  }
  //A6 固定传“E”
  cancelRequest.request.A6 = "E";
  //A7 默认传“1.5”
  if (version != null) {
    cancelRequest.request.A7 = version;
  } else {
    cancelRequest.request.A7 = "1.5";
  }

  //B1 orderNo
  if (orderNo != null) {
    cancelRequest.data.B1 = orderNo;
  }
  //A8 加签
  cancelRequest.request.A8 = getSign(cancelRequest, signKey);

  //请求
  var cancelRequestXmlStr = "<xml>" + json2xml(cancelRequest) + "</xml>";
  console.log("cancelRequestXmlStr==>" + cancelRequestXmlStr);
  my.request({
    headers: {
      'content-type': 'application/xml'
    },
    method: 'POST',
    url: baseUrl,
    dataType: 'text',
    timeout: 15000,
    data: cancelRequestXmlStr,
    success: (res) => {
      console.log("撤单：request success==>" + JSON.stringify(res));
      //解析
      var payResponse = change2PayResponse(res.data);
      var response = { code: {}, data: {} };
      response.code = 0;
      response.data = payResponse;
      if (callback != null) {
        callback(response);
      }
    },
    fail: (res) => {
      console.log("撤单：request fail==>" + JSON.stringify(res));
      var response = { code: {}, data: {} };
      response.code = -1;
      response.data = decodeErrorMessage(res.error);
      if (callback != null) {
        callback(response);
      }
    }
  });
}

function decodeErrorMessage(error) {
  if (error == null) {
    return "请求失败";
  }
  switch (error) {
    case "11": return "无权跨域";
    case "12": return "网络出错";
    case "13": return "请求超时";
    case "14": return "解码失败";
    case "19": return "HTTP错误";
    case "20": return "请求已被停止/服务端限流";
  }
  return "请求失败";
}

/**
 * 发起支付
 */
function requestPay({ baseUrl, type, saasid, marketId, posId, cashierCode, version, orderNo, token, payPrice, paymentInfoShowMsg, signKey, requestCount, goodsDetailList, callback }) {
  //首先检查baseUrl
  if (baseUrl == null || baseUrl.trim().length == 0) {
    //直接失败
    var response = { code: {}, data: {} };
    //-1代表支付失败
    response.code = -1;
    response.data = "支付地址未配置";
    callback(response);
    return;
  }
  //如果超过轮询次数
  if (requestCount != null) {
    if (requestCount <= 0) {
      if (callback != null) {
        var response = { code: {}, data: {} };
        //-1代表支付失败
        response.code = -1;
        response.data = "轮询超过最大次数";
        callback(response);
        return;
      }
    }
  } else {
    //没有传入请求次数时，自造一个
    requestCount = 0;
  }

  var payRequest = {
    request: {
    },
    data: {
    }
  };
  var payRequestXmlStr;



  //A1 固定传“A”
  payRequest.request.A1 = "A";
  //A2 saasid
  if (saasid != null) {
    payRequest.request.A2 = saasid;
  }
  //A3 marketId
  if (marketId != null) {
    payRequest.request.A3 = marketId;
  }
  //A4 posId
  if (posId != null) {
    payRequest.request.A4 = posId;
  }
  //A5 cashierCode
  if (cashierCode != null) {
    payRequest.request.A5 = cashierCode;
  }
  //A6 固定传“A”
  payRequest.request.A6 = "A";
  //A7 默认传“1.5”
  if (version != null) {
    payRequest.request.A7 = version;
  } else {
    payRequest.request.A7 = "1.5";
  }

  //A10 支付类型
  if (type == TYPE_BARCODE_PAY) {
    //条码付 
    payRequest.request.A10 = "36";
  } else if (type == TYPE_FACE_PAY) {
    //条码刷脸付
    payRequest.request.A10 = "36";
  }

  //B1 orderNo
  if (orderNo != null) {
    payRequest.data.B1 = orderNo;
  }
  //B2 token，扫码付是付款条码，刷脸付是faceToken
  if (token != null) {
    payRequest.data.B2 = token;
  }
  //B3 描述信息
  payRequest.data.B3 = "蜻蜓IOT支付";

  //B4 支付金额，分
  if (payPrice != null) {
    payRequest.data.B4 = payPrice;
  }

  //B5 商品明细
  if (goodsDetailList != null) {
    payRequest.data.B5 = goodsDetailList;
  }

  //B20 支付类型（固定传IOT-DEVICE）
  payRequest.data.B20 = "IOT-DEVICE";

  //A8 加签
  payRequest.request.A8 = getSign(payRequest, signKey);

  //请求
  payRequestXmlStr = "<xml>" + json2xml(payRequest) + "</xml>";
  console.log("payRequestXmlStr==>" + payRequestXmlStr);
  my.request({
    headers: {
      'content-type': 'application/xml'
    },
    method: 'POST',
    url: baseUrl,
    dataType: 'text',
    timeout: 15000,
    data: payRequestXmlStr,
    success: (res) => {
      console.log("request success==>" + JSON.stringify(res));
      //解析
      var payResponse = change2PayResponse(res.data);
      //支付成功
      if (payResponse.C1 == "SUCCESS" && payResponse.C2 == "PAYSUCCESS") {
        console.log("支付成功==>" + JSON.stringify(payResponse));
        //支付成功
        if (callback != null) {
          var response = { code: {}, data: {} };
          //0代表支付成功
          response.code = 0;
          response.data = payResponse;
          callback(response);
        }
      } else if (payResponse.C1 == "SUCCESS" && payResponse.C2 == "PAYWAIT") {
        //继续轮询
        console.log("支付等待中，去查单");
        checkOrderState({ baseUrl, saasid, marketId, posId, cashierCode, version, orderNo, signKey, requestCount: REQUEST_MAX_SIZE, callback });
      } else {
        //支付失败，直接回调
        console.log("支付失败");
        if (callback != null) {
          var response = { code: {}, data: {} };
          //-1代表支付失败
          response.code = -1;
          if (payResponse.C4 != null && payResponse.C4.trim() != "") {
            response.data = payResponse.C4 + "";
          } else {
            response.data = "支付失败";
          }
          callback(response);
        }
      }
    },
    fail: (res) => {
      console.log("request fail==>" + JSON.stringify(res) + ";count==>" + requestCount);
      //失败后立即查单一次
      checkOrderState({ baseUrl, saasid, marketId, posId, cashierCode, version, orderNo, signKey, requestCount: 1, callback })
    }
  });
}

/**
 * 退款
 */
function refund({ baseUrl, saasid, marketId, posId, cashierCode, version, orderNo, refundNo, refundPrice, signKey, goodsDetailList, callback }) {
  //首先检查baseUrl
  if (baseUrl == null || baseUrl.trim().length == 0) {
    //直接失败
    var response = { code: {}, data: {} };
    //-1代表支付失败
    response.code = -1;
    response.data = "支付地址未配置";
    callback(response);
    return;
  }
  var refundRequest = {
    request: {
    },
    data: {
    }
  };
  var refundRequestXmlStr;

  //A1 接口类型，固定传“A”
  refundRequest.request.A1 = "A";
  //A2 米雅侧商户号
  if (saasid != null) {
    refundRequest.request.A2 = saasid;
  }
  //A3 门店号
  if (marketId != null) {
    refundRequest.request.A3 = marketId;
  }
  //A4 设备号（POS机编号）
  if (posId != null) {
    refundRequest.request.A4 = posId;
  }
  //A5 收银员编号
  if (cashierCode != null) {
    refundRequest.request.A5 = cashierCode;
  }
  //A6 操作类型，固定传“C”，代表退款
  refundRequest.request.A6 = "C";
  //A7 版本号 默认传“1.5”
  if (version != null) {
    refundRequest.request.A7 = version;
  } else {
    refundRequest.request.A7 = "1.5";
  }

  //B1 原商户订单号
  if (orderNo != null) {
    refundRequest.data.B1 = orderNo;
  }
  //B2 退款单号 同笔订单内不可重复
  if (refundNo != null) {
    refundRequest.data.B2 = refundNo;
  }
  //B4 退款金额 单位：分
  if (refundPrice != null) {
    refundRequest.data.B4 = refundPrice;
  }
  //B5 商品信息
  if (goodsDetailList != null) {
    refundRequest.data.B5 = goodsDetailList;
  }

  //A8 签名
  refundRequest.request.A8 = getSign(refundRequest, signKey);

  refundRequestXmlStr = "<xml>" + json2xml(refundRequest) + "</xml>";
  console.log("refundRequestXmlStr==>" + refundRequestXmlStr);
  my.request({
    headers: {
      'content-type': 'application/xml'
    },
    method: 'POST',
    url: baseUrl,
    dataType: 'text',
    timeout: 15000,
    data: refundRequestXmlStr,
    success: (res) => {
      console.log("request success==>" + JSON.stringify(res));
      //解析
      var refundResponse = change2PayResponse(res.data);
      if (refundResponse.C2 == "REFUNDSUCCESS") {
        //退款成功
        if (callback != null) {
          var response = { code: {}, data: {} };
          response.code = 0;
          response.data = refundResponse;
          callback(response);
        }
      } else {
        //退款失败
        if (callback != null) {
          var response = { code: {}, data: {} };
          response.code = -1;
          if (refundResponse.C4 != null) {
            response.data = refundResponse.C4;
          } else {
            response.data = "退款失败";
          }
          callback(response);
        }
      }
    },
    fail: (res) => {
      console.log("request failed==>" + JSON.stringify(res));
      //提示退款失败
      if (callback != null) {
        var response = { code: {}, data: {} };
        response.code = -1;
        response.data = "退款失败：" + decodeErrorMessage(res.error);
        callback(response);
      }
    }
  });
}

function json2xml(obj) {
  var xmlStr = "";
  // console.log("keys==>" + Object.getOwnPropertyNames(obj));
  var len = Object.getOwnPropertyNames(obj).length;
  if (Object.getOwnPropertyNames(obj)[len - 1] == 'length') {
    //注意，遇到特殊字符时，需转义
    var objStr = obj + "";
    //首先替换掉&符号
    var objStrNew = objStr.replace(/&/g, "&amp;").replace(/\"/g, "&quot;").replace(/\'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    return xmlStr = xmlStr + objStrNew;
  }
  for (const [key, value] of Object.entries(obj)) {
    // console.log("key==>" + key + ";value==>" + value);
    xmlStr = xmlStr + "<" + key + ">" + json2xml(value);
    xmlStr = xmlStr + "</" + key + ">";
  }
  // console.log("xmlStr==>" + xmlStr);
  return xmlStr;
}

function change2PayResponse(source) {
  var payResponse = {
  };
  payResponse.C1 = findElement(source, "C1");
  payResponse.C2 = findElement(source, "C2");
  payResponse.C3 = findElement(source, "C3");
  payResponse.C4 = findElement(source, "C4");
  payResponse.C5 = findElement(source, "C5");
  payResponse.C6 = findElement(source, "C6");
  payResponse.C7 = findElement(source, "C7");
  payResponse.C8 = findElement(source, "C8");
  payResponse.C9 = findElement(source, "C9");
  payResponse.C10 = findElement(source, "C10");
  payResponse.C11 = findElement(source, "C11");
  payResponse.C12 = findElement(source, "C12");
  payResponse.C13 = findElement(source, "C13");
  payResponse.C14 = findElement(source, "C14");
  payResponse.C15 = findElement(source, "C15");
  payResponse.C19 = findElement(source, "C19");
  payResponse.C21 = findElement(source, "C21");
  payResponse.C23 = findElement(source, "C23");
  payResponse.C24 = findElement(source, "C24");
  payResponse.C25 = findElement(source, "C25");
  payResponse.C27 = findElement(source, "C27");
  return payResponse;

}

function findElement(source, tag) {
  var index1 = source.indexOf("<" + tag + ">");
  var index2 = source.indexOf("</" + tag + ">");
  return source.substr(index1 + tag.length + 2, index2 - index1 - tag.length - 2);
}

function sleep(numberMillis) {
  var now = new Date();
  var exitTime = now.getTime() + numberMillis;
  while (true) {
    now = new Date();
    if (now.getTime() > exitTime)
      return;
  }
}

/**
 * 加签名
 */
function getSign(request, signKey) {
  var targetStr = "";
  var sortAKeys = Object.keys(request.request);
  //排序
  sortAKeys.sort();
  //遍历
  sortAKeys.forEach(function (key) {
    if (key != "A8") {
      targetStr = targetStr + "&" + key + "=" + request.request[key];
    }
  });

  //排序
  var sortBKeys = Object.keys(request.data);
  sortBKeys.sort();
  //遍历
  sortBKeys.forEach(function (key) {
    targetStr = targetStr + "&" + key + "=" + request.data[key];

  });
  targetStr = targetStr + "&KEY=" + signKey;
  var signedStr = MD5(targetStr).toLocaleUpperCase();
  return signedStr;
}

function fen2Yuan(num) {
  if (typeof num !== "number" || isNaN(num)) return null;
  return (num / 100).toFixed(2);
}

function yuan2Fen(yuan) {
  return parseInt(yuan * 100);
}

module.exports.TYPE_BARCODE_PAY = TYPE_BARCODE_PAY;
module.exports.TYPE_FACE_PAY = TYPE_FACE_PAY;
module.exports.requestPay = requestPay;
module.exports.refund = refund;
module.exports.fen2Yuan = fen2Yuan;
module.exports.yuan2Fen = yuan2Fen;
module.exports.getPayWay = getPayWay;