package com.sonic.lion.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sonic.common.exception.SysException;
import com.sonic.common.rpc.GlobalResultCode;
import com.sonic.lion.enums.TradeStatus;
import com.sonic.lion.dao.UserSubscriptionNotifyDao;
import com.sonic.lion.domain.entity.*;
import com.sonic.lion.domain.enums.TradeEvent;
import com.sonic.lion.domain.req.GoogleUploadReceiptReq;
import com.sonic.lion.service.*;
import com.sonic.lion.enums.ToastResultCode;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.SecurityUtils;
import com.google.api.services.androidpublisher.AndroidPublisher;
import com.google.api.services.androidpublisher.AndroidPublisherScopes;
import com.google.api.services.androidpublisher.model.ProductPurchase;
import com.google.api.services.androidpublisher.model.SubscriptionPurchase;
import com.google.api.services.androidpublisher.model.VoidedPurchase;
import com.google.api.services.androidpublisher.model.VoidedPurchasesListResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author: code
 * @date: 2025/06/22
 * @Description: 不把Google内购这个奇葩当作支付渠道
 * @version: 1.0.0
 */
@Slf4j
@Service
public class GoogleServiceImpl implements GoogleService, InitializingBean {

    @Autowired
    private PayTradeService payTradeService;

    @Autowired
    private GoogleRecordService googleRecordService;

    @Autowired
    private PayChargeService payChargeService;

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Autowired
    private PayConfigService payConfigService;

    @Autowired
    private GoogleUploadReceiptService googleUploadReceiptService;

    @Autowired
    private UserSubscriptionService userSubscriptionService;

    @Value("${google.boundId}")
    private String BUNDLE_ID;

    @Value("${google.serviceAccountId}")
    private String serviceAccountId;

    @Value("${google.packageName}")
    private String packageName;

    @Value("${site.type}")
    private String siteType;

    private static final BigDecimal MINUS_ONE = new BigDecimal(-1);

    PrivateKey privateKey;

    @Autowired
    private UserSubscriptionNotifyDao userSubscriptionNotifyDao;

    @PostConstruct
    public void init() {
        try {
            ClassPathResource resource = new ClassPathResource(siteType + "/api-google.p12");
            privateKey = SecurityUtils.loadPrivateKeyFromKeyStore(
                    SecurityUtils.getPkcs12KeyStore(),
                    resource.getInputStream(), // 生成的P12文件
                    "notasecret", "privatekey", "notasecret");
        } catch (Exception e) {
            log.error("===> init google secret error : ", e);
            throw new SysException(GlobalResultCode.SYSTEM_EXCEPTION, e);
        }
    }

    @Override
    public Long calculatePaymentFee(@NonNull Long productAmount) {
        //蹩脚设计  为了兼容之前不传productId 版本。明明可以直接从productId取得
        String  googleFeeRatio =  payConfigService.getGoogleFeeRatio();
        BigDecimal number1 = new BigDecimal(googleFeeRatio.split("/")[0]);
        BigDecimal number2 = new BigDecimal(googleFeeRatio.split("/")[1]);
        return new BigDecimal(productAmount).multiply(number1).divide(number2, 0, BigDecimal.ROUND_HALF_UP).add(MINUS_ONE).longValue();
    }

    @Override
    public Long calculatePaymentFee(PayCharge payCharge) {
        return payCharge.getPayAmount() - payCharge.getChargeAmount();
    }

