package com.sf.payment.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.sf.common.utils.SecurityUtils;
import com.sf.common.utils.SnowflakeIdWorker;
import com.sf.order.domain.OrderInfo;
import com.sf.order.service.IOrderInfoService;
import com.sf.payment.config.HuaweiPaymentConfig;
import com.sf.payment.constant.GoodsConstants;
import com.sf.payment.domain.*;
import com.sf.payment.service.IHuaweiPaymentService;
import com.sf.payment.utils.HuaweiTokenGenerator;
import com.sf.service.domain.GoodsMessages;
import com.sf.service.service.IGoodsMessagesService;
import com.sf.system.domain.UserMember;
import com.sf.system.service.IUserMemberService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.X509EncodedKeySpec;
import java.text.MessageFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * 功能描述:
 *
 * @author a_kun
 * @date 2024/4/12 10:21
 */
@Slf4j
@Service
public class HuaweiPaymentServiceImpl implements IHuaweiPaymentService {

    /**
     * token url to get the authorization
     */
    private static final String TOKEN_URL = "https://oauth-login.cloud.huawei.com/oauth2/v3/token";

    /**
     * 查询消耗型/非消耗型商品的订单最新状态。
     */
    private static final String ORDER_STATUS_QUERY_URL = "/order/harmony/v1/application/order/status/query";

    /**
     * 查询自动续期订阅商品的最新状态。
     */
    private static final String SUBSCRIPTION_STATUS_QUERY_URL = "/subscription/harmony/v1/application/subscription/status/query";

    /**
     * 站点信息。(中国)
     */
    private static final String ROOT_URL = "https://iap.cloud.huawei.com";

    private static final String PUBLIC_KEY = "PUBLIC_KEY";

    @Resource
    private IOrderInfoService orderInfoService;

    @Resource
    private IUserMemberService userMemberService;

    @Resource
    private IGoodsMessagesService goodsMessagesService;

    @Resource
    private HuaweiPaymentConfig huaweiPaymentConfig;

    @Resource
    private SnowflakeIdWorker snowflakeIdWorker;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void purchasesVerify(HuaweiPurchasesVerifyDTO verifyDTO) {
        // 待发放会员商品
        PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload;
        // 验证 TODO 证书验签官网未实现
        if (GoodsConstants.GOODS_TYPE_CONSUMABLE.equals(verifyDTO.getType())
                || GoodsConstants.GOODS_TYPE_NON_CONSUMABLE.equals(verifyDTO.getType())) {
            Assert.hasText(verifyDTO.getJwsPurchaseOrder(), "订单信息不能为空");
            // 消耗/非消耗商品购买验证
            consumablePurchasesVerify(verifyDTO.getJwsPurchaseOrder());
        } else if (GoodsConstants.GOODS_TYPE_AUTOMATIC_RENEWAL_SUBSCRIPTION.equals(verifyDTO.getType())) {
            Assert.hasText(verifyDTO.getJwsSubscriptionStatus(), "订单信息不能为空");
            // 订阅商品购买验证
            subscriptionPurchasesVerify(verifyDTO.getJwsSubscriptionStatus());
        } else {
            throw new IllegalArgumentException("商品类型错误！");
        }
    }

