package cn.iocoder.yudao.module.member.service.card;

import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.member.controller.admin.card.vo.CardPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.card.vo.CardSaveReqVO;
import cn.iocoder.yudao.module.member.controller.admin.card.vo.RechargeVO;
import cn.iocoder.yudao.module.member.controller.app.card.vo.AppCardMonthVO;
import cn.iocoder.yudao.module.member.controller.app.card.vo.AppCardPageReqVO;
import cn.iocoder.yudao.module.member.controller.app.card.vo.FaceVo;
import cn.iocoder.yudao.module.member.dal.dataobject.card.CardDO;
import cn.iocoder.yudao.module.member.dal.dataobject.order.DishOrderDO;
import cn.iocoder.yudao.module.member.dal.dataobject.rechargelog.RechargeLogDO;
import cn.iocoder.yudao.module.member.dal.dataobject.refund.RefundDO;
import cn.iocoder.yudao.module.member.dal.dataobject.refundlog.RefundLogDO;
import cn.iocoder.yudao.module.member.dal.mysql.card.CardMapper;
import cn.iocoder.yudao.module.member.dal.mysql.group.MemberGroupMapper;
import cn.iocoder.yudao.module.member.dal.mysql.order.DishOrderMapper;
import cn.iocoder.yudao.module.member.dal.mysql.rechargelog.RechargeLogMapper;
import cn.iocoder.yudao.module.member.dal.mysql.refund.IntegralRefundMapper;
import cn.iocoder.yudao.module.member.dal.mysql.refundlog.RefundLogMapper;
import cn.iocoder.yudao.module.member.enums.CostTypeEnum;
import cn.iocoder.yudao.module.member.service.amount.AmountService;
import cn.iocoder.yudao.module.member.service.amount.CashRechargeService;
import cn.iocoder.yudao.module.member.service.amount.DeductionService;
import cn.iocoder.yudao.module.member.service.amount.WxRechargeService;
import cn.iocoder.yudao.module.member.util.MemberConstants;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;

/**
 * 余额变动明细 Service 实现类
 *
 * @author 开发账号
 */
@Service
@Validated
public class CardServiceImpl implements CardService {

    @Resource
    private CardMapper cardMapper;
    @Resource
    private MemberGroupMapper memberGroupMapper;

    @Resource
    private DishOrderMapper dishOrderMapper;

    @Resource
    private IntegralRefundMapper refundMapper;

    @Resource
    private RefundLogMapper refundLogMapper;

    @Resource
    private RechargeLogMapper rechargeLogMapper;

    @Resource
    private AmountService amountService;

    @Resource
    private WxRechargeService wxRechargeService;

    @Resource
    private CashRechargeService cashRechargeService;

    @Resource
    private DeductionService deductionService;

    @Override
    public Long createCard(CardSaveReqVO createReqVO) {
        // 插入
        CardDO card = BeanUtils.toBean(createReqVO, CardDO.class);
        cardMapper.insert(card);
        // 返回
        return card.getId();
    }

    @Override
    public void updateCard(CardSaveReqVO updateReqVO) {
        // 校验存在
        validateCardExists(updateReqVO.getId());
        // 更新
        CardDO updateObj = BeanUtils.toBean(updateReqVO, CardDO.class);
        cardMapper.updateById(updateObj);
    }

    @Override
    public void deleteCard(Long id) {
        // 校验存在
        validateCardExists(id);
        // 删除
        cardMapper.deleteById(id);
    }

    private void validateCardExists(Long id) {
        if (cardMapper.selectById(id) == null) {
            throw exception(CARD_NOT_EXISTS);
        }
    }

    @Override
    public CardDO getCard(Long id) {
        return cardMapper.selectById(id);
    }

