package com.sonic.lion.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.api.services.androidpublisher.model.VoidedPurchase;
import com.sonic.common.rpc.Page;
import com.sonic.daosupport.converter.PageConverter;
import com.sonic.lion.dao.AppleRefundRecordDao;
import com.sonic.lion.dao.PayCallChannelRecordDao;
import com.sonic.lion.dao.PayTradeDao;
import com.sonic.lion.domain.entity.*;
import com.sonic.lion.domain.enums.CallChannelStatus;
import com.sonic.lion.domain.input.ChannelRefundInput;
import com.sonic.lion.domain.output.ChannelRefundOutput;
import com.sonic.lion.domain.req.RefundQueryReq;
import com.sonic.lion.enums.BizType;
import com.sonic.lion.enums.PayChannel;
import com.sonic.lion.enums.TradeStatus;
import com.sonic.lion.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;

@Slf4j
@Service
public class RefundServiceImpl implements RefundService {

    @Autowired
    private GoogleService googleService;

    @Autowired
    private StripeServiceImpl stripeService;

    @Autowired
    private AppleRefundRecordDao appleRefundRecordDao;

    @Autowired
    private GoogleRecordService googleRecordService;

    @Autowired
    private PayTradeService payTradeService;

    @Autowired
    private PayCallChannelRecordDao payCallChannelRecordDao;

    @Autowired
    private AccountBuffService accountBuffService;

    @Autowired
    private PayTradeDao payTradeDao;

    @Autowired
    private PayChannelRouterService payChannelRouterService;

    @Autowired
    private ChannelBlacklistService channelBlacklistService;

    @Override
    public void pull(LocalDateTime startTime) {
        //从Google 拉取争议数据 并存储在数据库
        pullFromGoogle(startTime);
    }

    public void pullFromGoogle(LocalDateTime startTime) {
        try {
            List<VoidedPurchase> googleRefund = googleService.voidedPurchases(startTime);
            log.info("开始拉取google 退款数据,{},{}", startTime, googleRefund);
            for (VoidedPurchase voidedPurchase : googleRefund) {
                handleGoogleVoidedPurchaseDetail(voidedPurchase);
            }
        } catch (Exception e) {
            log.error("拉取google 退款数据失败", e);
            e.printStackTrace();
        }
    }