    @Override
    public void uploadReceipt(@NonNull GoogleUploadReceiptReq req) throws GeneralSecurityException, IOException {
        PayTrade payTrade = payTradeService.getByTradeNo(req.getTradeNo());
        if(TradeStatus.FINISHED.equals(payTrade.getStatus())){
            return ;
        }
        //先保存票据
        Map<String, String> transactions = new HashMap<>();
        transactions.put(req.getTradeNo(), req.getReceipt());
        GoogleUploadReceipt googleUploadReceipt = GoogleUploadReceipt.builder()
                .transactionsJsonStr(JSONObject.toJSONString(transactions))
                .receipt(req.getReceipt())
                .processed(Boolean.FALSE)
                .build();
        try {
            googleUploadReceiptService.save(googleUploadReceipt);
        } catch (DuplicateKeyException e) {
            log.warn("该google收据已保存");
            return;
        }

        req.setReceiptId(googleUploadReceipt.getId());
        boolean processed = processReceipt(req);
        //处理完成后更新处理状态
        googleUploadReceiptService.updateProcessed(googleUploadReceipt.getId(), processed);
    }

    @Override
    public void processReceipt() {
//        LocalDateTime startCreateTime = LocalDateTime.now().minusHours(24L);
//        List<GoogleUploadReceipt> list = googleUploadReceiptService.list(Boolean.FALSE, startCreateTime, 10);
//        for (GoogleUploadReceipt googleUploadReceipt : list) {
//            try {
//                log.info("google 收据补偿处理, receiptId: {}", googleUploadReceipt.getId());
//                JSONObject transactions = JSON.parseObject(googleUploadReceipt.getTransactionsJsonStr());
//                GoogleUploadReceiptReq req = GoogleUploadReceiptReq.builder()
//                        .receiptId(googleUploadReceipt.getId())
//                        .transactions(transactions)
//                        .receipt(googleUploadReceipt.getReceipt())
//                        .build();
//
//                boolean processed = processReceipt(req);
//                //处理完成后更新处理状态
//                googleUploadReceiptService.updateProcessed(googleUploadReceipt.getId(), processed);
//            } catch (Exception e) {
//                log.error("google收据处理失败, receiptId: {}", googleUploadReceipt.getId());
//            }
//        }
    }

    /**
     * 处理收据
     * @param req
     * @return 是否处理完成
     */
    private boolean processReceipt(GoogleUploadReceiptReq req) throws GeneralSecurityException, IOException {
        String tradeNo = req.getTradeNo();
        //找不到相应的tradeNo
        if (StringUtils.isBlank(tradeNo)) {
            return false;
        }
        ProductPurchase productPurchase = googleCheck(req.getProductId(), req.getReceipt());
        req.setTransactionId(productPurchase.getOrderId());
        //判断状态
        if (productPurchase.getConsumptionState() != 1 && productPurchase.getPurchaseState() != 0) {
            ToastResultCode.GOOGLE_TICKET_STATUS_ERROR.check(true);
            return false;
        }
        PayCharge payCharge = payChargeService.getByBundleIdAndProductIdAndPlatform(BUNDLE_ID, req.getProductId(), "android");
        PayTrade payTrade = payTradeService.getByTradeNo(tradeNo);
        if (!Objects.equals(payCharge.getPayAmount(), payTrade.getOccurAmount())) {
            log.error("付款金额不匹配");
            return false;
        }
        GoogleRecord iapRecord = googleRecordService.getByTransactionId(req.getTransactionId());
        if (iapRecord == null) {
            //事务
            TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
            try {
                GoogleRecord googleRecord = GoogleRecord.builder()
                        .tradeNo(tradeNo)
                        .transactionId(req.getTransactionId())
                        .receiptId(req.getReceiptId())
                        .bundleId(BUNDLE_ID)
                        .productId(req.getProductId())
                        .result(JSONObject.toJSONString(productPurchase))
                        .build();
                googleRecordService.save(googleRecord);

                //未处理的收据，可开始交易处理
                payTradeService.tradeHandle(payTrade, TradeEvent.PAYMENT);
                transactionManager.commit(transactionStatus);
            } catch (Exception e) {
                transactionManager.rollback(transactionStatus);
                log.error("google receipt process error", e);
            }
        }
        return true;
    }

