package com.ruoyi.cai.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.config.CaiProperties;
import com.ruoyi.cai.domain.*;
import com.ruoyi.cai.dto.admin.vo.AccountAdminVo;
import com.ruoyi.cai.dto.video.VideoSettleResp;
import com.ruoyi.cai.dto.video.WithholdingFeeUserResp;
import com.ruoyi.cai.enums.ConsumeLogType;
import com.ruoyi.cai.enums.SystemConfigEnum;
import com.ruoyi.cai.enums.account.AccountChangeCodeEnum;
import com.ruoyi.cai.enums.account.AccountTypeEnum;
import com.ruoyi.cai.manager.IdManager;
import com.ruoyi.cai.manager.SystemConfigManager;
import com.ruoyi.cai.mapper.AccountMapper;
import com.ruoyi.cai.service.*;
import com.ruoyi.cai.util.NumCaUtil;
import com.ruoyi.cai.ws.bean.Room;
import com.ruoyi.cai.ws.bean.RoomData;
import com.ruoyi.cai.ws.bean.UserData;
import com.ruoyi.cai.ws.constant.RoomStatusEnums;
import com.ruoyi.cai.ws.service.RoomService;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
 * 用户账户Service业务层处理
 *
 * @author 77
 * @date 2023-12-23
 */
