package com.sonic.lion.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import com.sonic.lion.dao.UserSubscriptionNotifyDao;
import com.sonic.lion.dao.UserSubscribeLogDao;
import com.sonic.lion.dao.UserSubscriptionDao;
import com.sonic.lion.domain.bo.SubscriptionBo;
import com.sonic.lion.domain.entity.AppStoreProduct;
import com.sonic.lion.domain.entity.UserSubscriptionNotify;
import com.sonic.lion.domain.entity.UserSubscribeLog;
import com.sonic.lion.domain.entity.UserSubscription;
import com.sonic.lion.enums.ThirdAccountType;
import com.sonic.lion.enums.ToastResultCode;
import com.sonic.lion.service.CommonMessageService;
import com.sonic.lion.service.PayAccountFundThirdService;
import com.sonic.lion.service.PayChargeService;
import com.sonic.lion.service.UserSubscriptionService;
import com.sonic.lion.utils.DateConvertUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @author code
 * 订阅相关
 */
@Slf4j
@Service
public class UserSubscriptionServiceImpl implements UserSubscriptionService {

    @Autowired
    private UserSubscriptionDao userSubscriptionDao;
    @Autowired
    private UserSubscriptionNotifyDao userSubscriptionNotifyDao;
    @Autowired
    private PayChargeService payChargeService;
    @Autowired
    private UserSubscribeLogDao userSubscribeLogDao;
    @Autowired
    private PayAccountFundThirdService payAccountFundThirdService;
    @Autowired
    private CommonMessageService commonMessageService;

    /**
     * 增加订阅日志
     *
     * @param appStoreProduct
     * @param userSubscriptionDb
     */
    private void addLogs(Long orgUserId, AppStoreProduct appStoreProduct, UserSubscription userSubscriptionDb) {
        UserSubscribeLog userSubscribeLog = UserSubscribeLog.builder()
                .orgUserId(orgUserId)
                .userId(userSubscriptionDb.getUserId()).subscriptionId(userSubscriptionDb.getSubscriptionId())
                .platform(appStoreProduct.getPlatform()).buff(0L).payAmount(appStoreProduct.getPayAmount())
                .memberType(appStoreProduct.getMemberType()).period(appStoreProduct.getPeriod()).productId(appStoreProduct.getProductId())
                .createTime(LocalDateTime.now()).build();

        if (userSubscriptionDb.getPlatform().equals(UserSubscription.Platform.APPLE)) {
            userSubscribeLog.setAppStoreAmount(BigDecimal.valueOf(userSubscribeLog.getPayAmount()).multiply(new BigDecimal("0.3")).setScale(0, BigDecimal.ROUND_HALF_UP).longValue());
        } else if (userSubscriptionDb.getPlatform().equals(UserSubscription.Platform.GOOGLE)) {
            userSubscribeLog.setAppStoreAmount(BigDecimal.valueOf(userSubscribeLog.getPayAmount()).multiply(new BigDecimal("0.15")).setScale(0, BigDecimal.ROUND_HALF_UP).longValue());
        } else if (userSubscriptionDb.getPlatform().equals(UserSubscription.Platform.STRIPE)) {
            //pay_amount * 0.029 + 30
            userSubscribeLog.setAppStoreAmount(BigDecimal.valueOf(userSubscribeLog.getPayAmount()).multiply(new BigDecimal("0.029")).setScale(0, BigDecimal.ROUND_HALF_UP).longValue() + 30);
        }
        userSubscribeLogDao.insert(userSubscribeLog);
    }

    @Override
    public void bindStripe(SubscriptionBo subscriptionBo) {
        ToastResultCode.DATA_NOT_EXITS.check(subscriptionBo == null);
        ToastResultCode.SUBSCRIPTION_STATUS_ERROR.check(subscriptionBo.getCurrentPeriodEnd() <= System.currentTimeMillis() / 1000);
        //根据客户查询用户ID
        Long userId = payAccountFundThirdService.getAccountIdByOpenId(subscriptionBo.getCustomer(), ThirdAccountType.STRIPE_PAYMENT);
        //查询商品详情
        AppStoreProduct appStoreProduct = payChargeService.getAppStoreProduct(subscriptionBo.getPriceId(), UserSubscription.Platform.STRIPE.getDesc());
        //获取不到商品的话直接抛出异常
        ToastResultCode.DATA_NOT_EXITS.check(appStoreProduct == null);
        LocalDateTime purchaseDate = DateConvertUtils.getDate(subscriptionBo.getCurrentPeriodStart());
        //统一对结束时间做一个处理，+2个小时
        subscriptionBo.setCurrentPeriodEnd(subscriptionBo.getCurrentPeriodEnd() + (2 * 60 * 60));
        LocalDateTime expTime = DateConvertUtils.getDate(subscriptionBo.getCurrentPeriodEnd());
        this.bind(subscriptionBo.getId(), UserSubscription.Platform.STRIPE, expTime, purchaseDate, appStoreProduct.getProductId(), userId, null, null);
    }