    /**
     * 生产环境沙盒充值逻辑, 提审账号校验
     *
     * @param accountId
     */
    private void sandBoxChargeCheck(Long accountId) {
//        List<Long> reviewAccountList = payConfigService.listReviewAccount();
//        SysExceptionUtils.check(reviewAccountList == null || !reviewAccountList.contains(accountId), "", "生产环境校验沙盒环境收据，但交易发起账户不是iOS提审账户");
//        //提审账号最大可充值金额校验
//        BizExceptionUtils.check(payTradeService.sumChargeOccurAmount(accountId) > AccountConstant.REVIEW_ACCOUNT_MAX_CHARGE_AMOUNT, "", "The top-up amount on the day cannot exceed $1000.00");
    }

    @Override
    public ProductPurchase googleCheck(String productId, String token) throws GeneralSecurityException, IOException {
//        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10001));
//        HttpTransport httpTransport = new NetHttpTransport.Builder().setProxy(proxy).build();

        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        GoogleCredential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport).setJsonFactory(JacksonFactory.getDefaultInstance())
                .setServiceAccountId(serviceAccountId)
                .setServiceAccountScopes(AndroidPublisherScopes.all())
                .setServiceAccountPrivateKey(privateKey).build();

        AndroidPublisher publisher = new AndroidPublisher.Builder(httpTransport,
                JacksonFactory.getDefaultInstance(), credential).build();

        AndroidPublisher.Purchases.Products products = publisher.purchases().products();
        AndroidPublisher.Purchases.Products.Get product = products.get(packageName, productId, token);
        ProductPurchase purchase = product.execute();
        return purchase;
    }


    @Override
    public SubscriptionPurchase getSubscriptionPurchase(String productId, String token) throws GeneralSecurityException, IOException {
        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        GoogleCredential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport).setJsonFactory(JacksonFactory.getDefaultInstance())
                .setServiceAccountId(serviceAccountId)
                .setServiceAccountScopes(AndroidPublisherScopes.all())
                .setServiceAccountPrivateKey(privateKey).build();

        AndroidPublisher publisher = new AndroidPublisher.Builder(httpTransport,
                JacksonFactory.getDefaultInstance(), credential).build();

        AndroidPublisher.Purchases.Subscriptions sub = publisher.purchases().subscriptions();
        AndroidPublisher.Purchases.Subscriptions.Get get = sub.get(packageName,  productId,  token);
        SubscriptionPurchase subscriptionPurchase = get.execute();
        return subscriptionPurchase;
    }


    @Override
    public void handReceiptSubscribe(String productId, Long receiptId, Long userId) {
        //productId 变了就是升级了。
        try {
            GoogleUploadReceipt googleUploadReceipt  = googleUploadReceiptService.getById(receiptId) ;
            String receipt = googleUploadReceipt.getReceipt();
            SubscriptionPurchase subscriptionPurchase = getSubscriptionPurchase(productId, receipt);
            Long expiresDateMs = Long.valueOf(subscriptionPurchase.getExpiryTimeMillis());
            LocalDateTime expTime = LocalDateTime.ofEpochSecond(expiresDateMs/1000, 0, ZoneOffset.ofHours(8));
            String subscriptionId = subscriptionPurchase.getOrderId().substring(0, 24);
            userSubscriptionService.bind(subscriptionId, UserSubscription.Platform.GOOGLE,expTime,LocalDateTime.now() ,productId,userId,subscriptionPurchase.getLinkedPurchaseToken(),null);
            googleUploadReceiptService.updateProcessed(receiptId,true);
        }catch (Exception e){
            log.info("处理上传Google 订阅 凭据失败:{},{}", productId ,receiptId);
            log.error("处理上传Google 订阅 凭据失败",e);
        }
    }



    @Override
    public  List<VoidedPurchase>  voidedPurchases(LocalDateTime startTime) throws GeneralSecurityException, IOException {
        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        GoogleCredential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport).setJsonFactory(JacksonFactory.getDefaultInstance())
                .setServiceAccountId(serviceAccountId)
                .setServiceAccountScopes(AndroidPublisherScopes.all())
                .setServiceAccountPrivateKey(privateKey).build();
        AndroidPublisher publisher = new AndroidPublisher.Builder(httpTransport,
                JacksonFactory.getDefaultInstance(), credential).build();
        //获取google list对象
        AndroidPublisher.Purchases.Voidedpurchases.List voidPurchaseList = publisher.purchases().voidedpurchases().list(packageName);
        Long milliseconds = startTime.atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
        //设置查询参数
