package com.sonic.frog.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.sonic.frog.dao.UserDeductionStatDao;
import com.sonic.frog.domain.entity.UserDeductionStat;
import com.sonic.frog.enums.DeductionTypeEnum;
import com.sonic.frog.service.UserDeductionStatService;
import com.sonic.frog.utils.KeyGenerator;
import com.sonic.lion.lib.client.PayClient;
import com.sonic.lion.lib.enums.BizType;
import com.sonic.lion.lib.input.BalanceCheckoutInput;
import com.sonic.lion.lib.output.AccountBuffOutput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/**
 * 用户聊天、语音，语音通话扣费统计表 Service实现类
 * <p>
 * *文本消息（含聊天辅助）
 * 发送一条，计入预扣费总金额，同时检查用户帐户余额是否足够，
 * 余额够时：每一次发送文本10分钟后，如果没有新的消息产生，计入一条流水
 * 余额不够时：余额减预扣费金额，算一次流水。
 * <p>
 * *发送和听取语音
 * 每一次发送或听取计入预扣费总金额，同时检查用户帐户余额是否足够。
 * 余额够时：每一次听取和发送后语音消息10分钟后，如果没有新的消息产生，计入一条流水
 * 余额不够时：余额减预扣费金额，算一次流水。
 * <p>
 * *语音通话收费
 * 每一分钟，计入预扣费总金额，同时检查用户帐户余额是否足够。
 * 余额够时：在每一次语音通话结束后即可生成流水。
 * 余额不够时：余额减预扣费金额直接生成流水。
 */
@Slf4j
@Service
public class UserDeductionStatServiceImpl extends ServiceImpl<UserDeductionStatDao, UserDeductionStat> implements UserDeductionStatService {

    @Autowired
    private PayClient payClient;
    @Autowired
    private UserDeductionStatDao userDeductionStatDao;

    @Override
    public void userDeductionStat(Long userId, Long aiId, DeductionTypeEnum deductionType, String extra) {
        //查询统计是否存在
        UserDeductionStat userDeductionStat = getOne(Wrappers.<UserDeductionStat>lambdaQuery()
                .eq(UserDeductionStat::getUserId, userId)
                .eq(UserDeductionStat::getAiId, aiId)
                .eq(UserDeductionStat::getDeductionType, deductionType.getIndex())
                .last("limit 1")
        );
        //如果是语音通话且通话已经结束，则生成流水
        if (DeductionTypeEnum.VOICE_CALL.equals(deductionType)) {
            Map map = JSONObject.parseObject(extra, Map.class);
            int status = Integer.parseInt(map.get("status").toString());
            //通话结束
            if (status == 3) {
                //为什么要大于0才执行，因为在余额不足时，会生成流水，并置为0
                if (userDeductionStat != null && userDeductionStat.getPreDeductionAmount() > 0) {
                    //发起支付扣款
                    saveUserBill(userId, deductionType, userDeductionStat.getPreDeductionAmount());
                    //预扣费金额置为0
                    updatePreDeductionAmountToZero(userDeductionStat.getId());
                }
                //结束后直接return
                return;
            }
        }

        //总的预扣金额
        Long totalPreDeductionAmount = deductionType.getAmount();
        if (userDeductionStat != null) {
            totalPreDeductionAmount = userDeductionStat.getPreDeductionAmount() + deductionType.getAmount();
        }

        if (userDeductionStat == null) {
            //不存在，初始化
            userDeductionStat = new UserDeductionStat();
            userDeductionStat.setUserId(userId);
            userDeductionStat.setAiId(aiId);
            userDeductionStat.setDeductionType(deductionType.getIndex());
            userDeductionStat.setPreDeductionAmount(deductionType.getAmount());
            userDeductionStat.setLastTime(LocalDateTime.now());
            userDeductionStat.setCreateTime(LocalDateTime.now());
            userDeductionStat.setEditTime(LocalDateTime.now());
            save(userDeductionStat);
        } else {
            //更新总的预扣金额
            update(Wrappers.<UserDeductionStat>lambdaUpdate()
                    .eq(UserDeductionStat::getId, userDeductionStat.getId())
                    //更新预扣费金额+本次预扣费金额
                    .set(UserDeductionStat::getPreDeductionAmount, totalPreDeductionAmount)
                    .set(UserDeductionStat::getLastTime, LocalDateTime.now())
                    .set(UserDeductionStat::getEditTime, LocalDateTime.now())
            );
        }

        //每一次都要扣款结账但不生成流水(扣除用户余额)
        checkoutToPlatformNotBill(userId, deductionType, deductionType.getAmount());
    }

