package com.ym.mec.biz.service.impl;

import com.alibaba.fastjson.JSON;
import com.ym.mec.auth.api.client.SysUserFeignService;
import com.ym.mec.auth.api.entity.SysUser;
import com.ym.mec.biz.dal.dao.StudentPaymentOrderDao;
import com.ym.mec.biz.dal.dao.SysCouponCodeDao;
import com.ym.mec.biz.dal.dao.SysCouponDao;
import com.ym.mec.biz.dal.dto.HorseRaceLampDto;
import com.ym.mec.biz.dal.dto.SysCouponCodeDto;
import com.ym.mec.biz.dal.entity.*;
import com.ym.mec.biz.dal.enums.CouponTypeEnum;
import com.ym.mec.biz.dal.page.SysCouponCodeQueryInfo;
import com.ym.mec.biz.service.SysCouponCodeService;
import com.ym.mec.biz.service.SysCouponService;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.page.QueryInfo;
import com.ym.mec.common.page.WrapperUtil;
import com.ym.mec.common.service.IdGeneratorService;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.common.tenant.TenantContextHolder;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

@Service
public class SysCouponCodeServiceImpl extends BaseServiceImpl<Long, SysCouponCode> implements SysCouponCodeService {

    @Autowired
    private SysCouponCodeDao sysCouponCodeDao;
    @Autowired
    private SysCouponService sysCouponService;
    @Autowired
    private SysCouponDao sysCouponDao;
    @Autowired
    private IdGeneratorService idGeneratorService;
    @Autowired
    private StudentPaymentOrderDao studentPaymentOrderDao;

    @Autowired
    private SysUserFeignService sysUserFeignService;

