package com.sonic.lion.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.api.services.androidpublisher.model.ProductPurchase;
import com.sonic.lion.channel.config.StripeConfig;
import com.sonic.lion.dao.PayCallChannelRecordDao;
import com.sonic.lion.domain.bo.WebhookBo;
import com.sonic.lion.domain.entity.PayCallChannelRecord;
import com.sonic.lion.domain.entity.UserSubscriptionNotify;
import com.sonic.lion.domain.enums.CallChannelStatus;
import com.sonic.lion.domain.input.ChannelWebhookInput;
import com.sonic.lion.domain.req.GoogleUploadReceiptReq;
import com.sonic.lion.domain.req.GoogleUploadReceiptReqV2;
import com.sonic.lion.enums.*;
import com.sonic.lion.service.*;
import com.sonic.lion.utils.AppleWebhookVerifier;
import com.sonic.lion.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.UUID;

@Slf4j
@Service
public class WebhookHandlerServiceImpl implements WebhookHandlerService {

    @Autowired
    private IapV2Service iapV2Service;
    @Autowired
    private GoogleService googleService;
    @Autowired
    private UserSubscriptionService userSubscriptionService;
    @Autowired
    private PayCallChannelRecordDao payCallChannelRecordDao;
    @Autowired
    private TradeHandler tradeHandler;
    @Autowired
    private GoogleUploadReceiptService googleUploadReceiptService;
    @Autowired
    private AppleWebhookVerifier appleWebhookVerifier;
    @Autowired
    private StripeServiceImpl stripeService;
    @Autowired
    private StripeConfig stripeConfig;
    @Autowired
    private StripeSubscribeService stripeSubscribeService;