@Service
@Slf4j
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    @Autowired
    private ConsumeLogService consumeLogService;
    @Autowired
    private UserService userService;
    @Autowired
    private AccountChangeLogService accountChangeLogService;
    @Autowired
    private SystemConfigManager systemConfigManager;
    @Autowired
    private CaiProperties caiProperties;


    @Override
    public Account getByUserId(Long userId) {
        return this.getOne(Wrappers.lambdaQuery(Account.class).eq(Account::getUserId,userId).last("limit 1"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ConsumeLog guardDecr(ConsumeLog consumeLog, ConsumeLogType consumeLogType){
        return giftDecr(consumeLog,consumeLogType,null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ConsumeLog giftDecr(ConsumeLog consumeLog, ConsumeLogType consumeLogType, Gift gift) {
        log.info("开始扣费 consumeLog={}", JSON.toJSONString(consumeLog));
        Long userId = consumeLog.getSourceUserId();
        Long amount = consumeLog.getAmount();
        Account account = this.getByUserId(userId);
        long totalCoin = account.getIncomeCoin() + account.getCoin();
        if(totalCoin < amount){
            throw new ServiceException("余额不足");
        }
        boolean flag = false;
        long coin = -amount; // 消费余额
        long incomeCoin = 0; // 消费收益
        long diff = account.getCoin() - amount;
        // 充值币够用
        if(diff > 0){
            flag = baseMapper.decrCoin(userId, -coin);
        } else { // 充值币不够用
            coin = -account.getCoin();
            incomeCoin = diff;
            boolean decrCoin = baseMapper.decrCoin(userId, -coin);
            boolean decrIncomeCoin = baseMapper.decrIncomeCoin(userId, -diff);
            if(decrCoin && decrIncomeCoin){
                flag = true;
            }
        }
        // 扣费不成功
        if(!flag){
            throw new ServiceException("扣费失败，请重新购买");
        }
        // 开始处理 接收方的费用
        Long targetUserId = consumeLog.getTargetUserId();
        if(targetUserId != null){
            Long anchorAmount = NumCaUtil.coin(amount,consumeLog.getTargetRate());
            consumeLog.setAnchorAmount(anchorAmount);
            baseMapper.incsIncomeCoin(targetUserId,anchorAmount);
        }
        // 记录消费方的流水
        if(coin != 0){
            AccountChangeCodeEnum change = ConsumeLogType.getSourceChange(consumeLogType, AccountTypeEnum.COIN);
            accountChangeLogService.saveLogNoAdmin(userId, consumeLog.getSourceUsercode(), change, coin, consumeLog.getTraceId()
                ,consumeLog.getTargetUserId(),gift);
        }
        if(incomeCoin != 0){
            AccountChangeCodeEnum change = ConsumeLogType.getSourceChange(consumeLogType, AccountTypeEnum.INCOME_COIN);
            accountChangeLogService.saveLogNoAdmin(userId, consumeLog.getSourceUsercode(), change, incomeCoin, consumeLog.getTraceId()
                    ,consumeLog.getTargetUserId(),gift);
        }
        // 记录接收方的流水
        if(targetUserId != null){
            AccountChangeCodeEnum change = ConsumeLogType.getTargetChange(consumeLogType);
            accountChangeLogService.saveLogNoAdmin(consumeLog.getTargetUserId(), consumeLog.getTargetUsercode(), change, consumeLog.getAnchorAmount(), consumeLog.getTraceId()
                    ,consumeLog.getSourceUserId(),gift);
        }
        // 获取分销的比例和用户
        consumeLogService.calculateInitFenxiao(consumeLog);
        consumeLogService.save(consumeLog);
        return consumeLog;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long imDesc(User fromUser, User toUser, Long price) {
        Long fromUserId = fromUser.getId();
        Account account = this.getByUserId(fromUserId);
        long totalCoin = account.getIncomeCoin() + account.getCoin();
        if(totalCoin < price){
            throw new ServiceException("余额不足");
        }
        boolean flag = false;
        long coin = -price; // 消费余额
        long incomeCoin = 0; // 消费收益
        long diff = account.getCoin() - price;
        // 充值币够用
        if(diff > 0){
            flag = baseMapper.decrCoin(fromUserId, -coin);
        } else { // 充值币不够用
            coin = -account.getCoin();
            incomeCoin = diff;
            boolean decrCoin = baseMapper.decrCoin(fromUserId, -coin);
            boolean decrIncomeCoin = baseMapper.decrIncomeCoin(fromUserId, -incomeCoin);
            if(decrCoin && decrIncomeCoin){
                flag = true;
            }
        }
        // 扣费不成功
        if(!flag){
            throw new ServiceException("扣费失败，请重新购买");
        }
        // 开始处理 接收方的费用
        BigDecimal imRate = systemConfigManager.getSystemConfigOfBigDecimal(SystemConfigEnum.DAY_MAX_DYNAMIC);
        Long anchorAmount = NumCaUtil.coin(price,imRate);
        baseMapper.incsIncomeCoin(toUser.getId(),anchorAmount);
        Long tractId = IdManager.nextId();
        // 记录消费方的流水
        if(coin != 0){
            accountChangeLogService.saveLogNoAdmin(fromUser.getId(),fromUser.getUsercode(), AccountChangeCodeEnum.IM_COIN_OUT, coin, tractId, toUser.getId());
        }
        if(incomeCoin != 0){
            accountChangeLogService.saveLogNoAdmin(fromUser.getId(),fromUser.getUsercode(), AccountChangeCodeEnum.IM_INCOME_COIN_OUT,incomeCoin,tractId,toUser.getId());
        }
        // 记录接收方的流水
        if(anchorAmount != 0){
            accountChangeLogService.saveLogNoAdmin(toUser.getId(),toUser.getUsercode(), AccountChangeCodeEnum.IM_INCOME,anchorAmount,tractId,fromUser.getId());
        }
        return tractId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdraw(Long userId, Long incomeCoin,Long traceId){
        User user = userService.getById(userId);
        Account account = this.getByUserId(userId);
        if(account == null || user == null){
            throw new ServiceException("无效账号");
        }
        if(account.getIncomeCoin() < incomeCoin){
            throw new ServiceException("需"+incomeCoin+caiProperties.getCoinName()+"才可提现");
        }
        boolean incs = baseMapper.decrIncomeCoin(userId, incomeCoin);
        if(!incs){
            throw new ServiceException("需"+incomeCoin+caiProperties.getCoinName()+"才可提现");
        }
        accountChangeLogService.saveLogNoAdmin(user.getId(),user.getUsercode(), AccountChangeCodeEnum.WITHDRAW,incomeCoin,traceId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdrawFail(Long userId, Long incomeCoin, Long traceId){
        User user = userService.getById(userId);
        Account account = this.getByUserId(userId);
        if(account == null || user == null){
            throw new ServiceException("无效账号");
        }
        baseMapper.incsIncomeCoin(userId, incomeCoin);
        accountChangeLogService.saveLogNoAdmin(user.getId(),user.getUsercode(), AccountChangeCodeEnum.WITHDRAW_FAIL,incomeCoin,traceId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recharge(ConsumeLog consumeLog){
        log.info("开始充值 consumeLog={}", JSON.toJSONString(consumeLog));
        Long userId = consumeLog.getSourceUserId();
        Long amount = consumeLog.getAmount();
        User user = userService.getById(userId);
        Account account = this.getByUserId(userId);
        if(account == null || user == null){
            throw new ServiceException("无效账号");
        }
        baseMapper.incsCoin(userId, amount);
        accountChangeLogService.saveLogNoAdmin(user.getId(),user.getUsercode(), AccountChangeCodeEnum.RECHARGE,amount,consumeLog.getTraceId());
        // 获取分销的比例和用户
        consumeLogService.calculateInitFenxiao(consumeLog);
        consumeLogService.save(consumeLog);
    }


    @Override
    public Page<AccountAdminVo> pageAdmin(PageQuery pageQuery, AccountAdminVo bo) {
        return baseMapper.pageAdmin(pageQuery.build(),bo);
    }

    @Autowired
    private GiftService giftService;

    /**
     * 分销
     *
     * @return
     */
    @Override
    public boolean distribution(ConsumeLog consumeLog, Long userId, Long amount, AccountChangeCodeEnum accountChangeEnum) {
        User user = userService.getById(userId);
        Account account = this.getByUserId(userId);
        if(account == null || user == null){
            log.error("分销失败,无效账号 userId={}",userId);
            return false;
        }
        if(amount == null || accountChangeEnum == null){
            log.error("分销失败，参数错误 amount={},accountChangeEnum={}",amount,accountChangeEnum);
            return false;
        }
        baseMapper.incsCoin(userId, amount);
        if(ConsumeLogType.GIFT.getCode().equals(consumeLog.getType())){ // 礼物
            Gift gift = giftService.getById(consumeLog.getBusinessParam());
            accountChangeLogService.saveLogNoAdmin(userId,user.getUsercode(), accountChangeEnum,amount,consumeLog.getTraceId(),
                    consumeLog.getTargetUserId(),gift);
            return true;
        }else if(ConsumeLogType.VIDEO.getCode().equals(consumeLog.getType())
            || ConsumeLogType.GUARD.getCode().equals(consumeLog.getType())){ // 视频和守护
            accountChangeLogService.saveLogNoAdmin(userId,user.getUsercode(), accountChangeEnum,amount,consumeLog.getTraceId(),
                    consumeLog.getTargetUserId());
            return true;
        }else if(ConsumeLogType.RECHARGE.getCode().equals(consumeLog.getType())){ // 充值
            accountChangeLogService.saveLogNoAdmin(userId,user.getUsercode(), accountChangeEnum,amount,consumeLog.getTraceId(),
                    consumeLog.getSourceUserId());
            return true;
        }
        return false;
    }

    /**
     * 视频-预付款
     * @param userId
     * @param price
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public WithholdingFeeUserResp withholdingFeeUser(Long userId, Long price) {
        Account account = this.getByUserId(userId);
        if(account == null){
            throw new ServiceException("账户异常");
        }
        long totalCoin = account.getIncomeCoin() + account.getCoin();
        if(totalCoin < price){
            throw new ServiceException("余额不足");
        }
        boolean flag = false;
        long coin = -price; // 消费余额
        long incomeCoin = 0; // 消费收益
        long diff = account.getCoin() - price;
        // 充值币够用
        if(diff > 0){
            flag = baseMapper.decrCoin(userId, -coin);
        } else { // 充值币不够用
            coin = -account.getCoin();
            incomeCoin = diff;
            boolean decrCoin = baseMapper.decrCoin(userId, -coin);
            boolean decrIncomeCoin = baseMapper.decrIncomeCoin(userId, -incomeCoin);
            if(decrCoin && decrIncomeCoin){
                flag = true;
            }
        }
        // 扣费不成功
        if(!flag){
            throw new ServiceException("扣费失败");
        }
        WithholdingFeeUserResp resp = new WithholdingFeeUserResp();
        resp.setDecrCoin(-coin);
        resp.setDecrIncomeCoin(-incomeCoin);
        return resp;
    }

    @Autowired
    private RoomService roomService;
    @Autowired
    private UserCallService userCallService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public VideoSettleResp videoSettle(Room room) {
        RoomData roomData = room.getRoomData();
        UserCall userCall = userCallService.getById(roomData.getRoomId());
        UserData callUserData = room.getCallUserData();
        Long callPrice = roomData.getCallPrice();
        Long callTime = roomService.getCallTime(room);
        // 本次支付金额
        long totalAmount = callPrice * ((callTime / 60) + 1); // 本次需要支付的金额
        if(callTime % 60 == 0){
            totalAmount = callPrice * (callTime / 60);
        }
        Long payCoin = roomData.getPayCoin(); // 已经支付的余额
        Long payIncome = roomData.getPayIncome(); // 已经支付的收益
        // 补差价
        long diff = totalAmount - (payCoin + payIncome);
        Account callAccount = this.getByUserId(callUserData.getId());
        long userAmount = callAccount.getCoin() + callAccount.getIncomeCoin(); // 用户余额
        if(diff > 0){
            // 账户上面有余额
            if(userAmount > 0){
                diff = Math.min(userAmount, diff);
                log.info("roomid {} 已支付 {}  仍需要补差价：{}, 用户余额：{} ",
                        roomData.getRoomId(),roomData.getPayCoin() + roomData.getPayIncome(),diff, userAmount);
                WithholdingFeeUserResp resp = withholdingFeeUser(callUserData.getId(), diff);
                payCoin = payCoin + resp.getDecrCoin();
                payIncome = payIncome + resp.getDecrIncomeCoin();
            } else {
                log.info("房间需要补差价，但用户余额不足 roomid {} 已支付 {}  仍需要补差价：{}", roomData.getRoomId(),roomData.getPayCoin() + roomData.getPayIncome(),diff);
            }
        }else{
            // 退钱逻辑
            long incsCoin = 0L; // 需要退的金额
            long incsIncomeCoin = 0L; // 需要退的收益金额
            diff = Math.abs(diff); // 待退款的钱
            if(roomData.getPayCoin() > diff){ // 消费的金额大于待退款金额
                incsCoin = diff;
            }else {
                if(roomData.getPayIncome() > (diff - roomData.getPayCoin())){ // 消费金额不够退，但是收益金额扣扣费
                    incsCoin = roomData.getPayCoin();
                    incsIncomeCoin = roomData.getPayIncome() - (diff - roomData.getPayCoin());
                } else { // 消费金额不够退 余额也不够退 能退多少是多少
                    incsCoin = roomData.getPayCoin();
                    incsIncomeCoin = roomData.getPayIncome();
                }
            }
            if(incsCoin > 0){
                payCoin = payCoin - incsCoin;
                baseMapper.incsCoin(callUserData.getId(), incsCoin);
            }
            if(incsIncomeCoin > 0){
                payIncome = payIncome - incsIncomeCoin;
                baseMapper.incsCoin(callUserData.getId(), incsIncomeCoin);
            }
        }
        Long amountReal = payCoin + payIncome; // 实际支付的金额
        Long anchorAmount = NumCaUtil.coin(amountReal,roomData.getVideoDivide());
        User fromUser = userService.getById(userCall.getFromUid());
        User toUser = userService.getById(userCall.getToUid());

        baseMapper.incsIncomeCoin(toUser.getId(),anchorAmount);
        Long tractId = IdManager.nextId();
        // 记录消费方的流水
        if(payCoin != 0){
            accountChangeLogService.saveLogNoAdmin(fromUser.getId(),fromUser.getUsercode(), AccountChangeCodeEnum.VIDEO_COIN_OUT, payCoin, tractId);
        }
        if(payIncome != 0){
            accountChangeLogService.saveLogNoAdmin(fromUser.getId(),fromUser.getUsercode(), AccountChangeCodeEnum.VIDEO_INCOME_COIN_OUT,payIncome,tractId);
        }
        // 记录接收方的流水
        if(anchorAmount != 0){
            accountChangeLogService.saveLogNoAdmin(toUser.getId(),toUser.getUsercode(), AccountChangeCodeEnum.VIDEO_INCOME,anchorAmount,tractId);
        }
        ConsumeLog consumeLog =  new ConsumeLog();
        consumeLog.init(fromUser,toUser);
        consumeLog.setType(ConsumeLogType.VIDEO.getCode());
        consumeLog.setTraceId(tractId);
        consumeLog.setTargetRate(roomData.getVideoDivide());
        consumeLog.setAmount(amountReal);
        consumeLog.setAnchorAmount(anchorAmount);
        // 获取分销的比例和用户
        consumeLogService.calculateInitFenxiao(consumeLog);
        consumeLogService.save(consumeLog);
        userCallService.update(Wrappers.lambdaUpdate(UserCall.class)
                .eq(UserCall::getId, userCall.getId())
                .set(UserCall::getTraceId,tractId)
                .set(UserCall::getBeginTime, DateUtil.date(roomData.getBeginTime()).toLocalDateTime())
                .set(UserCall::getEndTime,DateUtil.date(roomData.getHangupTime()).toLocalDateTime())
                .set(UserCall::getCallTime,roomData.getHangupTime() - roomData.getBeginTime())
                .set(UserCall::getCallAmount,amountReal)
                .set(UserCall::getCallIncome, anchorAmount)
                .set(UserCall::getStatus, RoomStatusEnums.STATUS_HANGUP.getCode()));
        VideoSettleResp resp = new VideoSettleResp();
        resp.setConsumeLog(consumeLog);
        resp.setPayIncome(payIncome);
        resp.setPayCoin(payCoin);
        resp.setAnchorIncome(anchorAmount);
        return resp;
    }

}
