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

import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.*;
import cn.iocoder.yudao.module.member.dal.dataobject.user.*;
import cn.iocoder.yudao.module.member.dal.mysql.user.*;
import cn.iocoder.yudao.module.system.enums.common.BooleanEnum;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 会员卡 Service 实现类
 *
 * @author 系统管理员
 */
@Service
@Validated
public class MemberCardServiceImpl implements MemberCardService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private MemberCardMapper memberCardMapper;
    @Resource
    private MemberUserMapper memberUserMapper;
    @Resource
    private MemberCardChangeRecordMapper memberCardChangeRecordMapper;
    @Resource
    private MemberCardGiftRecordMapper memberCardGiftRecordMapper;
    @Resource
    private MemberCardIssuanceRecordMapper memberCardIssuanceRecordMapper;
    @Resource
    private MemberCardIssuanceRecordMapper memberCardReissueRecordMapper;


    @Override
    public List<MemberCardDO> getMemberCardByMemberUserId(Long memberUserId) {
        return memberCardMapper.selectList(new LambdaQueryWrapperX<MemberCardDO>()
                .eq(MemberCardDO::getMemberUserId, memberUserId)
                .eq(MemberCardDO::getStatus, BooleanEnum.TRUE.getValue()));
    }

    @Override
    public List<MemberCardDO> getAllMemberCardByMemberUserId(Long memberUserId) {
        return memberCardMapper.selectList(new LambdaQueryWrapperX<MemberCardDO>()
                .eq(MemberCardDO::getMemberUserId, memberUserId));
    }

    @Override
    public List<MemberCardDO> getMemberCardListByIdCard(String idCard) {
        List<MemberUserDO> memberUserList = memberUserMapper
                .selectList(new LambdaQueryWrapperX<MemberUserDO>()
                        .eq(MemberUserDO::getIdNumber, idCard));

        if (CollectionUtil.isEmpty(memberUserList)){
            return null;
        }
        MemberUserDO memberUserDO = memberUserList.stream().findFirst().get();
        return memberCardMapper.selectList(new LambdaQueryWrapperX<MemberCardDO>()
                .eq(MemberCardDO::getMemberUserId, memberUserDO.getId()));
    }

    @Override
    public void update(MemberCardDO memberCardDO) {
        this.memberCardMapper.updateById(memberCardDO);
    }

    @Override
    public void recharge(MemberCardRechargeSaveVO saveVO) {
        MemberCardGiftRecordDO giftRecord = saveVO.getGiftRecord();
        MemberCardChangeRecordDO rechargeRecord = saveVO.getRechargeRecord();

        MemberCardDO dbMemberCard = memberCardMapper.selectById(rechargeRecord.getMemberCardId());
        //变更前金额
        BigDecimal beforeChangeAmount = dbMemberCard.getBalanceAmount();
        //变更后金额
        BigDecimal afterChangeAmount = beforeChangeAmount.add(rechargeRecord.getAmount());
        rechargeRecord.setBeforeChangeAmount(beforeChangeAmount);
        rechargeRecord.setAfterChangeAmount(afterChangeAmount);
        rechargeRecord.setCreatedTime(new Date());
        rechargeRecord.setType(1); //充值
        rechargeRecord.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        rechargeRecord.setCreatedTime(new Date());
        rechargeRecord.setOrgType(dbMemberCard.getOrgType());
        if (giftRecord != null) {
            rechargeRecord.setGiftAmount(giftRecord.getAmount());
            dbMemberCard.setGiftAmount(dbMemberCard.getGiftAmount().add(rechargeRecord.getGiftAmount()));
        }
        memberCardChangeRecordMapper.insert(rechargeRecord);

        //更新会员卡金额
        dbMemberCard.setBalanceAmount(afterChangeAmount);
        dbMemberCard.setAccumulatedAmount(dbMemberCard.getAccumulatedAmount().add(rechargeRecord.getAmount())); //累计充值金额
        memberCardMapper.updateById(dbMemberCard);

        if (giftRecord != null) {
            giftRecord.setCreatedTime(new Date());
            giftRecord.setType(1);
            giftRecord.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
            memberCardGiftRecordMapper.insert(giftRecord);
        }
    }

    @Override
    public void issuanceCard(MemberCardDO memberCardDO) {
        memberCardDO.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        memberCardDO.setCreatedTime(new Date());
        memberCardMapper.insert(memberCardDO);

        MemberUserDO memberUserDO = memberUserMapper.selectById(memberCardDO.getMemberUserId());
        //创建发卡记录
        MemberCardIssuanceRecordDO memberCardIssuanceRecordDO = new MemberCardIssuanceRecordDO();
        memberCardIssuanceRecordDO.setNewCardName(memberCardDO.getMemberCardName());
        memberCardIssuanceRecordDO.setNewCardNumber(memberCardDO.getMemberCardNumber());
        memberCardIssuanceRecordDO.setNewCardId(memberCardDO.getId());
        memberCardIssuanceRecordDO.setMemberUserId(memberCardDO.getMemberUserId());
        memberCardIssuanceRecordDO.setMemberUserName(memberUserDO.getName());
        memberCardIssuanceRecordDO.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        memberCardIssuanceRecordDO.setType(1); //发卡
        memberCardIssuanceRecordDO.setCreatedTime(new Date());
        memberCardIssuanceRecordMapper.insert(memberCardIssuanceRecordDO);
    }

    @Override
    public void reissueCard(MemberCardIssuanceRecordDO reissueRecordDO) {
        //查询旧卡金额
        MemberCardDO oldMemberCard = memberCardMapper.selectOne(new LambdaQueryWrapperX<MemberCardDO>().eq(MemberCardDO::getMemberCardNumber, reissueRecordDO.getOlderCardNumber()));
        //生成新会员卡
        MemberCardDO memberCardDO = new MemberCardDO();
        memberCardDO.setMemberUserId(reissueRecordDO.getMemberUserId());
        memberCardDO.setMemberCardName(reissueRecordDO.getNewCardName());
        memberCardDO.setMemberCardNumber(reissueRecordDO.getNewCardNumber());
        memberCardDO.setBalanceAmount(oldMemberCard.getBalanceAmount());
        memberCardDO.setGiftAmount(oldMemberCard.getGiftAmount());
        memberCardDO.setActivateCardTime(LocalDate.now());
        memberCardDO.setPermission(oldMemberCard.getPermission());
        memberCardDO.setAccumulatedAmount(oldMemberCard.getAccumulatedAmount());
        memberCardDO.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        memberCardDO.setCreatedTime(new Date());
        memberCardMapper.insert(memberCardDO);

        //旧卡禁用掉
        oldMemberCard.setStatus(BooleanEnum.FALSE.getValue());
        memberCardMapper.updateById(oldMemberCard);

        //创建补卡逻辑
        reissueRecordDO.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        reissueRecordDO.setCreatedTime(new Date());
        reissueRecordDO.setNewCardId(memberCardDO.getId());
        reissueRecordDO.setType(2); //补卡
        memberCardReissueRecordMapper.insert(reissueRecordDO);
    }

    @Override
    public void freezeOrThawCard(MemberCardDO updateVo) {
        MemberCardDO memberCardDO = memberCardMapper.selectById(updateVo.getId());
        memberCardDO.setStatus(updateVo.getStatus());
        memberCardMapper.updateById(memberCardDO);
    }

    @Override
    public void refund(MemberCardChangeRecordDO memberCardChangeRecordDO) {
        MemberCardDO memberCard = memberCardMapper.selectById(memberCardChangeRecordDO.getMemberCardId());
        memberCardChangeRecordDO.setType(2); //退费
        memberCardChangeRecordDO.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        memberCardChangeRecordDO.setCreatedTime(new Date());
        memberCardChangeRecordDO.setBeforeChangeAmount(memberCard.getBalanceAmount());
        memberCardChangeRecordDO.setOrgType(memberCard.getOrgType());
        BigDecimal afterChangeAmount = memberCard.getBalanceAmount().subtract(memberCardChangeRecordDO.getAmount());
        memberCardChangeRecordDO.setAfterChangeAmount(afterChangeAmount);
        memberCardChangeRecordMapper.insert(memberCardChangeRecordDO);

        memberCard.setBalanceAmount(afterChangeAmount);
        memberCardMapper.updateById(memberCard);
    }

    @Override
    public PageResult<MemberCardChangeRecordDO> getMemberCardConsumptionRecordPage(MemberCardChangePageReqVO pageReqVO) {
        return memberCardChangeRecordMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<MemberCardIssuanceRecordDO> getMemberCardChangeRecordPage(MemberCardIssuancePageReqVO pageReqVO) {
        return memberCardIssuanceRecordMapper.selectPage(pageReqVO);
    }

    @Override
    public Long selectMemberCardCount(Long memberUserId) {
        Long count = memberCardMapper.selectCount(new LambdaQueryWrapperX<MemberCardDO>().eq(MemberCardDO::getMemberUserId, memberUserId)
                .eq(MemberCardDO::getStatus, BooleanEnum.TRUE.getValue()));
        return count == null ? 0L : count;
    }

    @Override
    public List<MemberCardDO> getEmployeeMemberCardList() {
        List<MemberCardDO> employeeMemberCardList = memberCardMapper.getEmployeeMemberCardList();
        for (MemberCardDO memberCardDO : employeeMemberCardList) {
            Integer rechargeCount = (Integer) redisTemplate.opsForValue().get(memberCardDO.getMemberCardNumber());
            memberCardDO.setRechargeCount(rechargeCount == null ? 0 : rechargeCount);
        }
        return employeeMemberCardList;
    }

    @Override
    public void batchRecharge(MemberCardBatchRechargeSaveVO batchRechargeSaveVO) {
        BigDecimal amount = batchRechargeSaveVO.getAmount(); //充值金额
        List<MemberCardDO> memberCards = batchRechargeSaveVO.getMemberCards();

        for (MemberCardDO memberCard : memberCards) {
            String redisKey = memberCard.getMemberCardNumber();
            Integer obj = (Integer) redisTemplate.opsForValue().get(redisKey);
            if (obj == null) {
                //充值金额
                BigDecimal balanceAmount = memberCard.getBalanceAmount();
                BigDecimal afterChangeAmount = balanceAmount.add(amount);
                memberCard.setBalanceAmount(afterChangeAmount);
                memberCard.setAccumulatedAmount(memberCard.getAccumulatedAmount().add(amount));
                memberCardMapper.updateById(memberCard);

                //生成充值记录
                MemberCardChangeRecordDO cardConsumptionRecord = new MemberCardChangeRecordDO();
                cardConsumptionRecord.setMemberUserId(memberCard.getMemberUserId());
                cardConsumptionRecord.setMemberUserName(memberCard.getMemberUserName());
                cardConsumptionRecord.setMemberCardId(memberCard.getId());
                cardConsumptionRecord.setCardName(memberCard.getMemberCardName());
                cardConsumptionRecord.setCardNumber(memberCard.getMemberCardNumber());
                cardConsumptionRecord.setBeforeChangeAmount(balanceAmount);
                cardConsumptionRecord.setAfterChangeAmount(afterChangeAmount);
                cardConsumptionRecord.setAmount(amount);
                cardConsumptionRecord.setType(1);
                cardConsumptionRecord.setPayType(batchRechargeSaveVO.getPayType());
                cardConsumptionRecord.setPayTypeName(batchRechargeSaveVO.getPayTypeName());
                cardConsumptionRecord.setRemarks("员工批量充值");
                cardConsumptionRecord.setCreatedTime(new Date());
                cardConsumptionRecord.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
                cardConsumptionRecord.setOrgType(memberCard.getOrgType());
                memberCardChangeRecordMapper.insert(cardConsumptionRecord);

                //计算充值次数
                LocalDateTime now = LocalDateTime.now();
                LocalDate nextMonthFirstDay = now.toLocalDate().plusMonths(1).withDayOfMonth(1);
                LocalDateTime nextMonthFirstZeroHour = LocalDateTime.of(nextMonthFirstDay, LocalTime.MIN);
                Duration duration = Duration.between(now, nextMonthFirstZeroHour);
                long hoursBetween = duration.toHours();
                if (obj == null) {
                    redisTemplate.opsForValue().set(redisKey, 1, hoursBetween * 60, TimeUnit.MINUTES);
                }
            }
        }
    }

    @Override
    public PageResult<MemberCardPageRespVO> getCardPage(MemberCardPageReqVO pageReqVO) {
        Page<MemberCardPageRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<MemberCardPageRespVO> list = memberCardMapper.getCardPage(page, pageReqVO);
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public MemberCardRespVO getCardDetail(Long memberCardId) {
        MemberCardDO memberCard = memberCardMapper.selectById(memberCardId);
        MemberCardRespVO respVO = new MemberCardRespVO();
        BeanUtils.copyProperties(memberCard, respVO);
        respVO.setMemberUser(memberUserMapper.selectById(memberCard.getMemberUserId()));
        return respVO;
    }

    @Override
    public PageResult<MemberCardIssuanceRecordDO> statisticsMemberCardIssuanceRecordPage(MemberCardIssuancePageReqVO pageReqVO) {
        Page<MemberCardIssuanceRecordDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<MemberCardIssuanceRecordDO> list = memberCardIssuanceRecordMapper.statisticsMemberCardIssuanceRecordPage(page, pageReqVO);
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public PageResult<MemberCardChangeRecordDO> statisticsMemberCardConsumptionRecordPage(MemberCardChangePageReqVO pageReqVO) {
        Page<MemberCardChangeRecordDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<MemberCardChangeRecordDO> list = memberCardChangeRecordMapper.statisticsMemberCardConsumptionRecordPage(page, pageReqVO);
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public Map<String, Object> statisticsRechargeAmount(MemberCardChangePageReqVO pageReqVO) {
        List<MemberCardChangeRecordDO> list = memberCardChangeRecordMapper.statisticsRechargeAmount(pageReqVO);
        BigDecimal totalAmount = BigDecimal.ZERO; //总计
        BigDecimal cashAmount = BigDecimal.ZERO;  //现金
        BigDecimal wxPayAmount = BigDecimal.ZERO; //微信
        BigDecimal bankCardPayAmount = BigDecimal.ZERO;  //银行转账
        BigDecimal couponAmount = BigDecimal.ZERO;       //优惠券
        BigDecimal lossAmount = BigDecimal.ZERO;       //院方损失
        BigDecimal cloudPayAmount = BigDecimal.ZERO;       //云缴费
        BigDecimal longTermInsuranceAmount = BigDecimal.ZERO; //长护险
        for (MemberCardChangeRecordDO cardConsumptionRecordDO : list) {
            String payType = cardConsumptionRecordDO.getPayType();
            BigDecimal amount = cardConsumptionRecordDO.getAmount();
            totalAmount = totalAmount.add(amount);
            switch (payType) {
                case "1":
                    bankCardPayAmount = bankCardPayAmount.add(amount);
                    break;
                case "2":
                    cashAmount = cashAmount.add(amount);
                    break;
                case "3":
                    couponAmount = couponAmount.add(amount);
                    break;
                case "4":
                    lossAmount = lossAmount.add(amount);
                    break;
                case "5":
                    longTermInsuranceAmount = longTermInsuranceAmount.add(amount);
                    break;
                case "6":
                    wxPayAmount = wxPayAmount.add(amount);
                    break;
                case "7":
                    cloudPayAmount = cloudPayAmount.add(amount);
                    break;
            }
        }
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("totalAmount", totalAmount);
        dataMap.put("cashAmount", cashAmount);
        dataMap.put("wxPayAmount", wxPayAmount);
        dataMap.put("bankCardPayAmount", bankCardPayAmount);
        dataMap.put("couponAmount", couponAmount);
        dataMap.put("lossAmount", lossAmount);
        dataMap.put("cloudPayAmount", cloudPayAmount);
        dataMap.put("longTermInsuranceAmount", longTermInsuranceAmount);
        return dataMap;
    }

    @Override
    public PageResult<MemberCardStatisticReportVO> getMemberCardStatisticalReportPage(MemberCardPageReqVO pageReqVO) {
        Page<MemberCardStatisticReportVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<MemberCardStatisticReportVO> list = memberCardMapper.getMemberCardStatisticalReportPage(page, pageReqVO);
        for (MemberCardStatisticReportVO reportVO : list) {
            BigDecimal totalGiftAmount;       //累计赠送金额
            BigDecimal consumptionAmount;     //累计消费金额
            BigDecimal giftConsumptionAmount; //赠送消费金额
            BigDecimal refundAmount;          //退款金额

            List<MemberCardGiftRecordDO> memberCardGiftRecords = memberCardGiftRecordMapper.selectList(new LambdaQueryWrapperX<MemberCardGiftRecordDO>()
                    .eq(MemberCardGiftRecordDO::getMemberCardId, reportVO.getId()));

            totalGiftAmount = memberCardGiftRecords.stream().filter(e -> e.getType() == 1)
                    .map(MemberCardGiftRecordDO::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            giftConsumptionAmount = memberCardGiftRecords.stream().filter(e -> e.getType() == 2)
                    .map(MemberCardGiftRecordDO::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);


            List<MemberCardChangeRecordDO> memberCardConsumptionRecords = memberCardChangeRecordMapper
                    .selectList(new LambdaQueryWrapperX<MemberCardChangeRecordDO>()
                            .eq(MemberCardChangeRecordDO::getMemberCardId, reportVO.getId()));

            consumptionAmount = memberCardConsumptionRecords.stream().filter(i -> i.getType() == 3)
                    .map(MemberCardChangeRecordDO::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            refundAmount = memberCardConsumptionRecords.stream().filter(i -> i.getType() == 2)
                    .map(MemberCardChangeRecordDO::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            reportVO.setTotalGiftAmount(totalGiftAmount);
            reportVO.setConsumptionAmount(consumptionAmount);
            reportVO.setGiftConsumptionAmount(giftConsumptionAmount);
            reportVO.setRefundAmount(refundAmount);
        }
        return new PageResult<>(list, page.getTotal());
    }
}