    /**
     * 绑定 userId 关系 或 续期
     *
     * @param subscriptionId
     * @param platform
     * @param exptime
     * @param purchaseDate
     * @param productId
     * @param userId
     * @param purchaseToken
     * @param ip
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void bind(String subscriptionId, UserSubscription.Platform platform, LocalDateTime exptime, LocalDateTime purchaseDate, String productId, Long userId, String purchaseToken, String ip) {
        if (exptime != null && LocalDateTime.now().isAfter(exptime)) {
            log.info("===> sub bind 新过期时间不正确1, {}, {}", LocalDateTime.now(), exptime);
            return;
        }
        //相差毫秒数
        long seconds = 0;
        AppStoreProduct appStoreProduct = payChargeService.getAppStoreProduct(productId, platform.getDesc());
        UserSubscription userSubscriptionDb = userSubscriptionDao.getBySubscriptionId(subscriptionId);
        //如果是Stripe渠道的话，根据用户ID来进行查询订阅所绑定的数据
        if(UserSubscription.Platform.STRIPE == platform) {
            userSubscriptionDb = userSubscriptionDao.getByUserId(userId);
        }
        //看数据库有没有 已经存在的绑定 不存在就是新订阅的产品。
        if (userSubscriptionDb == null) {
            UserSubscription userSubscription = UserSubscription.builder()
                    .userId(userId)
                    .productId(productId)
                    .purchaseToken(purchaseToken)
                    .memberType(appStoreProduct.getMemberType())
                    .priceType(appStoreProduct.getPeriod())
                    .purchaseTime(purchaseDate)
                    .autoRenewStatus(Boolean.TRUE)
                    .ip(ip)
                    .subscriptionId(subscriptionId).platform(platform).expTime(exptime).build();
            ToastResultCode.USER_ID_NOT_NULL.check(userId == null);

            userSubscriptionDao.insert(userSubscription);
            userSubscriptionDb = userSubscription;
            // 如果已经存在 说明之前 该用户订阅过本产品 。中间有过暂停 或者 是需要续期。  只需要更新续期的到期时间 并 向下游发送消息。
            Duration duration = Duration.between(LocalDateTime.now(), exptime);
            //相差毫秒数
            seconds = duration.getSeconds();
        } else {
            //幂等性处理 如果 当前数据库的时间 已经新报出的数据之后 。丢弃
            if (userSubscriptionDb.getExpTime().isAfter(exptime) || userSubscriptionDb.getExpTime().isEqual(exptime)) {
                log.info("===> sub bind 新过期时间不正确2, {}, {}", userSubscriptionDb.getExpTime(), exptime);
                return;
            }
            Duration duration = Duration.between(userSubscriptionDb.getExpTime().isAfter(LocalDateTime.now()) ? userSubscriptionDb.getExpTime() : LocalDateTime.now(), exptime);
            //相差毫秒数
            seconds = duration.getSeconds();
            // 这里也可能是产品ID 变更了。升级了
            UserSubscription updateSub = UserSubscription.builder()
                    .id(userSubscriptionDb.getId())
                    .subscriptionId(subscriptionId)
                    .userId(userId)
                    .purchaseToken(purchaseToken)
                    .purchaseTime(purchaseDate)
                    .editTime(LocalDateTime.now())
                    .autoRenewStatus(Boolean.TRUE)
                    .productId(productId)
                    .expTime(exptime).build();
            userSubscriptionDao.updateById(updateSub);
        }
        Long orgUserId = userSubscriptionDb.getUserId();
        //重新查询出订阅的基础数据，以获取据库中全量的变化数据
        userSubscriptionDb = userSubscriptionDao.selectById(userSubscriptionDb.getId());
        log.info("===> sub bind orgUserId : {}, currentUserId : {}, seconds : {}", orgUserId, userSubscriptionDb.getUserId(), seconds);
        //添加处理日志
        addLogs(orgUserId, appStoreProduct, userSubscriptionDb);
        //发送mq消息到服务进行会员处理
        sendMessageWhenStatusChange(userSubscriptionDb, false);
    }

    @Override
    public UserSubscription getBySubscriptionId(String subscriptionId) {
        return userSubscriptionDao.getBySubscriptionId(subscriptionId);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Long addSubscriptionNotify(String subscriptionId, String messageId, String content, String
            type, UserSubscriptionNotify.Platform platform) {
        UserSubscriptionNotify userSubscriptionNotify = UserSubscriptionNotify.builder().subscriptionId(subscriptionId).messageId(messageId)
                .content(content).type(type).platform(platform).status(UserSubscriptionNotify.STATUS.PROCESSING).build();
        userSubscriptionNotifyDao.insert(userSubscriptionNotify);
        return userSubscriptionNotify.getId();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Long addSubscriptionNotifyV2(String subscriptionId, String messageId, String content, String extend, String
            type, UserSubscriptionNotify.Platform platform) {
        UserSubscriptionNotify userSubscriptionNotify = UserSubscriptionNotify.builder().subscriptionId(subscriptionId).messageId(messageId)
                .content(content).extend(extend).type(type).platform(platform).status(UserSubscriptionNotify.STATUS.PROCESSING).build();
        userSubscriptionNotifyDao.insert(userSubscriptionNotify);
        return userSubscriptionNotify.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setPurchaseToken(Long id, String token) {
        userSubscriptionDao.updateToken(id, token);
    }

    /**
     * 订阅状态变更 发送 MQ消息到下游
     *
     * @param userSubscription
     */
    public void sendMessageWhenStatusChange(UserSubscription userSubscription, boolean changeStatus) {
        if(!changeStatus) {
            //会员订阅成功同志消息处理
            commonMessageService.memberRenewSuccess(userSubscription.getUserId(), userSubscription.getExpTime());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void cancel(String subscriptionId, UserSubscription.Platform platform, Long expTime) {
        UserSubscription userSubscription = userSubscriptionDao.getBySubscriptionId(subscriptionId);
        //更新数据库字段。
        userSubscriptionDao.updateById(UserSubscription.builder().id(userSubscription.getId()).autoRenewStatus(Boolean.FALSE).build());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeNotify(Long notifyId) {
        userSubscriptionNotifyDao.updateStatus(notifyId, UserSubscriptionNotify.STATUS.FINISHED.name());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatusAndSubscriptionId(Long notifyId, String subscriptionId) {
        userSubscriptionNotifyDao.updateStatusAndSubscriptionId(notifyId, UserSubscriptionNotify.STATUS.FINISHED.name(), subscriptionId);
    }

    @Override
    public void updateNotifyExtend(Long notifyId, Long appleRefundRecordId, String extend) {
        userSubscriptionNotifyDao.updateNotifyExtend(notifyId, appleRefundRecordId, extend);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void failNotify(Long notifyId) {
        userSubscriptionNotifyDao.updateStatus(notifyId, UserSubscriptionNotify.STATUS.FAIL.name());
    }


    /**
     * 续订失败发送系统消息
     *
     * @param subscriptionId
     * @param platform
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void payFailSystemMessage(String subscriptionId, UserSubscription.Platform platform) {
        UserSubscription userSubscription = userSubscriptionDao.getBySubscriptionId(subscriptionId);
        if (userSubscription == null || BooleanUtils.isTrue(userSubscription.getRemind())) {
            return;
        }
        //发送系统消息
        commonMessageService.memberRenewFail(userSubscription.getUserId());
        //更新提醒字段
        userSubscriptionDao.updateRemind(userSubscription.getId());
    }

    @Transactional
    @Override
    public void updateStatusByThird(String subscriptionId, UserSubscription.Platform platform, String productId, Boolean autoRenew) {
        log.info("变更订阅状态{} , {} ,{}", subscriptionId, productId, autoRenew);
        UserSubscription userSubscription = userSubscriptionDao.getBySubscriptionId(subscriptionId);
        AppStoreProduct appStoreProduct = payChargeService.getAppStoreProduct(productId, platform.getDesc());
        userSubscription.setProductId(productId);
        userSubscription.setPriceType(appStoreProduct.getPeriod());
        userSubscription.setAutoRenewStatus(autoRenew);
        userSubscription.setMemberType(appStoreProduct.getMemberType());
        if (userSubscription == null) {
            return;
        }
        int rows = userSubscriptionDao.updateById(UserSubscription.builder().id(userSubscription.getId())
                .productId(productId)
                .priceType(userSubscription.getPriceType())
                .memberType(userSubscription.getMemberType())
                .autoRenewStatus(autoRenew).build());
        // 状态变更了
        if (rows > 0) {
            sendMessageWhenStatusChange(userSubscription, true);
        } else {
            log.info("变更订阅状态失败 数据库已经是这样了{} , {} ,{}", subscriptionId, productId, autoRenew);
        }
    }

    @Override
    public List<Long> queryUserIsSubscribe(List<Long> userIdList) {
        if (CollectionUtils.isEmpty(userIdList)) {
            return Lists.newArrayList();
        }
        return userSubscriptionDao.queryUserSubscription(userIdList);
    }

}