    @Override
    public BaseDAO<Long, SysCouponCode> getDAO() {
        return sysCouponCodeDao;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public boolean exchangeCouponTest(Integer userId, Integer couponId, Long paymentOrderId, Integer exchangeNum) {
        return exchangeCoupon(userId, couponId, paymentOrderId, exchangeNum, 0, null);
    }

    /**
     * 兑换优惠券
     *
     * @param userId:         用户编号
     * @param couponId:       优惠券编号
     * @param paymentOrderId: 订单编号
     * @param exchangeNum:    兑换数量
     * @param type:           2有paymentOrderId属于订单送券,交易获取，0手动领取 1手动发放
     */
    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public boolean exchangeCoupon(Integer userId, Integer couponId, Long paymentOrderId, Integer exchangeNum, int type, Integer issueId) {
        //检查参数
        SysCouponCode sysCouponCode = checkParam(userId, couponId, paymentOrderId, exchangeNum, type, issueId);
        //兑换数量0或者比0小直接返回
        if (exchangeNum <= 0) {
            return true;
        }
        //校验优惠券是否合法
        SysCoupon sysCoupon = checkCoupon(userId, couponId, exchangeNum);
        //写入数据
        opsCoupon(userId, sysCouponCode, couponId, exchangeNum, sysCoupon);
        return true;
    }

    private SysCouponCode checkParam(Integer userId, Integer couponId, Long paymentOrderId, Integer exchangeNum, int type, Integer issueId) {
        if (type > 2 || type < 0) {
            throw new BizException("优惠券获取途径的参数错误!");
        }
        SysCouponCode sysCouponCode = new SysCouponCode();
        //校验参数
        if (WrapperUtil.checkObj(userId, couponId, exchangeNum)) {
            //2 订单满减
            if (type == 2 && Objects.nonNull(paymentOrderId)) {
                StudentPaymentOrder studentPaymentOrder = studentPaymentOrderDao.get(paymentOrderId);
                if (Objects.isNull(studentPaymentOrder)) {
                    throw new BizException("无效订单");
                }
                sysCouponCode.setPaymentOrderId(paymentOrderId);
            } else if (type == 1 && Objects.nonNull(issueId)) {
                //1 手动发券
                sysCouponCode.setIssueId(issueId);
            }
            //0 手动领券
        } else {
            throw new BizException("领取失败,参数错误!");
        }
        sysCouponCode.setType(type);
        return sysCouponCode;
    }

    private SysCoupon checkCoupon(Integer userId, Integer couponId, Integer exchangeNum) {
        //锁定优惠券
        SysCoupon sysCoupon = sysCouponDao.lockCoupon(couponId);

        Date now = new Date();
        //如果优惠券不存在，或者已停用，或者不在领取有效期内，或者已消耗完，则此优惠券无效
        Optional<SysCoupon> optional = Optional.ofNullable(sysCoupon);
        optional.orElseThrow(() -> new BizException("未查询到优惠券信息!"));
        optional.filter(c -> 1 == c.getStatus()).orElseThrow(() -> new BizException("优惠券已被停用，请刷新!"));
        optional.filter(c -> now.compareTo(c.getStartDate()) > -1).orElseThrow(() -> new BizException("未到领取时间!"));
        optional.filter(c -> now.compareTo(c.getEndDate()) < 0).orElseThrow(() -> new BizException("优惠券已过期!"));
        optional.filter(c -> c.getStockCount() == -1
                        || (c.getStockCount() > -1 && c.getStockCount() >= c.getConsumeNum() + exchangeNum))
                .orElseThrow(() -> new BizException("优惠券库存不足!"));

        // 检测学生能否领取优惠券
        checkUser(userId,sysCoupon);

        //校验兑换的最大次数
        if (sysCoupon.getLimitExchangeNum() != -1) {
            int userUserNum = sysCouponCodeDao.countWithUserAndCoupon(userId, couponId);
            //如果用户使用数量超过优惠券限制则领取无效
            if (userUserNum + exchangeNum > sysCoupon.getLimitExchangeNum()) {
                throw new BizException("当前优惠券最多可领取{}次", sysCoupon.getLimitExchangeNum());
            }
        }
        return sysCoupon;
    }

    private void checkUser(Integer userId, SysCoupon sysCoupon) {
        SysUser sysUser = sysUserFeignService.queryUserById(userId);
        if (StringUtils.isBlank(sysCoupon.getUseCondition())) {
            return;
        }
        UseCondition useCondition = JSON.parseObject(sysCoupon.getUseCondition(), UseCondition.class);
        // 检测分部
        if (StringUtils.isNotBlank(useCondition.getOrganId())) {
            List<String> organIdList = Arrays.asList(useCondition.getOrganId().split(","));
            if (!organIdList.contains(sysUser.getOrganId().toString())) {
                throw new BizException("学生不在优惠券适用分部");
            }

        }
    }

    private void opsCoupon(Integer userId, SysCouponCode sysCouponCode, Integer couponId, Integer exchangeNum, SysCoupon sysCoupon) {
        Integer tenantId = TenantContextHolder.getTenantId();
        Date now = new Date();
        sysCouponCode.setUserId(userId);
        sysCouponCode.setCouponId(couponId);
        sysCouponCode.setUsageStatus(0);
        sysCouponCode.setTenantId(tenantId);
        switch (sysCoupon.getEffectiveType()) {
            case DAYS:
                sysCouponCode.setUseStartDate(now);
                sysCouponCode.setUseDeadlineDate(DateUtil.addDays(now, sysCoupon.getDeadline()));
                break;
            case TIME_BUCKET:
                sysCouponCode.setUseStartDate(sysCoupon.getEffectiveStartTime());
                sysCouponCode.setUseDeadlineDate(sysCoupon.getEffectiveExpireTime());
                break;
            default:
                throw new BizException("无效优惠券", sysCoupon.getLimitExchangeNum());
        }
        List<SysCouponCode> couponCodes = new ArrayList<>();
        for (int i = 0; i < exchangeNum; i++) {
            SysCouponCode couponCode = new SysCouponCode();
            BeanUtils.copyProperties(sysCouponCode, couponCode);
            couponCode.setCode(String.valueOf(idGeneratorService.generatorId("coupon")));
            couponCodes.add(couponCode);
        }
        sysCouponCodeDao.batchInsert(couponCodes);

        Map<String, Object> updateParam = new HashMap<>();
        updateParam.put("exchangeNum", exchangeNum);
        updateParam.put("id", sysCoupon.getId());
        updateParam.put("tenantId", tenantId);
        if (sysCoupon.getWarningStatus() == 0 && sysCoupon.getStockCount() != -1 && sysCoupon.getStockCount() - sysCoupon.getConsumeNum() < sysCoupon.getWarningStockNum()) {
//            sysCouponService.stockWarning(sysCoupon.getId(), sysCoupon.getName());
            updateParam.put("warningStatus", 1);
        }

        //有的优惠券是无限库存模式，所以不需要校验库存 checkStock有值则需要校验
        if (sysCoupon.getStockCount() > 0) {
            updateParam.put("checkStock", 1);
        }
        //扣减库存操作
        if (sysCouponDao.updateExchange(updateParam) != 1) {
            throw new BizException("优惠券库存不足!");
        }
    }

    @Override
    public PageInfo<SysCouponCodeDto> querySysCouponUseList(SysCouponCodeQueryInfo queryInfo) {
        PageInfo<SysCouponCodeDto> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<String, Object>();
        MapUtil.populateMap(params, queryInfo);

        List<SysCouponCodeDto> dataList = new ArrayList<>();
        int count = sysCouponCodeDao.countSysCouponUseList(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = sysCouponCodeDao.querySysCouponUseList(params);
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public PageInfo<HorseRaceLampDto> queryHorseRaceLampDtoList(QueryInfo queryInfo) {
        PageInfo<HorseRaceLampDto> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<String, Object>();
        MapUtil.populateMap(params, queryInfo);

        List<HorseRaceLampDto> dataList = new ArrayList<>();
        int count = sysCouponCodeDao.countHorseRaceLampDtoList(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = sysCouponCodeDao.queryHorseRaceLampDtoList(params);
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public StudentPaymentOrder use(List<Integer> couponIdList, BigDecimal amount, Boolean useFlag) {
        return use(couponIdList, amount, useFlag, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public StudentPaymentOrder use(List<Integer> couponIdList, BigDecimal amount, Boolean useFlag, String... allowType) {
        StudentPaymentOrder studentPaymentOrder = new StudentPaymentOrder();
        if (CollectionUtils.isNotEmpty(couponIdList)) {
            BigDecimal fullAmount = BigDecimal.ZERO;
            BigDecimal faceAmount = BigDecimal.ZERO;
            //校验优惠券
            List<SysCouponCodeDto> couponCodeDtoList = checkCoupon(couponIdList, allowType);

            for (SysCouponCodeDto sysCouponCodeDto : couponCodeDtoList) {
                CouponTypeEnum couponType = sysCouponCodeDto.getCouponType();
                switch (couponType) {
                    case DISCOUNT:
                        throw new BizException("操作失败：暂不支持折扣劵");
                    case FULL_REDUCTION:
                        fullAmount = fullAmount.add(sysCouponCodeDto.getFullAmount());
                        faceAmount = faceAmount.add(sysCouponCodeDto.getFaceValue());
                        break;
                    default:
                        throw new BizException("操作失败：错误的优惠券类型");
                }
            }

            if (amount.compareTo(fullAmount) < 0) {
                throw new BizException("操作失败：当前消费金额不满足优惠券满减条件");
            }
            amount = amount.subtract(faceAmount);
            if (amount.signum() < 0) {
                amount = BigDecimal.ZERO;
            }
            //使用优惠券
            if (useFlag) {
                useCoupon(couponIdList);
            }
            studentPaymentOrder.setCouponCodeId(StringUtils.join(couponIdList, ","));
            studentPaymentOrder.setCouponRemitFee(faceAmount);
        }
        studentPaymentOrder.setExpectAmount(amount);
        studentPaymentOrder.setActualAmount(amount);
        return studentPaymentOrder;
    }

    @Override
    public List<SysCouponCodeDto> checkCoupon(List<Integer> couponIdList, String... allowType) {
        Date date = new Date();
        List<SysCouponCodeDto> couponCodeDtoList = findByIdList(couponIdList);
        if (CollectionUtils.isEmpty(couponCodeDtoList)) {
            throw new BizException("操作失败：未查询到优惠券");
        }
        if (couponIdList.size() != couponCodeDtoList.size()) {
            throw new BizException("操作失败：优惠券数据异常");
        }

        for (SysCouponCodeDto sysCouponCodeDto : couponCodeDtoList) {
            if (sysCouponCodeDto.getUsageStatus() == 1) {
                throw new BizException("操作失败：优惠券已使用");
            }
            if (sysCouponCodeDto.getUseStartDate().after(date)) {
                throw new BizException("操作失败：不在优惠券使用期限");
            }
            if (date.after(sysCouponCodeDto.getUseDeadlineDate())) {
                throw new BizException("操作失败：优惠券已过期");
            }
            //该商品只允许使用 allowType参数内的优惠券类型,null则不验证
            if (Objects.nonNull(allowType) && !WrapperUtil.checkStr(sysCouponCodeDto.getTypeDetail(), allowType)) {
                throw new BizException("操作失败 : 优惠券类型使用错误");
            }
        }
        return couponCodeDtoList;
    }

    /**
     * 获取优惠券相关参数-校验优惠券和传入的支付金额是否合法
     *
     * @param couponIdList 优惠券集合
     * @param allowType    本次允许使用的优惠券类型,null则不验证
     */
    @Override
    public CouponPayParam getCouponPayParam(List<Integer> couponIdList, String... allowType) {
        if (CollectionUtils.isEmpty(couponIdList)) {
            return null;
        }
        //查询本次付款使用的优惠券
        List<SysCouponCodeDto> sysCouponCodeDtoList = checkCoupon(couponIdList, allowType);
        //优惠券减免的总额度
        BigDecimal couponRemitTotal = WrapperUtil.sumList(sysCouponCodeDtoList, SysCouponCodeDto::getFaceValue);

        //将各种类型的优惠券合并 算出总面试 总减免金额
        Map<String, CouponPayTypeInfo> couponTypeInfo = new HashMap<>();
        //根据CouponDetailTypeEnum 进行分组集合
        WrapperUtil.groupList(sysCouponCodeDtoList, SysCouponCodeDto::getTypeDetail)
                .forEach((typeDetail, list) -> {
                    CouponPayTypeInfo coupon = new CouponPayTypeInfo();
                    list.forEach(c -> {
                        coupon.setCouponAmount(coupon.getCouponAmount().add(c.getFullAmount()));
                        coupon.setFaceValue(coupon.getFaceValue().add(c.getFaceValue()));
                    });
                    coupon.setTypeDetail(typeDetail);
                    couponTypeInfo.put(typeDetail, coupon);
                });

        CouponPayParam result = new CouponPayParam();
        result.setCouponRemitTotal(couponRemitTotal);
        result.setCouponTypeInfo(couponTypeInfo);
        return result;
    }

    /**
     * 获取优惠券相关参数-校验优惠券和传入的支付金额是否合法
     *
     * @param couponIdList 优惠券集合
     * @param total        本次订单所有商品的总数量
     * @param payAmount    页面传入的本次支付的金额
     */
    @Override
    public CouponPayParam getCouponPayParam(List<Integer> couponIdList) {
        return getCouponPayParam(couponIdList, null);
    }

    @Override
    public List<SysCouponCodeDto> findByIdList(List<Integer> couponIdList) {
        return sysCouponCodeDao.findByIdList(couponIdList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void useCoupon(List<Integer> couponIdList) {
        sysCouponCodeDao.use(couponIdList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void quit(String couponCodeId) {
        if (StringUtils.isEmpty(couponCodeId)) {
            return;
        }
        sysCouponCodeDao.quit(couponCodeId);
    }
}