    private void handleGoogleVoidedPurchaseDetail(VoidedPurchase voidedPurchase) {
        try {
            String orderId = (String) voidedPurchase.get("orderId");

            Long cancellation_date_ms = Long.valueOf(voidedPurchase.get("voidedTimeMillis").toString());
            LocalDateTime cancellation_date = LocalDateTime.ofEpochSecond(cancellation_date_ms / 1000, 0, ZoneOffset.ofHours(8));

            Long purchase_date_ms = Long.valueOf(voidedPurchase.get("purchaseTimeMillis").toString());
            LocalDateTime purchase_date = LocalDateTime.ofEpochSecond(purchase_date_ms / 1000, 0, ZoneOffset.ofHours(8));

            AppleRefundRecord appleRefundRecord = AppleRefundRecord.builder()
                    .platform(AppleRefundRecord.Platform.GOOGLE)
                    .transactionId(orderId)
                    .cancellationDate(cancellation_date)
                    .purchaseDate(purchase_date).build();
            int count = appleRefundRecordDao.selectCount(Wrappers.<AppleRefundRecord>lambdaQuery()
                    .eq(AppleRefundRecord::getTransactionId, orderId));
            //已经存在的数据 不再处理
            if (count > 0) {
                return;
            }
            GoogleRecord googleRecord = googleRecordService.getByTransactionId(orderId);
            if (googleRecord != null) {
                appleRefundRecord.setProductId(googleRecord.getProductId());
                appleRefundRecord.setTradeNo(googleRecord.getTradeNo());
                PayTrade payTrade = payTradeService.getByTradeNo(googleRecord.getTradeNo());
                appleRefundRecord.setAmount(payTrade.getOccurAmount());
                appleRefundRecord.setUserId(payTrade.getSrcAccountId());
                appleRefundRecord.setBuff(payTrade.getAmount());
                appleRefundRecord.setContent(JSON.toJSONString(voidedPurchase));

                //开始处理 冻结
                AppleRefundRecord.FronzenStatus fronzenStatus = accountBuffService.fronzenBalanceAndSendMessage(payTrade.getSrcAccountId(), payTrade.getAmount());
                appleRefundRecord.setFronzenStatus(fronzenStatus);
                AccountBuff accountBuff = accountBuffService.getByUid(payTrade.getSrcAccountId());
                appleRefundRecord.setAfterFronzenBuff(accountBuff.getBalance());
                appleRefundRecord.setInsertTime(LocalDateTime.now());
                appleRefundRecordDao.insert(appleRefundRecord);
                //将用户加入渠道黑名单中
                channelBlacklistService.addBlacklist(appleRefundRecord.getUserId(), PayChannel.STRIPE.name());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Page<AppleRefundRecord> list(RefundQueryReq refundQueryReq) {
        LambdaQueryWrapper<AppleRefundRecord> queryWrapper = Wrappers.<AppleRefundRecord>lambdaQuery()
                .eq(refundQueryReq.getUserId() != null && refundQueryReq.getUserId() != 0, AppleRefundRecord::getUserId, refundQueryReq.getUserId())
                .eq(StringUtils.isNotBlank(refundQueryReq.getTradeNo()), AppleRefundRecord::getTradeNo, refundQueryReq.getTradeNo())
                .eq(StringUtils.isNotBlank(refundQueryReq.getTransactionId()), AppleRefundRecord::getTransactionId, refundQueryReq.getTransactionId())
                .gt(refundQueryReq.getStartTime() != null, AppleRefundRecord::getCancellationDate, refundQueryReq.getStartTime())
                .lt(refundQueryReq.getEndTime() != null, AppleRefundRecord::getCancellationDate, refundQueryReq.getEndTime())
                .orderByDesc(AppleRefundRecord::getCancellationDate);

        IPage<AppleRefundRecord> appleRefundRecordList = appleRefundRecordDao.selectPage(PageConverter.buildQueryPage(refundQueryReq.getPage()), queryWrapper);
        Page<AppleRefundRecord> pageResult = PageConverter.convert(appleRefundRecordList);
        return pageResult;
    }


    @Deprecated
    @Override
    public void paypalRefundByRecordId(Long recordId) {
        PayCallChannelRecord payCallChannelRecord = payCallChannelRecordDao.selectById(recordId);
        if (!payCallChannelRecord.getDisputed()) {
            return;
        }
        PayTrade payTrade = payTradeService.getByTradeNo(payCallChannelRecord.getTradeNo());
        if (payCallChannelRecord.getChannel().equals(PayChannel.STRIPE) && !payTrade.getStatus().equals(TradeStatus.REFUNDED)) {

            ChannelRefundInput channelRefundInput = new ChannelRefundInput();
            channelRefundInput.setAmount(payTrade.getOccurAmount());
            channelRefundInput.setTransactionId(payCallChannelRecord.getTransactionId());
            ChannelRefundOutput channelRefundOutput = stripeService.refund(channelRefundInput);
            if (channelRefundOutput.getStatus().equals(CallChannelStatus.SUCC)) {
                payCallChannelRecordDao.updateStatus(recordId, CallChannelStatus.CANCEL.getValue());
                payTradeService.updateStatus(payTrade.getId(), TradeStatus.REFUNDED, TradeStatus.PROCESSING);

                PayCallChannelRecord r = new PayCallChannelRecord();
                r.setChannel(PayChannel.STRIPE);
                r.setTradeNo(payTrade.getTradeNo());
                r.setBizType(BizType.REFUND);
                r.setBatchId(channelRefundOutput.getBatchId());
                r.setTransactionId(channelRefundOutput.getTransactionId());
                r.setResult(channelRefundOutput.getResult());
                r.setStatus(channelRefundOutput.getStatus());
                r.setAmount(channelRefundInput.getAmount());
                r.setPayerId("");
                r.setNextCheckTime(LocalDateTime.now());
                r.setLastCheckTime(LocalDateTime.now());
                r.setCreateTime(LocalDateTime.now());
                r.setEditTime(LocalDateTime.now());
                r.setCheckNum(0);
                payCallChannelRecordDao.insert(r);

            }
            log.info("发生争议而且 未入账 已成功退款,{}", payCallChannelRecord);
        }
    }


    @Override
    public void tryRefund(PayTrade payTrade, PayCallChannelRecord payCallChannelRecord) {
        if (!payTrade.getStatus().equals(TradeStatus.REFUNDED) && !payTrade.getStatus().equals(TradeStatus.FINISHED) && !payTrade.getStatus().equals(TradeStatus.PAID)) {
            //如果不是 已经成功付款 已经成功退款 都开始退款流程。
            log.info("发现争议开始退款 :{}", payTrade.getTradeNo());
            PayChannelService payChannelService = payChannelRouterService.getPayChannelService(payTrade.getPayChannel());
            ChannelRefundInput channelRefundInput = new ChannelRefundInput();
            channelRefundInput.setAmount(payTrade.getOccurAmount());
            channelRefundInput.setTransactionId(payCallChannelRecord.getTransactionId());
            ChannelRefundOutput channelRefundOutput = payChannelService.refund(channelRefundInput);
            if (channelRefundOutput.getStatus().equals(CallChannelStatus.SUCC)) {
                payCallChannelRecordDao.updateStatus(payCallChannelRecord.getId(), CallChannelStatus.CANCEL.getValue());
                PayCallChannelRecord r = new PayCallChannelRecord();
                r.setChannel(payTrade.getPayChannel());
                r.setTradeNo(payTrade.getTradeNo());
                r.setBizType(BizType.REFUND);
                r.setBatchId(channelRefundOutput.getBatchId());
                r.setTransactionId(channelRefundOutput.getTransactionId());
                r.setResult(channelRefundOutput.getResult());
                r.setStatus(channelRefundOutput.getStatus());
                r.setAmount(channelRefundInput.getAmount());
                r.setPayerId("");
                r.setNextCheckTime(LocalDateTime.now());
                r.setLastCheckTime(LocalDateTime.now());
                r.setCreateTime(LocalDateTime.now());
                r.setEditTime(LocalDateTime.now());
                r.setCheckNum(0);
                payCallChannelRecordDao.insert(r);
                payTradeDao.updateById(PayTrade.builder().id(payTrade.getId()).status(TradeStatus.REFUNDED).build());
            }
        } else {
            log.info("尝试退款但状态不对 :{}", payTrade.getTradeNo());
        }
    }

    @Override
    public boolean processingDisupte(ProcessingDispute processingDispute) {
        return false;
    }

}