    @Override
    public void googleHandler(GoogleUploadReceiptReqV2 req) {
        boolean exitsReceipt = googleUploadReceiptService.countRecepit(req.getReceipt());
        BizResultCode.EXISTS_ERROR.check(exitsReceipt, "1019", "duplicate  receipt");
        try {
            ProductPurchase productPurchase = googleService.googleCheck(req.getProductId(), req.getReceipt());
            GoogleUploadReceiptReq googleUploadReceiptReq = new GoogleUploadReceiptReq();
            googleUploadReceiptReq.setReceipt(req.getReceipt());
            googleUploadReceiptReq.setProductId(req.getProductId());
            googleUploadReceiptReq.setTradeNo(productPurchase.getObfuscatedExternalProfileId());
            googleService.uploadReceipt(googleUploadReceiptReq);
        } catch (Exception e) {
            log.error("上传Google上传收据异常：", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void appleHandler(HttpServletRequest request) throws Exception {
        String payload = getPayload(request);
        log.info("===> appleV2 webhook payload: {} ", payload);
        //入参验签处理
        boolean bl = appleWebhookVerifier.handleAppleWebhook(JSONObject.parseObject(payload).getString("signedPayload"));
        ToastResultCode.SYSTEM_EXCEPTION.check(!bl, "", "sign verifier error");
        //解出JWT中的body内容
        String jwtBody = JwtUtils.getJwtBody(payload);
        JSONObject jwtBodyJson = JSON.parseObject(jwtBody);
        JSONObject jwtBodyDataJson = jwtBodyJson.getJSONObject("data");
        //解析出交易原数据
        JSONObject signedTransactionInfoJson = JSON.parseObject(JwtUtils.getJwtBody(jwtBodyDataJson.getString("signedTransactionInfo")));
        //设置到data对象中去
        jwtBodyDataJson.put("transactionInfo", signedTransactionInfoJson);
        //获取自动续订的签名数据
        String signedRenewalInfoBody = JwtUtils.getJwtBody(jwtBodyDataJson.getString("signedRenewalInfo"));
        //获取自动续订商品ID
        String autoRenewProductId = null;
        if(StringUtils.isNoneBlank(signedRenewalInfoBody)) {
            JSONObject signedRenewalInfoJson = JSON.parseObject(signedRenewalInfoBody);
            //设置到data对象中去
            jwtBodyDataJson.put("renewalInfo", signedRenewalInfoJson);
            autoRenewProductId = signedRenewalInfoJson.getString("autoRenewProductId");
        }
        //移除掉两个签名数据
        jwtBodyDataJson.remove("signedTransactionInfo");
        jwtBodyDataJson.remove("signedRenewalInfo");
        //将数据放回去
        jwtBodyJson.put("data", jwtBodyDataJson);
        //获取通知类型，自动续订的产品id
        String notificationType = jwtBodyJson.getString("notificationType");

        log.info("===> appleV2 webhook json: {}, {}, {}", autoRenewProductId, notificationType, jwtBodyJson.toJSONString());

        Long notifyId = userSubscriptionService.addSubscriptionNotifyV2("", UUID.randomUUID().toString().replace("-", ""), jwtBodyJson.toJSONString(), payload, notificationType, UserSubscriptionNotify.Platform.IOS);
        try {
            //调用v2版本的方法处理回调
            iapV2Service.handSubscribeWebhook(notifyId, jwtBodyJson.toJSONString());
        } catch (Exception e) {
            log.error("===> appleV2 webhook handler error notifyId1 : " + notifyId, e);
        }
    }

    @Override
    public void stripePaymentHandler(String signature, String payload) {
        ChannelWebhookInput input = ChannelWebhookInput.builder()
                .signature(signature)
                .payload(payload)
                .build();
        //验签
        WebhookBo webhookBo = stripeService.webhookBeforeHandler(input, stripeConfig.getPaymentWebhookSecret());
        //处理交易
        String batchId = webhookBo == null ? null : webhookBo.getId();
        if (batchId == null) {
            log.error("===> stripePaymentHandler 不能在webhook中获取batchId");
            return;
        }
        PayCallChannelRecord record = payCallChannelRecordDao.selectByBatchId(batchId);
        if (record == null) {
            log.error("===> stripePaymentHandler 未找到调用记录:{}", batchId);
            return;
        }
        if (record.getStatus() == CallChannelStatus.SUCC) {
            log.info("===> stripePaymentHandler 渠道调用记录已是成功状态");
            return;
        }
        //处理账户 加款 流程
        tradeHandler.syncOrder(record);
        log.info("===> stripePaymentHandler end");
    }

    @Override
    public void stripePayoutHandler(String signature, String payload) {
        ChannelWebhookInput input = ChannelWebhookInput.builder()
                .signature(signature)
                .payload(payload)
                .build();
        //验签
        WebhookBo webhookBo = stripeService.webhookBeforeHandler(input, stripeConfig.getPayoutWebhookSecret());
        String batchId = webhookBo == null ? null : webhookBo.getId();
        if (batchId == null) {
            log.error("===> stripePayoutHandler 不能在webhook中获取batchId");
            return;
        }
        PayCallChannelRecord record = payCallChannelRecordDao.selectByBatchId(batchId);
        if (record == null) {
            log.error("===> stripePayoutHandler 未找到调用记录:{}", batchId);
            return;
        }
        if (record.getStatus() == CallChannelStatus.SUCC) {
            log.info("===> stripePayoutHandler 渠道调用记录已是成功状态");
            return;
        }
        //处理账户 扣款 流程
        tradeHandler.syncOrder(record);
        log.info("===> stripePayoutHandler end");
    }

    @Override
    public void stripeSubscriptionHandler(String signature, String payload) throws Exception {
        ChannelWebhookInput input = ChannelWebhookInput.builder()
                .signature(signature)
                .payload(payload)
                .build();
        //验签
        WebhookBo webhookBo = stripeService.webhookBeforeHandler(input, stripeConfig.getSubscriptionWebhookSecret());
        log.error("===> stripeSubscriptionHandler webhookBo : {}", webhookBo);
        //处理交易
        String batchId = webhookBo == null ? null : webhookBo.getId();
        if (batchId == null) {
            log.error("===> stripeSubscriptionHandler 不能在webhook中获取batchId");
            return;
        }
        //订阅通知消息入库
        Long notifyId = userSubscriptionService.addSubscriptionNotify(webhookBo.getId(), webhookBo.getCustomerId(), payload, webhookBo.getEventType(), UserSubscriptionNotify.Platform.STRIPE);
        //处理订阅表的基础数据处理
        stripeSubscribeService.subscriptionHandler(notifyId, webhookBo);
        log.info("===> stripeSubscriptionHandler end");
    }

    @Override
    public void stripeDisputeHandler(String signature, String payload) throws Exception {
        ChannelWebhookInput input = ChannelWebhookInput.builder()
                .signature(signature)
                .payload(payload)
                .build();
        //验签
        WebhookBo webhookBo = stripeService.webhookBeforeHandler(input, stripeConfig.getDisputeWebhookSecret());
        //处理交易
        String batchId = webhookBo == null ? null : webhookBo.getId();
        if (batchId == null) {
            log.error("===> stripeDisputeHandler 不能在webhook中获取batchId");
            return;
        }
        PayCallChannelRecord record = payCallChannelRecordDao.selectByBatchId(batchId);
        if (record == null) {
            log.error("===> stripeDisputeHandler 未找到调用记录:{}", batchId);
            return;
        }
        if (record.getStatus() == CallChannelStatus.SUCC) {
            log.info("===> stripeDisputeHandler 渠道调用记录已是成功状态");
            return;
        }
        //处理账户 争议 流程 FIXME

        log.info("===> stripeDisputeHandler end");
    }


    /**
     * 获取入参数据
     *
     * @param request
     * @return
     * @throws Exception
     */
    private String getPayload(HttpServletRequest request) throws Exception {
        String body;
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }
        body = stringBuilder.toString();
        return body;
    }
}