    @Override
    public PageResult<CardDO> getCardPage(CardPageReqVO pageReqVO) {
        return cardMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<CardDO> getCardPage(AppCardPageReqVO pageReqVO) {
        return cardMapper.selectPage(pageReqVO);
    }


    @Override
    public void recharge(BigDecimal money, String flag, BigDecimal giftMoney,Long userId) {
        wxRechargeService.wxRecharge(money, money.subtract(giftMoney), giftMoney,userId);
    }

    @Override
    public BigDecimal getMoney(Long storeId) {
        return amountService.getAmount(SecurityFrameworkUtils.getLoginUserId(),storeId);
    }

    @Override
    public BigDecimal getMoneyByUserId(Long userId,Long storeId) {
        return amountService.getAmount(userId,storeId);
    }

    @Override
    public CardDO getCardDoByUserId(Long userId) {
        CardDO lastCardDO = cardMapper.selectOne(Wrappers.<CardDO>lambdaQuery().eq(CardDO::getUserId, userId)
                .orderByDesc(CardDO::getCreateTime).orderByDesc(CardDO::getId).last(MemberConstants.LIMIT_ONE));

        return lastCardDO;
    }

    @Override
    public void insertOne(CardDO cardDO) {
        cardMapper.insert(cardDO);
    }

    @Override
    public AppCardMonthVO getMonthMoney(Long userId, String flag, String time) {

        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(MemberConstants.TIME_FORMAT);
        LocalDate parse = LocalDate.parse(time, dateFormatter);

        // 获取指定日期所在月份的第一天
        LocalDate firstDayOfMonth = parse.withDayOfMonth(1);

        // 获取指定日期所在月份的最后一天
        YearMonth yearMonth = YearMonth.from(parse);
        LocalDate lastDayOfMonth = yearMonth.atEndOfMonth();

        // 获取所在月份的第一天的开始时间
        LocalDateTime firstDayOfMonthStart = firstDayOfMonth.atStartOfDay();

        // 获取所在月份的最后一天的结束时间
        LocalDateTime lastDayOfMonthEnd = lastDayOfMonth.atTime(LocalTime.MAX);
        AppCardMonthVO appCardMonthVO = cardMapper.selectMonth(userId, flag, firstDayOfMonthStart, lastDayOfMonthEnd);

        return appCardMonthVO;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rechargeByAdmin(RechargeVO vo) {
        cashRechargeService.rechargeByAdmin(vo.getUserIds(),vo.getGroupId(),vo.getMoney(),vo.getCarteenId(),vo.getGroup());
    }


    public FaceVo getFaceData(String faceId) {
        return cardMapper.getFaceData(faceId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(Long userId, BigDecimal money, Long orderId) {

        DishOrderDO dishOrderDO = dishOrderMapper.selectById(orderId);

        deductionService.refund(userId, money,dishOrderDO);
        //记录
        refundLog(dishOrderDO,money);
        //订单处理

        //可退款金额
        dishOrderDO.setRefundAmount(BigDecimal.ZERO);
        //待支付金额
        if (dishOrderDO.getPayAmount().compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal newPayAmount = dishOrderDO.getPayAmount().subtract(money);
            if (newPayAmount.compareTo(BigDecimal.ZERO) > 0) {
                dishOrderDO.setPayAmount(newPayAmount);
                rechargeLog(distribute(dishOrderDO, money).get("wx"));
            } else {
                handleOtherMoney(dishOrderDO,money);
                dishOrderDO.setPayAmount(BigDecimal.ZERO);
                dishOrderDO.setOrderStatus(DishOrderDO.COMPLETE);
            }
        }
        dishOrderMapper.updateById(dishOrderDO);
    }

    public void  handleOtherMoney(DishOrderDO dishOrderDO,BigDecimal money){
        //查找其余未支付订单
        List<DishOrderDO> dishOrderDOS = dishOrderMapper.selectList(Wrappers.<DishOrderDO>lambdaQuery().eq(DishOrderDO::getOrderStatus, DishOrderDO.TOCOMPLETE)
                .ne(DishOrderDO::getId, dishOrderDO.getId()));
        //判断多出的金额来自哪一部分
        BigDecimal payAmount = dishOrderDO.getPayAmount();

        Map<String, BigDecimal> distribute = distribute(dishOrderDO, money);
        //开始微信金额
        BigDecimal wxStart = distribute.get("wx");
        //可用金额
        BigDecimal gift = distribute.get("gift");
        BigDecimal cash = distribute.get("cash");
        BigDecimal wx = distribute.get("wx");


        //去除该订单已支付的
        if(payAmount.compareTo(gift)<=0){
            gift = gift.subtract(payAmount);
        }else {
            BigDecimal left = payAmount.subtract(gift);
            gift = BigDecimal.ZERO;
            if(left.compareTo(cash)<=0){
                cash = cash.subtract(left);
            }else{
                BigDecimal left1 = left.subtract(cash);
                cash = BigDecimal.ZERO;
                if(left1.compareTo(wx)<=0){
                    wx = wx.subtract(left1);
                }else {
                    wx= BigDecimal.ZERO;
                }
            }
        }

        if (CollectionUtil.isNotEmpty(dishOrderDOS)){


            //待修改的订单
            ArrayList<DishOrderDO> update = new ArrayList<>();

            //顺序补充直到金额用完跳出循环
            for (DishOrderDO vo : dishOrderDOS){
                BigDecimal voPayAmount = vo.getPayAmount();

                //订单补充的金额
                BigDecimal supGift = BigDecimal.ZERO;
                BigDecimal supCash = BigDecimal.ZERO;
                BigDecimal supWx = BigDecimal.ZERO;


                //计算
                if(voPayAmount.compareTo(gift)<=0){
                    gift = gift.subtract(voPayAmount);
                    supGift = voPayAmount;
                }else {
                    voPayAmount = voPayAmount.subtract(gift);
                    supGift = gift;
                    gift = BigDecimal.ZERO;

                    if(voPayAmount.compareTo(cash)<=0){
                        cash = cash.subtract(voPayAmount);
                        supCash = voPayAmount;
                    }else{
                        voPayAmount = voPayAmount.subtract(cash);
                        supCash = cash;
                        cash = BigDecimal.ZERO;

                        if(voPayAmount.compareTo(wx)<=0){
                            wx = wx.subtract(voPayAmount);
                            supWx = voPayAmount;
                        }else {
                            voPayAmount = voPayAmount.subtract(wx);
                            supWx = wx;
                            wx= BigDecimal.ZERO;
                        }
                    }
                }

                //更改数据
                vo.setGiftAmount(vo.getGiftAmount().add(supGift));
                vo.setCashAmount(vo.getCashAmount().add(supCash));
                vo.setWxAmount(vo.getWxAmount().add(supWx));
                vo.setPayAmount(voPayAmount);
                vo.setRefundAmount(vo.getRefundAmount().add(supGift).add(supCash).add(supWx));
                if(voPayAmount.compareTo(BigDecimal.ZERO)<=0){
                    vo.setOrderStatus(DishOrderDO.COMPLETE);
                }
                update.add(vo);
                //判断是否跳出循环
                if(gift.compareTo(BigDecimal.ZERO) == 0 && cash.compareTo(BigDecimal.ZERO) == 0 && wx.compareTo(BigDecimal.ZERO) ==0){
                    break;
                }
            }

            //批量
            if(CollectionUtil.isNotEmpty(update)){
                dishOrderMapper.updateBatch(update);
            }
        }
        rechargeLog(wxStart.subtract(wx));
    }

    //退款金额记录
    public void refundLog(DishOrderDO dishOrderDO,BigDecimal money){

        BigDecimal wx = distribute(dishOrderDO, money).get("wx");

        List<RefundLogDO> refundLogDOS = refundLogMapper.selectList(Wrappers.<RefundLogDO>lambdaQuery()
                .apply("TO_DAYS(NOW())-TO_DAYS(create_time) = 0"));
        if(CollectionUtil.isNotEmpty(refundLogDOS)){
            RefundLogDO refundLogDO = refundLogDOS.get(0);
            refundLogDO.setMoney(refundLogDO.getMoney().add(wx));
            refundLogDO.setLeftMoney(refundLogDO.getLeftMoney().add(wx));
            refundLogMapper.updateById(refundLogDO);
        }else {
            RefundLogDO refundLogDO = new RefundLogDO();
            refundLogDO.setMoney(wx);
            refundLogDO.setLeftMoney(wx);
            refundLogDO.setCreateTime(LocalDateTime.now());
            refundLogMapper.insert(refundLogDO);
        }
    }

    //补充金额记录
    public void rechargeLog(BigDecimal money){
        List<RechargeLogDO> rechargeLogDOList = rechargeLogMapper.selectList(Wrappers.<RechargeLogDO>lambdaQuery()
                .apply("TO_DAYS(NOW())-TO_DAYS(create_time) = 0"));
        if(CollectionUtil.isNotEmpty(rechargeLogDOList)){
            RechargeLogDO rechargeLogDO = rechargeLogDOList.get(0);
            rechargeLogDO.setMoney(rechargeLogDO.getMoney().add(money));
            rechargeLogMapper.updateById(rechargeLogDO);
        }else {
            RechargeLogDO rechargeLogDO = new RechargeLogDO();
            rechargeLogDO.setMoney(money);
            rechargeLogDO.setCreateTime(LocalDateTime.now());
            rechargeLogMapper.insert(rechargeLogDO);
        }
    }
    //分配金额
    public Map<String,BigDecimal>  distribute(DishOrderDO dishOrderDO,BigDecimal money){
        HashMap<String, BigDecimal> map = new HashMap<>();
        BigDecimal giftAmount = dishOrderDO.getGiftAmount();
        BigDecimal cashAmount = dishOrderDO.getCashAmount();
        BigDecimal wxAmount = dishOrderDO.getWxAmount();

        //可用金额
        BigDecimal gift = BigDecimal.ZERO;
        BigDecimal cash = BigDecimal.ZERO;
        BigDecimal wx = BigDecimal.ZERO;

        //分配退款金额
        if(money.compareTo(giftAmount)<=0){
            gift = money;
        }else {
            gift = giftAmount;
            BigDecimal left = money.subtract(giftAmount);
            if(left.compareTo(cashAmount)<=0){
                cash = left;
            }else {
                cash = cashAmount;
                BigDecimal left1 = left.subtract(cashAmount);
                if(left1.compareTo(wxAmount)<=0){
                    wx = left1;
                }else {
                    wx = wxAmount;
                }
            }
        }
        map.put("gift",gift);
        map.put("cash",cash);
        map.put("wx",wx);

        return map;
    }




    public BigDecimal checkOrder(Long userId) {

        LambdaQueryWrapper<DishOrderDO> wrapper = new LambdaQueryWrapper<>();

        wrapper.eq(DishOrderDO::getUserId, userId);
        wrapper.eq(DishOrderDO::getOrderStatus, DishOrderDO.TOCOMPLETE);

        List<DishOrderDO> dishOrderDOS = dishOrderMapper.selectList(wrapper);

        if (CollectionUtil.isNotEmpty(dishOrderDOS)) {
            BigDecimal reduce = dishOrderDOS.stream().map(DishOrderDO::getTotalMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
            return reduce;
        } else {
            return BigDecimal.ZERO;
        }
    }

    @Override
    public BigDecimal getCashMoney(Long userId,Long storeId) {
        return amountService.getCashAmount(userId,storeId);
    }

    @Override
    public Boolean cashDraw(Long userId, BigDecimal money, Long storeId) {
        deductionService.cashDraw(userId, money, CostTypeEnum.CASH_WITHDRAW.getCode(), storeId);
        return true;
    }

    void updateOrderStatus(Long userId) {
        LambdaUpdateWrapper<DishOrderDO> wrapper = new LambdaUpdateWrapper<>();

        wrapper.eq(DishOrderDO::getUserId, userId);
        wrapper.eq(DishOrderDO::getOrderStatus, DishOrderDO.TOCOMPLETE);
        wrapper.set(DishOrderDO::getOrderStatus, DishOrderDO.COMPLETE);
        dishOrderMapper.update(wrapper);

    }

    void handleOrderWx(Long userId, BigDecimal money, BigDecimal giftAmount) {

        BigDecimal wxMoney = money;
        BigDecimal giftMoney = BigDecimal.ZERO;
        if (giftAmount != null) {
            wxMoney = money.subtract(giftMoney);
            giftMoney = giftAmount;
        }
        //开始的微信金额
        BigDecimal wxStart = wxMoney;
        //已审核的订单
        List<Long> refundOrder = getRefundOrder();

        //查询出所有未完全支付订单
        List<DishOrderDO> dishOrderDOS = dishOrderMapper.selectList(Wrappers.<DishOrderDO>lambdaQuery()
                .eq(DishOrderDO::getUserId, userId).eq(DishOrderDO::getOrderStatus, DishOrderDO.TOCOMPLETE));

        ArrayList<DishOrderDO> updateList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(dishOrderDOS)) {
            for (DishOrderDO dishOrderDO : dishOrderDOS) {
                BigDecimal payAmount = dishOrderDO.getPayAmount();
                if(payAmount.compareTo(money)<=0){
                    dishOrderDO.setOrderStatus(DishOrderDO.COMPLETE);
                    dishOrderDO.setPayAmount(BigDecimal.ZERO);
                    dishOrderDO.setRefundAmount(dishOrderDO.getRefundAmount().add(payAmount));
                    if(refundOrder.contains(dishOrderDO.getId())){
                        dishOrderDO.setRefundAmount(BigDecimal.ZERO);
                    }
                    if(payAmount.compareTo(wxMoney)<=0){
                        dishOrderDO.setWxAmount(dishOrderDO.getWxAmount().add(payAmount));
                        wxMoney = wxMoney.subtract(payAmount);
                    }else {
                        dishOrderDO.setWxAmount(dishOrderDO.getWxAmount().add(wxMoney));
                        wxMoney = BigDecimal.ZERO;
                        BigDecimal left = payAmount.subtract(wxMoney);
                        dishOrderDO.setGiftAmount(dishOrderDO.getGiftAmount().add(left));
                        giftMoney = giftMoney.subtract(left);
                    }
                    money = money.subtract(payAmount);

                    updateList.add(dishOrderDO);
                }else {
                    dishOrderDO.setPayAmount(payAmount.subtract(money));
                    dishOrderDO.setRefundAmount(dishOrderDO.getRefundAmount().add(money));
                    if(refundOrder.contains(dishOrderDO.getId())){
                        dishOrderDO.setRefundAmount(BigDecimal.ZERO);
                    }
                    dishOrderDO.setWxAmount(dishOrderDO.getWxAmount().add(wxMoney));
                    dishOrderDO.setGiftAmount(dishOrderDO.getGiftAmount().add(giftMoney));
                    wxMoney =BigDecimal.ZERO;
                    updateList.add(dishOrderDO);
                    break;
                }
            }
        }

        rechargeLog(wxStart.subtract(wxMoney));
        if(CollectionUtil.isNotEmpty(updateList)){
            dishOrderMapper.updateBatch(updateList);
        }

    }

    void handleOrderCash(List<Long> userIds, BigDecimal money) {
        ArrayList<DishOrderDO> updateList = new ArrayList<>();
        List<Long> refundOrder = getRefundOrder();
        //查询出所有未完全支付订单
        for (Long userId : userIds){
            BigDecimal cashMoney = money;

            List<DishOrderDO> dishOrderDOS = dishOrderMapper.selectList(Wrappers.<DishOrderDO>lambdaQuery()
                    .eq(DishOrderDO::getUserId, userId).eq(DishOrderDO::getOrderStatus, DishOrderDO.TOCOMPLETE));

            if (CollectionUtil.isNotEmpty(dishOrderDOS)) {
                for (DishOrderDO dishOrderDO : dishOrderDOS) {
                    BigDecimal payAmount = dishOrderDO.getPayAmount();
                    if(payAmount.compareTo(cashMoney)<=0){
                        dishOrderDO.setOrderStatus(DishOrderDO.COMPLETE);
                        dishOrderDO.setPayAmount(BigDecimal.ZERO);
                        dishOrderDO.setRefundAmount(dishOrderDO.getRefundAmount().add(payAmount));
                        if(refundOrder.contains(dishOrderDO.getId())){
                            dishOrderDO.setRefundAmount(BigDecimal.ZERO);
                        }
                        dishOrderDO.setCashAmount(dishOrderDO.getCashAmount().add(payAmount));
                        cashMoney = cashMoney.subtract(payAmount);
                        updateList.add(dishOrderDO);
                    }else {
                        dishOrderDO.setPayAmount(payAmount.subtract(cashMoney));
                        dishOrderDO.setRefundAmount(dishOrderDO.getRefundAmount().add(cashMoney));
                        if(refundOrder.contains(dishOrderDO.getId())){
                            dishOrderDO.setRefundAmount(BigDecimal.ZERO);
                        }
                        dishOrderDO.setCashAmount(dishOrderDO.getCashAmount().add(cashMoney));
                        updateList.add(dishOrderDO);
                        break;
                    }
                }
            }
        }
        if(CollectionUtil.isNotEmpty(updateList)){
            dishOrderMapper.updateBatch(updateList);
        }

    }


    public List<Long> getRefundOrder(){
        List<RefundDO> refundDOS = refundMapper.selectList(Wrappers.<RefundDO>lambdaQuery().in(RefundDO::getStatus, Arrays.asList("2", "3")));
        ArrayList<Long> orderIds = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(refundDOS)){
            List<Long> collect = refundDOS.stream().map(RefundDO::getOrderId).collect(Collectors.toList());
            orderIds.addAll(collect);
        }

        return orderIds;
    }
}