//        voidPurchaseList.setStartTime(milliseconds);
        //执行查询
        log.info("开始查询google退款:{}",milliseconds);
        VoidedPurchasesListResponse response = voidPurchaseList.execute();
        List<VoidedPurchase> voidedPurchases = response.getVoidedPurchases();
        log.info("google退款结果:{}", voidedPurchases);
        return voidedPurchases;
        /**
         *
         * ZG444444444444RT5	购买商品名称	购买数量	购买金额	购买时间	退款时间	用户ID	epal订单号	客户端（GP/苹果/web）
         * [{"kind":"androidpublisher#voidedPurchase","purchaseTimeMillis":"1635348172211",
         * "purchaseToken":"doibnmclnepnigcblgnodkgo.AO-J1OyK0N8BkSeDyyMpQeOG-Z7jcadCTMxkINqXVPZWcpl91YOqVkaOkWS4OqGl3WHk1XAXwx6P8DNQwMxLrmxXLGnM0ULr7g",
         * "voidedTimeMillis":"1636432172312","orderId":"GPA.3327-1510-4846-05509","voidedSource":0,"voidedReason":7}]
         */
    }

    /**
     *
     * https://developer.android.com/google/play/billing/subscriptions
     *
     * 订阅的 notificationType 可以具有以下值：
     * (1) SUBSCRIPTION_RECOVERED - 从帐号保留状态恢复了订阅。
     * (2) SUBSCRIPTION_RENEWED - 续订了处于活动状态的订阅。
     * (3) SUBSCRIPTION_CANCELED - 自愿或非自愿地取消了订阅。如果是自愿取消，在用户取消时发送。
     * (4) SUBSCRIPTION_PURCHASED - 购买了新的订阅。
     * (5) SUBSCRIPTION_ON_HOLD - 订阅已进入帐号保留状态（如果已启用）。
     * (6) SUBSCRIPTION_IN_GRACE_PERIOD - 订阅已进入宽限期（如果已启用）。
     * (7) SUBSCRIPTION_RESTARTED - 用户已通过 Play > 帐号 > 订阅重新激活其订阅（需要选择使用订阅恢复功能）。
     * (8) SUBSCRIPTION_PRICE_CHANGE_CONFIRMED - 用户已成功确认订阅价格变动。
     * (9) SUBSCRIPTION_DEFERRED - 订阅的续订时间点已延期。
     * (10) SUBSCRIPTION_PAUSED - 订阅已暂停。
     * (11) SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED - 订阅暂停计划已更改。
     * (12) SUBSCRIPTION_REVOKED - 用户在到期时间之前已撤消订阅。
     * (13) SUBSCRIPTION_EXPIRED - 订阅已到期。
     * @param notifyId
     * @param playload
     * @return
     * @throws Exception
     */
    @Override
    public boolean handSubscribeWebhook(Long notifyId, String type, String playload) throws Exception{
        doWhenStatusChange(notifyId,type, playload);
        doWhenPay(notifyId,type, playload);
        return false;
    }

    /**
     * 处理 google 状态变更。
     * @param nofityId
     * @param type
     * @param playload
     * @throws GeneralSecurityException
     * @throws IOException
     */
    private void doWhenStatusChange(Long nofityId,String type,String playload) throws GeneralSecurityException, IOException {
        String content = playload;
        JSONObject jsonObject = JSON.parseObject(content);
        JSONObject subscriptionNotification = jsonObject.getJSONObject("subscriptionNotification");
        String purchaseToken = subscriptionNotification.getString("purchaseToken");
        String productId = subscriptionNotification.getString("subscriptionId");

        //根据状态来判断是否续期
        boolean autoRenewing ="SUBSCRIPTION_RESTARTED".equals(type)|| "SUBSCRIPTION_RECOVERED".equals(type)
                || "SUBSCRIPTION_PURCHASED".equals(type) ||"SUBSCRIPTION_PRICE_CHANGE_CONFIRMED".equals(type)
                ||"SUBSCRIPTION_DEFERRED".equals(type) || "SUBSCRIPTION_RENEWED".equals(type);

        SubscriptionPurchase subscriptionPurchase = this.getSubscriptionPurchase(productId, purchaseToken);
        log.info("doWhenStatusChange nofityId:{}, subscriptionPurchase对象:{}" ,nofityId , subscriptionPurchase);
        String subscriptionId = subscriptionPurchase.getOrderId().substring(0, 24);

        //暂停状态变更的时候 取 接口的。
        if("SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED".equals(type)){
            autoRenewing = subscriptionPurchase.getAutoRenewing();
        }

        userSubscriptionService.updateStatusByThird(subscriptionId, UserSubscription.Platform.GOOGLE , productId,autoRenewing);

        if (!autoRenewing) {
            userSubscriptionService.cancel(subscriptionId, UserSubscription.Platform.GOOGLE, null);
        }
//        subscribeService.completeNotify(nofityId);
    }

    private void doWhenPay(Long nofityId,String type,String playload) throws GeneralSecurityException, IOException {

        if(!"SUBSCRIPTION_RENEWED".equals(type) && !"SUBSCRIPTION_CANCELED".equals(type) &&  !"SUBSCRIPTION_ON_HOLD".equals(type)) {
            return;
        }

        String content = playload;
        JSONObject jsonObject = JSON.parseObject(content);

        JSONObject subscriptionNotification = jsonObject.getJSONObject("subscriptionNotification");
        String purchaseToken = subscriptionNotification.getString("purchaseToken");
        String productId = subscriptionNotification.getString("subscriptionId");
        SubscriptionPurchase subscriptionPurchase = this.getSubscriptionPurchase(productId, purchaseToken);

        log.info("nofityId:{}, subscriptionPurchase对象:{}" ,nofityId , subscriptionPurchase);


        String subscriptionId = subscriptionPurchase.getOrderId().substring(0, 24);
        UserSubscription userSubscription = userSubscriptionService.getBySubscriptionId(subscriptionId);
        ToastResultCode.DATA_NOT_EXITS.check(userSubscription == null);
        userSubscriptionNotifyDao.setSubscriptionId(nofityId,subscriptionId);
        LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(subscriptionPurchase.getExpiryTimeMillis()/1000, 0, ZoneOffset.ofHours(8));
        userSubscriptionService.setPurchaseToken(userSubscription.getId() , purchaseToken);
        //处理续订
        if("SUBSCRIPTION_RENEWED".equals(type)) {
            //变更权益
            if(subscriptionPurchase.getPaymentState() ==1 ){
                userSubscriptionService.bind(subscriptionId, UserSubscription.Platform.GOOGLE,localDateTime, LocalDateTime.now(),productId,null,purchaseToken,null);
            }
        //处理取消
        }else if("SUBSCRIPTION_CANCELED".equals(type)){
            if(Boolean.FALSE.equals(subscriptionPurchase.getAutoRenewing())){
                userSubscriptionService.cancel(userSubscription.getSubscriptionId(), UserSubscription.Platform.GOOGLE,subscriptionPurchase.getExpiryTimeMillis());
            }
        //处理扣款失败
        }else if("SUBSCRIPTION_ON_HOLD".equals(type)){
            if(Boolean.FALSE.equals(subscriptionPurchase.getAutoRenewing())){
                userSubscriptionService.payFailSystemMessage(userSubscription.getSubscriptionId(), UserSubscription.Platform.GOOGLE);
            }
        }
        userSubscriptionService.completeNotify(nofityId);
    }

    @Override
    public void afterPropertiesSet() {

    }
}