    private PurchaseOrderPayload subscriptionPurchasesVerify(String jwsSubscriptionStatus) {
        DecodedJWT decodedJWT = JWT.decode(jwsSubscriptionStatus);
        //String header = decodedJWT.getHeader();
        String payload = decodedJWT.getPayload();
        // 前面应该不是base64编码后的，官网说都是编码后的，但是解码会报错
        //String signature = decodedJWT.getSignature();
        String decodeAppPayload = new String(Base64Utils.decode(payload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        // String decodeHeader = new String(Base64Utils.decode(header.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        // String decodeSignature = new String(Base64Utils.decode(signature.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        SubGroupStatusPayload appSubGroupStatusPayload = JSON.parseObject(decodeAppPayload, SubGroupStatusPayload.class);
        SubscriptionStatus lastSubscriptionStatus = appSubGroupStatusPayload.getLastSubscriptionStatus();
        PurchaseOrderPayload lastPurchaseOrder = lastSubscriptionStatus.getLastPurchaseOrder();
        // pack the request body
        Map<String, String> bodyMap = new HashMap<>();
        bodyMap.put("purchaseToken", lastPurchaseOrder.getPurchaseToken());
        bodyMap.put("purchaseOrderId", lastPurchaseOrder.getPurchaseOrderId());
        // construct the Authorization in Header
        Map<String, String> headers = buildAuthorization(huaweiPaymentConfig.getAppId(), bodyMap);

        // 订阅状态查询
        String response = HttpUtil.createPost(ROOT_URL + SUBSCRIPTION_STATUS_QUERY_URL)
                .addHeaders(headers)
                .body(JSON.toJSONString(bodyMap))
                .execute().body();
        log.info("订单状态查询返回信息:{}", response);
        HuaweiQueryResponse huaweiQueryResponse = JSON.parseObject(response, HuaweiQueryResponse.class);
        if (!"0".equals(huaweiQueryResponse.getResponseCode())) {
            throw new RuntimeException("订单状态查询失败");
        }
        DecodedJWT huaweiQueryResponseDecodedJWT = JWT.decode(huaweiQueryResponse.getJwsSubGroupStatus());
        String huaweiQueryResponsepayload = huaweiQueryResponseDecodedJWT.getPayload();
        String decodeHuaweiQueryResponsepayload = new String(Base64Utils.decode(huaweiQueryResponsepayload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        SubGroupStatusPayload subGroupStatusPayload = JSON.parseObject(decodeHuaweiQueryResponsepayload, SubGroupStatusPayload.class);
        PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload = subGroupStatusPayload.getLastSubscriptionStatus().getLastPurchaseOrder();
        // 发货
        return delivery(lastPurchaseOrder, huaweiQueryResponsePurchaseOrderPayload);
    }

    /**
     * 发货
     * @param purchaseOrder
     * @param huaweiQueryResponsePurchaseOrderPayload
     * @return
     */
    private PurchaseOrderPayload delivery(PurchaseOrderPayload purchaseOrder, PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload) {
        Assert.isTrue(purchaseOrder.getPurchaseOrderId().equals(huaweiQueryResponsePurchaseOrderPayload.getPurchaseOrderId()), "订单不一致，发货失败！");
        if (2 == huaweiQueryResponsePurchaseOrderPayload.getFinishStatus()) {
            // 还未发货
            // 查询平台是否配置该商品
            // 查询平台是否配置该商品
            GoodsMessages goods = goodsMessagesService.selectGoodsMessagesByCode(huaweiQueryResponsePurchaseOrderPayload.getProductId());
            Assert.notNull(goods, "未配置此商品，请检查商品配置");
            Assert.isTrue(goods.getOriginalPrice().equals(huaweiQueryResponsePurchaseOrderPayload.getPrice()), "商品价格与订单价格不一致，请检查价格配置");
            // 创建完成订单
            createOrder(huaweiQueryResponsePurchaseOrderPayload, goods);
            // 发放会员权益
            distributeMembershipBenefits(huaweiQueryResponsePurchaseOrderPayload);
        } else {
            log.info("华为应用内支付订单已发货！{}", JSON.toJSONString(huaweiQueryResponsePurchaseOrderPayload));
        }
        return huaweiQueryResponsePurchaseOrderPayload;
    }

    private PurchaseOrderPayload consumablePurchasesVerify(String jwsPurchaseOrder) {
        DecodedJWT decodedJWT = JWT.decode(jwsPurchaseOrder);
        //String header = decodedJWT.getHeader();
        String payload = decodedJWT.getPayload();
        // 前面应该不是base64编码后的，官网说都是编码后的，但是解码会报错
        //String signature = decodedJWT.getSignature();
        String decodeAppPayload = new String(Base64Utils.decode(payload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        // String decodeHeader = new String(Base64Utils.decode(header.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        // String decodeSignature = new String(Base64Utils.decode(signature.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        PurchaseOrderPayload appPurchaseOrderPayload = JSON.parseObject(decodeAppPayload, PurchaseOrderPayload.class);
        // pack the request body
        Map<String, String> bodyMap = new HashMap<>();
        bodyMap.put("purchaseToken", appPurchaseOrderPayload.getPurchaseToken());
        bodyMap.put("purchaseOrderId", appPurchaseOrderPayload.getPurchaseOrderId());
        // construct the Authorization in Header
        Map<String, String> headers = buildAuthorization(huaweiPaymentConfig.getAppId(), bodyMap);

        // 订单状态查询
        String response = HttpUtil.createPost(ROOT_URL + ORDER_STATUS_QUERY_URL)
                .addHeaders(headers)
                .body(JSON.toJSONString(bodyMap))
                .execute().body();
        log.info("订单状态查询返回信息:{}", response);
        HuaweiQueryResponse huaweiQueryResponse = JSON.parseObject(response, HuaweiQueryResponse.class);
        if (!"0".equals(huaweiQueryResponse.getResponseCode())) {
            throw new RuntimeException("订单状态查询失败");
        }
        DecodedJWT huaweiQueryResponseDecodedJWT = JWT.decode(huaweiQueryResponse.getJwsPurchaseOrder());
        // String huaweiQueryResponseHeader = huaweiQueryResponseDecodedJWT.getHeader();
        String huaweiQueryResponsepayload = huaweiQueryResponseDecodedJWT.getPayload();
        // String huaweiQueryResponsesignature = huaweiQueryResponseDecodedJWT.getSignature();

        //String decodehuaweiQueryResponseHeader = new String(Base64Utils.decode(huaweiQueryResponseHeader.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        String decodehuaweiQueryResponsepayload = new String(Base64Utils.decode(huaweiQueryResponsepayload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        // String decodehuaweiQueryResponsesignature = new String(Base64Utils.decode(huaweiQueryResponsesignature.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);

        PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload = JSON.parseObject(decodehuaweiQueryResponsepayload, PurchaseOrderPayload.class);
        // 暂时只做简单验证
        return delivery(appPurchaseOrderPayload, huaweiQueryResponsePurchaseOrderPayload);
    }

    private void createOrder(PurchaseOrderPayload appPurchaseOrderPayload, GoodsMessages goods) {
        Long userId = SecurityUtils.getUserId();
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderNo(snowflakeIdWorker.nextId());
        orderInfo.setOrderStatus(6L);
        orderInfo.setPayType(1L);
        orderInfo.setPayChannel(2L);
        orderInfo.setOrderAmt(appPurchaseOrderPayload.getPrice());
        orderInfo.setPayAmt(appPurchaseOrderPayload.getPrice());
        orderInfo.setReallyAmt(appPurchaseOrderPayload.getPrice());
        orderInfo.setReceiveType(0L);
        orderInfo.setGoodsId(goods.getId());
        orderInfo.setPayTime(DateUtil.date(appPurchaseOrderPayload.getPurchaseTime()));
        orderInfo.setCreateUserId(userId);
        orderInfo.setUpdateUserId(userId);
        orderInfo.setOutOrderNo(appPurchaseOrderPayload.getPurchaseOrderId());
        orderInfo.setPayData(JSON.toJSONString(appPurchaseOrderPayload));
        orderInfo.setGoodsPrice(goods.getOriginalPrice());
        orderInfo.setGoodsType(goods.getGoodsType());
        orderInfo.setGoodsCode(goods.getGoodsCode());
        orderInfoService.insertOrder(orderInfo);

    }

    private void distributeMembershipBenefits(PurchaseOrderPayload purchaseOrderPayload) {
        // 发放会员权益
        Long userId = SecurityUtils.getUserId();
        UserMember userMember = userMemberService.selectUserMemberByUserId(userId);
        if (userMember == null) {
            // 添加会员信息
            boolean isSubscription = GoodsConstants.GOODS_TYPE_AUTOMATIC_RENEWAL_SUBSCRIPTION.equals(purchaseOrderPayload.getProductType());
            DateTime payTime = DateUtil.date(purchaseOrderPayload.getPurchaseTime());
            userMember = new UserMember();
            userMember.setMemberLevel(isSubscription ? 1 : 2);
            userMember.setSubscriptionStatus(isSubscription ? 1 : 0);
            userMember.setUserId(userId);
            userMember.setIntegration(0L);
            DateTime expirationTime = DateUtil.offset(payTime, DateField.MONTH, 1);
            userMember.setExpirationTime(expirationTime);
            userMember.setCreateTime(new Date());
            userMember.setUpdateTime(new Date());
            userMemberService.insertUserMember(userMember);
        } else {
            // 更新
            DateTime expirationTime = DateUtil.offset(userMember.getExpirationTime(), DateField.MONTH, 1);
            userMember.setExpirationTime(expirationTime);
            userMember.setUpdateTime(new Date());
            userMemberService.updateUserMember(userMember);
        }
    }


    /**
     * Build Authorization in Header
     *
     * @return headers
     */
    public static Map<String, String> buildAuthorization(String appId, Map<String, String> body) {
        Map<String, Object> jwtHeader = new HashMap<>(8);
        jwtHeader.put("alg", "ES256");
        jwtHeader.put("typ", "JWT");
        jwtHeader.put("kid", "0ae3e1be-374b-43a5-a297-045addbf76eb");
        Map<String, Object> jwtPayload = new HashMap<>(8);
        jwtPayload.put("iss", "f59509e6-dd17-4644-b832-ff05233146c8");
        jwtPayload.put("aud", "iap-v1");
        jwtPayload.put("iat", DateUtil.currentSeconds());
        jwtPayload.put("exp", DateUtil.currentSeconds() + 1800L); // 半小时过期
        jwtPayload.put("aid", appId);
        jwtPayload.put("digest", getJwtPayloadDigest(body));

        String token = HuaweiTokenGenerator.createToken(jwtHeader, jwtPayload);
        // String authorization = MessageFormat.format("Basic {0}", Base64.encodeBase64String(oriString.getBytes(StandardCharsets.UTF_8)));
        String authorization = MessageFormat.format("Bearer {0}", token);
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", authorization);
        headers.put("Content-Type", "application/json; charset=UTF-8");
        return headers;
    }

    private static String getJwtPayloadDigest(Map<String, String> body) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(JSON.toJSONString(body).getBytes(StandardCharsets.UTF_8));
            byte[] digestByte = messageDigest.digest();
            StringBuilder stringBuffer = new StringBuilder();
            String temp;
            for (byte aByte : digestByte) {
                temp = Integer.toHexString(aByte & 0xFF);
                if (temp.length() == 1) {
                    stringBuffer.append("0");
                }
                stringBuffer.append(temp);
            }
            return stringBuffer.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 校验签名信息，校验InAppPurchaseData中的productId、price、currency等信息的一致性
     *
     * @param content   结果字符串
     * @param sign      签名字符串
     * @param publicKey IAP公钥
     * @return 是否校验通过
     */
    public static boolean checkSuccessOrder(String content, String sign, String publicKey, String signatureAlgorithm, OrderInfo orderInfo) {
        // 校验签名信息
        boolean checkRes = checkSign(content, sign, publicKey, StrUtil.blankToDefault(signatureAlgorithm, "SHA256WithRSA"));
        if (checkRes) {
            // 校验InAppPurchaseData中的productId、price、currency等信息的一致性
            checkRes = checkProductIdAndPriceAndCurrency(content, orderInfo);
        }
        return checkRes;
    }

    /**
     * 校验InAppPurchaseData中的productId、price、currency等信息的一致性
     *
     * @param content   结果字符串
     * @param orderInfo 您的订单信息，包括productId、price、currency
     * @return 是否校验通过
     */
    public static boolean checkProductIdAndPriceAndCurrency(String content, OrderInfo orderInfo) {
        InAppPurchaseData inAppPurchaseData = JSON.parseObject(content, InAppPurchaseData.class);
        // 校验InAppPurchaseData中的productId、price、currency等信息的一致性
        return inAppPurchaseData.getProductId().equals(orderInfo.getGoodsId())
                && inAppPurchaseData.getPrice().equals(orderInfo.getOrderAmt());
    }

    /**
     * 校验签名信息
     *
     * @param content            结果字符串
     * @param sign               签名字符串
     * @param publicKey          IAP公钥
     * @param signatureAlgorithm 签名算法字段，可从接口返回数据中获取，例如：OwnedPurchasesResult.getSignatureAlgorithm()
     * @return 是否校验通过
     */
    public static boolean checkSign(String content, String sign, String publicKey, String signatureAlgorithm) {
        if (sign == null) {
            return false;
        }
        if (publicKey == null) {
            return false;
        }

        // 当signatureAlgorithm为空时使用默认签名算法
        if (signatureAlgorithm == null || signatureAlgorithm.length() == 0) {
            signatureAlgorithm = "SHA256WithRSA";
            System.out.println("doCheck, algorithm: SHA256WithRSA");
        }
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            // 生成"RSA"的KeyFactory对象
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] decodedKey = Base64.decodeBase64(publicKey);
            // 生成公钥
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
            java.security.Signature signature = null;
            // 根据SHA256WithRSA算法获取签名对象实例
            signature = java.security.Signature.getInstance(signatureAlgorithm);
            // 初始化验证签名的公钥
            signature.initVerify(pubKey);
            // 把原始报文更新到签名对象中
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            // 将sign解码
            byte[] bsign = Base64.decodeBase64(sign);
            // 进行验签
            return signature.verify(bsign);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

}
