package com.ruoyi.mall.util;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * App下单
 * 用户在商户APP端选择微信支付后，商户需调用该接口在微信支付下单，生成用于调起支付的预支付交易会话标识(prepay_id)。
 * https://pay.weixin.qq.com/doc/v3/merchant/4013070347
 */
public class AppPrepay {
  private static String HOST = "https://api.mch.weixin.qq.com";
  private static String METHOD = "POST";
  private static String PATH = "/v3/pay/transactions/app";

  public static DirectAPIv3AppPrepayResponse creatOrder(
          String description,String outTradeNo,Long total
        ) {
    // TODO: 请准备商户开发必要参数，参考：https://pay.weixin.qq.com/doc/v3/merchant/4013070756
    AppPrepay client = new AppPrepay(
        PayConstants.MCH_ID,                    // mchid 商户号，是由微信支付系统生成并分配给每个商户的唯一标识符，商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
        PayConstants.MCH_SERIAL_NO,         // certificateSerialNo 商户API证书序列号，如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
        PayConstants.PRIVATE_KEY_FILE_PATH,    // privateKeyFilePath 商户API证书私钥文件路径，本地文件路径
        PayConstants.PUBLIC_KEY,      // wechatPayPublicKeyId，如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
        PayConstants.PUBLIC_KEY_FILE_PATH          //  wechatPayPublicKeyFilePath 微信支付公钥文件路径，本地文件路径
    );

    CommonPrepayRequest request = new CommonPrepayRequest();
    request.appid = PayConstants.APP_ID;//必填
    request.mchid =  PayConstants.MCH_ID;//必填
    request.notifyUrl = PayConstants.NOTIFY_URL;//必填【商户回调地址】商户接收支付成功回调通知的地址，需按照notify_url填写注意事项规范填写。

    request.description = description;//必填 【商品描述】商品信息描述，不能超过127个字符。
    request.outTradeNo = outTradeNo;//必填【商户订单号】商户系统内部订单号，要求6-32个字符内，只能是数字、大小写字母_-|* 且在同一个商户号下唯一。
    request.amount = new CommonAmountInfo();//必填【订单金额】订单金额信息
    request.amount.total = total;//必填【总金额】 订单总金额，单位为分，整型。示例：1元应填写 100
//    request.timeExpire = "2018-06-08T10:34:56+08:00";//选填 支付结束时间
//    request.attach = "自定义数据说明";//选填 创建订单时可传入自定义数据包，该数据对用户不可见，用于存储订单相关的商户自定义信息，其总长度限制在128字符以内。
//    request.goodsTag = "WXG";//选填【订单优惠标记】
//    request.supportFapiao = false;//选填 电子发票入口开放标识; 传入true时，支付成功消息和支付详情页将出现开票入口。
//    request.amount.currency = "CNY";//选填  货币类型;固定传：CNY，代表人民币。
//    request.detail = new CouponInfo();//选填 优惠功能
//    request.detail.costPrice = 1L;//选填 订单原价
//    request.detail.invoiceId = "wx123";//选填 商品小票ID
//    request.detail.goodsDetail = new ArrayList<>();//单品列表  选填
//    {
//      GoodsDetail item0 = new GoodsDetail();
//      item0.merchantGoodsId = "1246464644";//商户侧商品编码 必填
//      item0.wechatpayGoodsId = "1001";//微信支付商品编码
//      item0.goodsName = "iPhone6s 16G";//商品名称
//      item0.quantity = 1L;//商品数量 必填
//      item0.unitPrice = 528800L;//商品单价 必填
//      request.detail.goodsDetail.add(item0);
//    };
//    request.sceneInfo = new CommonSceneInfo();//选填  场景信息
//    request.sceneInfo.payerClientIp = "14.23.150.211";//必填 用户终端IP
//    request.sceneInfo.deviceId = "013467007045764";//选填 【商户端设备号】 商户端设备号（门店号或收银设备ID）
//    request.sceneInfo.storeInfo = new StoreInfo();//商户门店信息
//    request.sceneInfo.storeInfo.id = "0001";//必填 门店编号
//    request.sceneInfo.storeInfo.name = "腾讯大厦分店";//选填 门店名称
//    request.sceneInfo.storeInfo.areaCode = "440305";//选填 地区编码
//    request.sceneInfo.storeInfo.address = "广东省深圳市南山区科技中一道10000号";//选填 【详细地址】 详细的商户门店地址
//    request.settleInfo = new SettleInfo();// 选填 结算信息
//    request.settleInfo.profitSharing = false;//选填 分账标识
    DirectAPIv3AppPrepayResponse response =new DirectAPIv3AppPrepayResponse();
    try {
       response = client.run(request);

      // TODO: 请求成功，继续业务逻辑
      System.out.println(response);
      return response;
    } catch (WXPayUtility.ApiException e) {
      // TODO: 请求失败，根据状态码执行不同的逻辑
      e.printStackTrace();
    }
    return response;
  }

