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

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.resource.BytesResource;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.*;
import com.alipay.api.request.AntMerchantExpandIndirectZftCreateRequest;
import com.alipay.api.response.AntMerchantExpandIndirectZftCreateResponse;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.params.zftpay.ZftpayIsvParams;
import com.jeequan.jeepay.core.utils.ImageUtils;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.thirdparty.channel.zftpay.model.ReqEntity;
import com.jeequan.jeepay.thirdparty.channel.zftpay.model.ReqMethod;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;

@Slf4j
public class ZftpayKit {

    public static final String SUCCESS_CODE = "10000";

    public static JSONObject req(ZftpayIsvParams isvParams, JSONObject reqData) {
        AlipayConfig alipayConfig = getAlipayConfig(isvParams);

        log.warn("请求业务参数:{}", reqData);

        AlipayClient alipayClient = null;
        try {
            alipayClient = new DefaultAlipayClient(alipayConfig);
        } catch (AlipayApiException e) {
            log.error("获取直付通配置信息异常", e);
            throw new BizException("获取直付通配置信息异常");
        }

        AntMerchantExpandIndirectZftCreateRequest zftCreateRequest = new AntMerchantExpandIndirectZftCreateRequest();
        zftCreateRequest.setBizContent(reqData.toJSONString());

        try {
            AntMerchantExpandIndirectZftCreateResponse response = alipayClient.execute(zftCreateRequest);

            if (!response.isSuccess()) {
                throw new BizException(response.getSubMsg());
            }

            return (JSONObject) JSON.toJSON(response);
        } catch (Exception e) {
            log.info("直付通进件请求异常", e);
            throw new BizException(e.getMessage());
        }
    }

    private static AlipayConfig getAlipayConfig(ZftpayIsvParams isvParams) {
        AlipayConfig config = new AlipayConfig();
        config.setAlipayPublicKey(isvParams.getAlipayPublicKey());
        config.setPrivateKey(isvParams.getPrivateKey());
        config.setAppId(isvParams.getAppId());

        return config;
    }

    public static <T extends AlipayResponse> T req(ZftpayIsvParams isvParams, AlipayRequest<T> request) {
        AlipayConfig alipayConfig = getAlipayConfig(isvParams);

        log.info("直付通请求业务参数:{}", JSON.toJSONString(request.getBizModel()));

        AlipayClient alipayClient = null;
        try {
            alipayClient = new DefaultAlipayClient(alipayConfig);
        } catch (AlipayApiException e) {
            log.error("获取直付通配置信息异常", e);
            throw new BizException("获取直付通配置信息异常");
        }

        try {
            T response = alipayClient.execute(request);

            if (!response.isSuccess()) {
                throw new BizException(response.getSubMsg());
            }

            log.info("直付通请求参数打印: {}", JSON.toJSONString(response));

            return response;
        } catch (Exception e) {
            log.info("直付通进件请求异常", e);
            throw new BizException(e.getMessage());
        }
    }

    /**
     * 异步验签
     */
    public static void syncCheckSign(JSONObject params, ZftpayIsvParams isvParams, Charset charset) {


        //支付宝signType 不参与签名 TODO 回调通知签名和同步参数验签方式不一样
        String signContent;
        params.remove("sign_type");
        String sign = ((String) params.remove("sign"));
        if (ObjUtil.isEmpty(sign)) {
            // 没有签名就不验
            return;
        }

        signContent = JeepayKit.getStrSort(params);
        log.info("待验签数据, {}", signContent);
        Sign verify = SecureUtil.sign(SignAlgorithm.SHA256withRSA, isvParams.getPrivateKey(), isvParams.getAlipayPublicKey());
        boolean verifyResult = verify.verify(signContent.getBytes(charset), Base64.decode(sign));

        Assert.isTrue(verifyResult, "验签失败");
    }

    /**
     * 同步返回回调
     */
    public static void checkSign(String params, ZftpayIsvParams isvParams, String sign, Charset charset) {
        if (ObjUtil.isEmpty(sign)) {
            // 没有签名就不验
            return;
        }
        Sign verify = SecureUtil.sign(SignAlgorithm.SHA256withRSA, isvParams.getPrivateKey(), isvParams.getAlipayPublicKey());
        boolean verifyResult = verify.verify(params.getBytes(charset), Base64.decode(sign));

        Assert.isTrue(verifyResult, "验签失败");
    }

    /**
     * 发起请求
     *
     * @param method
     * @return
     */
    public static JSONObject reqUpload(ZftpayIsvParams isvParams, ReqMethod method, String fileUrl) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(param, httpHeaders);

        Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, isvParams.getPrivateKey(), isvParams.getAlipayPublicKey());

        ByteArrayOutputStream baos = ImageUtils.compressNetPic(fileUrl, 1024 * 1024 * 10);
        BytesResource bytesResource = new BytesResource(baos.toByteArray(), "test.png");

        HttpRequest httpRequest = HttpRequest.post(method.getUrl())
                .contentType(ContentType.MULTIPART.getValue());

        ReqEntity request = new ReqEntity(method, isvParams.getAppId());
        request.setImage_type("png");

        String encryptOriginData = JeepayKit.getStrSort(((JSONObject) JSON.toJSON(request)));
        request.setSign(DatatypeConverter.printBase64Binary(sign.sign(encryptOriginData)));

        // {"msg":"Success","code":"10000","order_id":"2024041700502000000075319698"}
        @Cleanup HttpResponse httpResponse = httpRequest.form("app_id", request.getApp_id())
                .form("method", request.getMethod())
                .form("format", request.getFormat())
                .form("charset", request.getCharset())
                .form("sign_type", request.getSign_type())
                .form("sign", request.getSign())
                .form("timestamp", request.getTimestamp())
                .form("version", request.getVersion())
                .form("image_content", bytesResource)
                .form("image_type", "png")
                .execute();

        if (!httpResponse.isOk()) {
            throw new BizException("直付通请求状态异常: statues = " + httpResponse.getStatus());
        }

        String respStr = httpResponse.body();
        log.info("直付通请求返回参数为：{}", respStr);
        JSONObject respData = JSON.parseObject(respStr);

        Assert.notNull(respData, "直付通请求返回数据异常");
        JSONObject bizData = respData.getJSONObject(method.getResponseKey());
        String code = bizData.getString("code");

        if (!SUCCESS_CODE.equals(code)) {
            throw new BizException(bizData.getString("msg") + bizData.getString("msg"));
        }

        return bizData;
    }
}
