package com.jeequan.jeepay.thirdparty.channel.sxfpay;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.sxfpay.SxfpayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.ImageUtils;
import com.jeequan.jeepay.core.utils.JeepayRSA2Kit;
import com.jeequan.jeepay.thirdparty.channel.sxfpay.model.ReqEntity;
import com.jeequan.jeepay.thirdparty.channel.sxfpay.model.ReqMethod;
import com.jeequan.jeepay.thirdparty.channel.sxfpay.model.RespEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
 * TODO
 *
 * @author crystal
 * @date 2024/2/27 11:01
 */
@Slf4j
public class SxfpayKit {

    public static final String SUCCESS_CODE = "0000";

    public static final String EXT_SUCCESS_CODE = "2002";

    public static final String EXT_SUCCESS_CODE_1 = "00";

    /**
     * 设置支付通用参数
     *
     * @param bizContent
     * @param payOrder
     */
    public static void setPayParams(MchAppConfigContext mchAppConfigContext, JSONObject bizContent, PayOrder payOrder, UnifiedOrderRQ rq) {
        bizContent.put("mno", mchAppConfigContext.getMchApplyment().getChannelMchNo());
        bizContent.put("ordNo", payOrder.getPayOrderId());
        bizContent.put("payType", payOrder.getWayCodeType());
        bizContent.put("amt", AmountUtil.convertCent2Dollar(payOrder.getFindAmt()));
        bizContent.put("trmIp", "127.0.0.1");
        long between = DateUtil.between(payOrder.getCreatedAt(), payOrder.getExpiredTime(), DateUnit.MINUTE);
        bizContent.put("timeExpire", between);
        bizContent.put("subject", payOrder.getSubject());
        Integer limitPay = rq.getLimitPay();
        if (limitPay == null || limitPay == -1) {
            bizContent.put("limitPay", "00");
        } else {
            bizContent.put("limitPay", "01");
        }
        if (CS.PAY_WAY_CODE_TYPE.ALIPAY.equals(payOrder.getWayCodeType())) {
            Integer hbFqNum = rq.getHbFqNum();
            if (hbFqNum != null) {
                bizContent.put("hbFqNum", hbFqNum);
                bizContent.put("hbFqPercent", rq.getHbFqPercent());
            }
        }
    }


    /**
     * 发起请求 返回业务参数
     *
     * @param method
     * @param reqData
     * @return
     */
    public static JSONObject reqApi(ReqMethod.Method method, SxfpayIsvParams isvParams, JSONObject reqData) {
        ReqEntity reqEntity = new ReqEntity(isvParams.getOrgId(), reqData);
        initSign(reqEntity, isvParams);
        log.info("【随行付】请求参数:{}", JSON.toJSONString(reqEntity));
        HttpResponse response = HttpRequest.post(ReqMethod.getUrl(method)).body(JSON.toJSONString(reqEntity)).execute();
        if (!response.isOk()) {
            throw new BizException("请求失败，请求状态异常");
        }
        log.info("【随行付】响应参数:{}", response.body());
        JSONObject respBody = JSON.parseObject(response.body());
        if (respBody == null) {
            throw new BizException("请求成功，接口返回参数异常，接口无响应参数");
        }
        if (!checkSign(respBody, isvParams)) {
            throw new BizException("随行付返回参数验签失败");
        }
        RespEntity respEntity = respBody.toJavaObject(RespEntity.class);
        if (!SUCCESS_CODE.equals(respEntity.getCode())) {
            throw new BizException(respEntity.getMsg());
        }
        JSONObject respData = respEntity.getRespData();
        if (!SUCCESS_CODE.equals(respData.get("bizCode")) && !EXT_SUCCESS_CODE.equals(respData.get("bizCode")) && !EXT_SUCCESS_CODE_1.equals(respData.get("bizCode"))) {
            throw new BizException("["+respData.get("bizCode")+"]" + respData.getString("bizMsg"));
        }
        return respData;
    }

    /**
     * 发起请求 返回业务参数
     *
     * @param method
     * @return
     */
    public static String reqUpload(ReqMethod.Method method, SxfpayIsvParams isvParams, String imgUrl, String pictureType) {

        Assert.notNull(imgUrl, "缺少类型为{}的图片", pictureType);

        // 下载文件
        byte[] imgFileByteArray = null;
        try {
            ByteArrayOutputStream baos = ImageUtils.compressNetPic(imgUrl, 2048 * 1024);
            imgFileByteArray = baos.toByteArray();
        } catch (IORuntimeException e) {
            e.printStackTrace();
        }

        log.info("【随行付】上传图片:{}, 图片类型为{}", imgUrl, pictureType);
        HttpResponse response = HttpRequest.post(ReqMethod.getUrl(method))
                .form("orgId", isvParams.getOrgId())
                .form("reqId", IdUtil.fastUUID())
                .form("pictureType", pictureType)
                .form("file", imgFileByteArray, IdUtil.fastUUID() + ".jpg")
                .execute();
        if (!response.isOk()) {
            log.info("请求失败, {}", JSON.toJSONString(response));
            throw new BizException("请求失败，请求状态异常");
        }
        log.info("【随行付】响应参数:{}", response.body());
        JSONObject respBody = JSON.parseObject(response.body());
        if (respBody == null) {
            throw new BizException("请求成功，接口返回参数异常，接口无响应参数");
        }
        if (!checkSign(respBody, isvParams)) {
            throw new BizException("随行付返回参数验签失败");
        }
        RespEntity respEntity = respBody.toJavaObject(RespEntity.class);
        if (!SUCCESS_CODE.equals(respEntity.getCode())) {
            throw new BizException(respEntity.getMsg());
        }
        JSONObject respData = respEntity.getRespData();
        if (!SUCCESS_CODE.equals(respData.get("bizCode")) && !EXT_SUCCESS_CODE.equals(respData.get("bizCode"))) {
            throw new BizException(respData.getString("bizMsg"));
        }
        return respData.getString("PhotoUrl");
    }

    /**
     * 校验接口返回参数
     *
     * @param respBody
     */
    public static boolean checkSign(JSONObject respBody, SxfpayIsvParams isvParams) {
        try {
            //如果参数中签名值为空，则不校验签名
            String isSign = respBody.getString("sign");
            if(StringUtils.isEmpty(isSign)){
                return true;
            }
            String sign = (String)respBody.remove("sign");
            return JeepayRSA2Kit.verify(getStrSort(respBody), isvParams.getPublicKey(), sign);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取签名字符串
     * @param map
     * @return
     */
    public static String getStrSort(Map<String, Object> map) {
        ArrayList<String> list = new ArrayList<>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            list.add(entry.getKey() + "=" + entry.getValue() + "&");
        }
        int size = list.size();
        String[] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(arrayToSort[i]);
        }
        return sb.substring(0, sb.length() - 1);
    }

    /**
     * 初始化签名
     *
     * @param reqEntity
     * @param isvParams
     */
    public static void initSign(ReqEntity reqEntity, SxfpayIsvParams isvParams) {
        try {
            log.info("加签私钥，{}", isvParams.getPrivateKey());
            String sign = JeepayRSA2Kit.getSign(reqEntity.getSignContent(), isvParams.getPrivateKey());
            reqEntity.setSign(sign);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BizException("签名异常");
        }
    }

    /**
     * 响应接口参数
     *
     * @return
     */
    public static Object ok() {
        JSONObject result = new JSONObject();
        result.put("code", "success");
        result.put("msg", "成功");
        return result;
    }
}