  public DirectAPIv3AppPrepayResponse run(CommonPrepayRequest request) {
    String uri = PATH;
    String reqBody = WXPayUtility.toJson(request);

    Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
    reqBuilder.addHeader("Accept", "application/json");
    reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
    reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody));
    reqBuilder.addHeader("Content-Type", "application/json");
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
    reqBuilder.method(METHOD, requestBody);
    Request httpRequest = reqBuilder.build();

    // 发送HTTP请求
    OkHttpClient client = new OkHttpClient.Builder().build();
    try (Response httpResponse = client.newCall(httpRequest).execute()) {
      String respBody = WXPayUtility.extractBody(httpResponse);
      if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
        // 2XX 成功，验证应答签名
        WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
                                      httpResponse.headers(), respBody);

        // 从HTTP应答报文构建返回数据
        return WXPayUtility.fromJson(respBody, DirectAPIv3AppPrepayResponse.class);
      } else {
        throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
      }
    } catch (IOException e) {
      throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
    }
  }

  private final String mchid;
  private final String certificateSerialNo;
  private final PrivateKey privateKey;
  private final String wechatPayPublicKeyId;
  private final PublicKey wechatPayPublicKey;

  public AppPrepay(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
    this.mchid = mchid;
    this.certificateSerialNo = certificateSerialNo;
    this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
    this.wechatPayPublicKeyId = wechatPayPublicKeyId;
    this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
  }

  public static class SettleInfo {
    @SerializedName("profit_sharing")
    public Boolean profitSharing;
  }

  public static class CommonAmountInfo {
    @SerializedName("total")
    public Long total;

    @SerializedName("currency")
    public String currency;
  }

  public static class CommonSceneInfo {
    @SerializedName("payer_client_ip")
    public String payerClientIp;

    @SerializedName("device_id")
    public String deviceId;

    @SerializedName("store_info")
    public StoreInfo storeInfo;
  }

  public static class DirectAPIv3AppPrepayResponse {
    @SerializedName("prepay_id")
    public String prepayId;
  }

  public static class GoodsDetail {
    @SerializedName("merchant_goods_id")
    public String merchantGoodsId;

    @SerializedName("wechatpay_goods_id")
    public String wechatpayGoodsId;

    @SerializedName("goods_name")
    public String goodsName;

    @SerializedName("quantity")
    public Long quantity;

    @SerializedName("unit_price")
    public Long unitPrice;
  }

  public static class CouponInfo {
    @SerializedName("cost_price")
    public Long costPrice;

    @SerializedName("invoice_id")
    public String invoiceId;

    @SerializedName("goods_detail")
    public List<GoodsDetail> goodsDetail;
  }

  public static class StoreInfo {
    @SerializedName("id")
    public String id;

    @SerializedName("name")
    public String name;

    @SerializedName("area_code")
    public String areaCode;

    @SerializedName("address")
    public String address;
  }

  public static class CommonPrepayRequest {
    @SerializedName("appid")
    public String appid;

    @SerializedName("mchid")
    public String mchid;

    @SerializedName("description")
    public String description;

    @SerializedName("out_trade_no")
    public String outTradeNo;

    @SerializedName("time_expire")
    public String timeExpire;

    @SerializedName("attach")
    public String attach;

    @SerializedName("notify_url")
    public String notifyUrl;

    @SerializedName("goods_tag")
    public String goodsTag;

    @SerializedName("support_fapiao")
    public Boolean supportFapiao;

    @SerializedName("amount")
    public CommonAmountInfo amount;

    @SerializedName("detail")
    public CouponInfo detail;

    @SerializedName("scene_info")
    public CommonSceneInfo sceneInfo;

    @SerializedName("settle_info")
    public SettleInfo settleInfo;
  }
}