    /**
     * 扣款但不生成流水
     *
     * @param userId
     * @param deductionType
     * @param amount
     */
    private void checkoutToPlatformNotBill(Long userId, DeductionTypeEnum deductionType, Long amount) {
        //生成订单编号
        BalanceCheckoutInput input = BalanceCheckoutInput.builder()
                .platform("Balance")
                .outTradeNo(KeyGenerator.instance().generatorUniqueKey(""))
                .bizType(deductionType.getBizType())
                .name(deductionType.name())
                .srcAccountId(userId)
                .desAccountId(-1L)
                .productAmount(amount)
                .platformFee(0L)
                .promoAmount(0L)
                .build();
        payClient.checkoutToPlatformNotBill(input);
    }

    /**
     * 保存用户流水
     *
     * @param userId
     * @param deductionType
     * @param totalPreDeductionAmount
     */
    private void saveUserBill(Long userId, DeductionTypeEnum deductionType, Long totalPreDeductionAmount) {
        //生成订单编号
        BalanceCheckoutInput input = BalanceCheckoutInput.builder()
                .platform("Balance")
                .outTradeNo(KeyGenerator.instance().generatorUniqueKey(""))
                .bizType(deductionType.getBizType())
                .name(deductionType.name())
                .srcAccountId(userId)
                .desAccountId(-1L)
                .productAmount(totalPreDeductionAmount)
                .platformFee(0L)
                .promoAmount(0L)
                .build();
        payClient.saveUserBill(input);
    }

    @Override
    public Integer textOrVoiceTypeNoChatCheckoutJob() {
        //扫描超过10分钟的没有文本和语音消息的聊天且预扣费金额大于0的记录
        List<UserDeductionStat> list = list(Wrappers.<UserDeductionStat>lambdaQuery()
                .gt(UserDeductionStat::getPreDeductionAmount, 0)
                .in(UserDeductionStat::getDeductionType, Lists.newArrayList(DeductionTypeEnum.TEXT.getIndex(), DeductionTypeEnum.VOICE.getIndex()))
                .lt(UserDeductionStat::getLastTime, LocalDateTime.now().minusMinutes(10))
        );
        log.info("扫描超过10分钟的没有文本和语音消息的聊天且预扣费金额大于0的记录:{}", list);
        if (CollectionUtils.isEmpty(list)) {
            return 0;
        }
        for (UserDeductionStat userDeductionStat : list) {
            //获取类型
            DeductionTypeEnum deductionTypeEnum = DeductionTypeEnum.getDeductionTypeEnum(userDeductionStat.getDeductionType());
            //预扣费金额大于0才发起扣款
            if (deductionTypeEnum != null && userDeductionStat.getPreDeductionAmount() > 0) {
                try {
                    //保存用户流水
                    saveUserBill(userDeductionStat.getUserId(), deductionTypeEnum, userDeductionStat.getPreDeductionAmount());
                    //预扣费金额置为0
                    updatePreDeductionAmountToZero(userDeductionStat.getId());
                } catch (Exception e) {
                    log.error("保存用户流水失败:", e);
                }
            }
        }
        return list.size();
    }

    /**
     * 预扣费金额置为0
     *
     * @param userDeductionStatId
     */
    private void updatePreDeductionAmountToZero(Integer userDeductionStatId) {
        update(Wrappers.<UserDeductionStat>lambdaUpdate()
                .eq(UserDeductionStat::getId, userDeductionStatId)
                .set(UserDeductionStat::getPreDeductionAmount, 0)
        );
    }

    @Override
    public Long getTotalDeductionAmount(Long userId) {
        return userDeductionStatDao.getTotalDeductionAmount(userId);
    }

    @Override
    public void userDeductionCheckout(Long userId) {
        //获取用户所有未完成扣费的记录
        List<UserDeductionStat> list = list(Wrappers.<UserDeductionStat>lambdaQuery()
                .eq(UserDeductionStat::getUserId, userId)
                .gt(UserDeductionStat::getPreDeductionAmount, 0)
        );
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (UserDeductionStat userDeductionStat : list) {
            Long preDeductionAmount = userDeductionStat.getPreDeductionAmount();
            Integer deductionType = userDeductionStat.getDeductionType();
            //获取类型
            DeductionTypeEnum deductionTypeEnum = DeductionTypeEnum.getDeductionTypeEnum(deductionType);
            //发起支付扣款
            saveUserBill(userId, deductionTypeEnum, preDeductionAmount);
            //预扣费金额置为0
            updatePreDeductionAmountToZero(userDeductionStat.getId());
        }
    }
}