package com.hfkj.service.discount.impl;

import com.hfkj.common.exception.BaseException;
import com.hfkj.common.exception.ErrorCode;
import com.hfkj.common.exception.ErrorHelp;
import com.hfkj.common.exception.SysCode;
import com.hfkj.dao.BsDiscountUserMapper;
import com.hfkj.entity.*;
import com.hfkj.model.DiscountUserModel;
import com.hfkj.model.ReceiveDiscountPkModel;
import com.hfkj.service.discount.*;
import com.hfkj.service.user.BsUserService;
import com.hfkj.sysenum.discount.*;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @className: BsDiscountUserServiceImpl
 * @author: HuRui
 * @date: 2024/8/29
 **/
@Service("discountUserService")
public class BsDiscountUserServiceImpl implements BsDiscountUserService {
    @Resource
    private BsDiscountUserMapper discountUserMapper;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private BsDiscountService discountService;
    @Resource
    private BsDiscountStockCodeService discountStockCodeService;
    @Resource
    private BsDiscountPkService discountPkService;
    @Resource
    private BsDiscountPkRelService discountPkRelService;
    @Resource
    private BsDiscountPkStockCodeService discountPkStockCodeService;
    @Resource
    private BsDiscountUserGiveRecordService discountUserGiveRecordService;
    @Resource
    private BsUserService userService;

    private static final String RECEIVE_DISCOUNT_LOCK_KEY = "RECEIVE_DISCOUNT";
    private static final String RECEIVE_DISCOUNT_PK_LOCK_KEY = "RECEIVE_DISCOUNT_PK";
    private static final String USE_DISCOUNT_LOCK_KEY = "USE_DISCOUNT";

    @Override
    public void editData(BsDiscountUser data) {
        data.setUpdateTime(new Date());
        if (data.getId() == null) {
            data.setCreateTime(new Date());
            discountUserMapper.insert(data);
        } else {
            discountUserMapper.updateByPrimaryKey(data);
        }
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public List<BsDiscountUser> receive(String discountNo, Integer number, DiscountStockCodeObtainTypeEnum obtainType, String userPhone,String agentAppid) {
        // 锁编号
        String lockKey = RECEIVE_DISCOUNT_LOCK_KEY+"_"+discountNo;
        // 获取锁
        Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "");
        if (Boolean.TRUE.equals(lock)) {
            try {
                // 锁超时时间 10秒
                redisTemplate.expire(lockKey, 10, TimeUnit.SECONDS);
                // 查询优惠券
                BsDiscount discount = discountService.getDetail(discountNo);
                if (discount == null) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未知的优惠券");
                }
                if (!discount.getStatus().equals(DiscountStatusEnum.status2.getCode())) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "无法领取！优惠券未上线或已结束");
                }
                if (new Date().compareTo(discount.getEndTime()) >= 1) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "无法领取！优惠券已结束");
                }
                // 查询用户
                BsUser user = userService.getUser(userPhone);
                if (user == null) {
                    // 注册
                    Map<String, Object> other = new HashMap<>();
                    other.put("agentAppid", agentAppid);
                    user = userService.register(userPhone, other);
                }
                // 获取库存code
                List<BsDiscountStockCode> discountStockCodeList = discountStockCodeService.getAssignCodeList(discountNo, number);
                // 领取的优惠券编码
                List<BsDiscountUser> discountUserList = new ArrayList<>();
                for (BsDiscountStockCode discountStockCode : discountStockCodeList) {
                    discountStockCode.setObtainType(obtainType.getCode());
                    discountStockCode.setObtainTime(new Date());
                    discountStockCode.setReceiveMerUserId(user.getId());
                    discountStockCode.setReceiveMerUserPhone(user.getPhone());
                    discountStockCode.setStatus(DiscountStockCodeStatusEnum.status2.getCode());
                    discountStockCode.setAgentAppid(agentAppid);
                    discountStockCodeService.update(discountStockCode);

                    // 优惠券绑定用户
                    BsDiscountUser discountUser = new BsDiscountUser();
                    discountUser.setUserId(user.getId());
                    discountUser.setUserPhone(user.getPhone());
                    discountUser.setDiscountId(discount.getId());
                    discountUser.setDiscountNo(discount.getDiscountNo());
                    discountUser.setDiscountName(discount.getDiscountName());
                    discountUser.setDiscountType(discount.getDiscountType());
                    discountUser.setOverlayDiscount(discount.getOverlayDiscount());
                    discountUser.setDiscountCondition(discount.getDiscountCondition());
                    discountUser.setDiscountPrice(discount.getDiscountPrice());
                    discountUser.setDiscountStockCode(discountStockCode.getId());
                    discountUser.setUseScope(discount.getUseScope());
                    discountUser.setStatus(DiscountUserStatusEnum.type1.getCode());
                    discountUser.setAgentAppid(agentAppid);
                    // 计算使用有效期
                    Calendar expirationDate = Calendar.getInstance();
                    expirationDate.setTime(new Date());
                    expirationDate.set(Calendar.HOUR_OF_DAY, 23);
                    expirationDate.set(Calendar.MINUTE, 59);
                    expirationDate.set(Calendar.SECOND, 59);
                    expirationDate.add(Calendar.DATE, discount.getReceiveExpirationDate());
                    // 领取有效期是否超过 优惠券结束时间
                    if (expirationDate.getTime().compareTo(discount.getEndTime()) == 1) {
                        // 领取有效期超过优惠券结束时间，直接取优惠券结束时间
                        discountUser.setExpirationDate(discount.getEndTime());
                    } else {
                        discountUser.setExpirationDate(expirationDate.getTime());
                    }
                    editData(discountUser);

                    discountUserList.add(discountUser);
                }
                // 释放锁
                redisTemplate.delete(lockKey);
                return discountUserList;

            } catch (BaseException e) {
                // 释放锁
                redisTemplate.delete(lockKey);
                throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, e.getErrorMsg());

            } catch (Exception e) {
                // 释放锁
                redisTemplate.delete(lockKey);
                throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "领取优惠券，遇到未知问题");
            }
        } else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            receive(discountNo, number, obtainType, userPhone, agentAppid);
        }
        return new ArrayList<>();
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public List<ReceiveDiscountPkModel> receivePk(String discountPkNo, Integer number, DiscountStockCodeObtainTypeEnum obtainType, String userPhone, String agentAppid) {
        // 锁编号
        String lockKey = RECEIVE_DISCOUNT_PK_LOCK_KEY+"_"+discountPkNo;
        // 获取锁
        Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "");

        List<ReceiveDiscountPkModel> list = new LinkedList<>();
        if (Boolean.TRUE.equals(lock)) {
            try {
                // 锁超时时间 10秒
                redisTemplate.expire(lockKey, 10, TimeUnit.SECONDS);
                // 查询优惠券包
                BsDiscountPk discountPk = discountPkService.getDetail(discountPkNo);
                if (discountPk == null) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未知的优惠券包");
                }
                if (!discountPk.getStatus().equals(DiscountPkStatusEnum.status2.getCode())) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "优惠券状态错误，未上架或已下线！");
                }
                // 查询用户
                BsUser user = userService.getUser(userPhone);
                if (user == null) {
                    // 注册
                    Map<String, Object> other = new HashMap<>();
                    other.put("agentAppid", agentAppid);
                    user = userService.register(userPhone, other);
                }
                // 优惠券包库存编码
                List<BsDiscountPkStockCode> codeList = discountPkStockCodeService.getAssignCodeList(discountPkNo, number);
                for (BsDiscountPkStockCode code : codeList) {
                    // 库存领取数据
                    List<BsDiscountUser> receiveList = new ArrayList<>();

                    code.setObtainType(obtainType.getCode());
                    code.setObtainTime(new Date());
                    code.setReceiveMerUserId(user.getId());
                    code.setReceiveMerUserPhone(user.getPhone());
                    code.setStatus(DiscountPkStockCodeStatusEnum.status2.getCode());
                    code.setAgentAppid(agentAppid);
                    discountPkStockCodeService.update(code);

                    // 查询优惠券包下的优惠券
                    List<BsDiscountPkRel> discountPkRelList = discountPkRelService.getList(discountPkNo);
                    for (BsDiscountPkRel discount : discountPkRelList) {
                        // 领取优惠券到用户账户
                        List<BsDiscountUser> discountUsers = receive(discount.getDiscountNo(), discount.getNumber(), obtainType, userPhone, agentAppid);
                        for (BsDiscountUser discountUser : discountUsers) {
                            // 优惠券库存编码
                            BsDiscountStockCode discountStockCode = discountStockCodeService.getDetail(discountUser.getDiscountStockCode());
                            if (discountStockCode != null) {
                                discountStockCode.setDiscountPkStockCode(code.getId());
                                discountStockCode.setDiscountPkId(discountPk.getId());
                                discountStockCode.setDiscountPkNo(discountPk.getDiscountPkNo());
                                discountStockCode.setDiscountPkName(discountPk.getDiscountPkName());
                                discountStockCodeService.update(discountStockCode);
                            }
                        }
                        receiveList.addAll(discountUsers);
                    }

                    ReceiveDiscountPkModel receiveDiscountPkModel = new ReceiveDiscountPkModel();
                    receiveDiscountPkModel.setDiscountPkNo(discountPkNo);
                    receiveDiscountPkModel.setDiscountPkStockCode(code.getId());
                    receiveDiscountPkModel.setPhone(userPhone);
                    receiveDiscountPkModel.setDiscountUserList(receiveList);
                    list.add(receiveDiscountPkModel);
                }
                return list;
            } catch (BaseException e) {
                // 释放锁
                redisTemplate.delete(lockKey);
                throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, e.getErrorMsg());

            } catch (Exception e) {
                // 释放锁
                redisTemplate.delete(lockKey);
                throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "领取优惠券，遇到未知问题");
            }
        } else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            receivePk(discountPkNo, number, obtainType, userPhone, agentAppid);
        }
        return new ArrayList<>();

    }

    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public BsDiscountUser userDiscount(Long id) {
        // 锁编号
        String lockKey = USE_DISCOUNT_LOCK_KEY+"_"+id;
        // 获取锁
        Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "");
        if (Boolean.TRUE.equals(lock)) {
            try {
                BsDiscountUser discountUser = getDetail(id);
                if (discountUser == null) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未知优惠券");
                }
                if (new Date().compareTo(discountUser.getExpirationDate()) >= 1) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "优惠券已过期，无法使用");
                }
                if (!discountUser.getStatus().equals(DiscountUserStatusEnum.type1.getCode())) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "优惠券无法使用，已被使用或已过期");
                }
                discountUser.setUseDate(new Date());
                discountUser.setStatus(DiscountUserStatusEnum.type2.getCode());
                editData(discountUser);

                // 查询优惠券库存
                BsDiscountStockCode stockCode = discountStockCodeService.getDetail(discountUser.getDiscountStockCode());
                if (stockCode != null) {
                    stockCode.setUseTime(discountUser.getUseDate());
                    stockCode.setStatus(DiscountStockCodeStatusEnum.status3.getCode());
                    discountStockCodeService.update(stockCode);
                }

                // 释放锁
                redisTemplate.delete(lockKey);
                return discountUser;

            } catch (BaseException e) {
                // 释放锁
                redisTemplate.delete(lockKey);
                throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, e.getErrorMsg());

            } catch (Exception e) {
                // 释放锁
                redisTemplate.delete(lockKey);
                throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "使用优惠券，遇到未知问题");
            }
        } else {
            userDiscount(id);
        }
        throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "使用优惠券，遇到未知问题");
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public BsDiscountUser revokeUserDiscount(Long id) {
        BsDiscountUser discountUser = getDetail(id);
        if (discountUser == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未知优惠券");
        }
        discountUser.setUseDate(null);
        discountUser.setStatus(DiscountUserStatusEnum.type1.getCode());
        editData(discountUser);

        // 查询优惠券库存
        BsDiscountStockCode stockCode = discountStockCodeService.getDetail(discountUser.getDiscountStockCode());
        if (stockCode != null) {
            stockCode.setUseTime(null);
            stockCode.setStatus(DiscountStockCodeStatusEnum.status2.getCode());
            discountStockCodeService.update(stockCode);
        }
        return discountUser;
    }

    @Override
    public BsDiscountUser getDetail(Long id) {
        return discountUserMapper.selectByPrimaryKey(id);
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRED, rollbackFor= {RuntimeException.class})
    public void give(Long codeId,Long giveUserId, String phone) {
        // 查询用户优惠券
        BsDiscountUser discountUser = getDetailByCodeId(codeId);
        if (discountUser == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未知的优惠券");
        }
        if (!discountUser.getStatus().equals(DiscountUserStatusEnum.type1.getCode())) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "优惠券无法赠送");
        }
        // 转赠人
        BsUser giveUser = userService.getUser(giveUserId);
        if (giveUser == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未找到转赠人信息");
        }
        if (!giveUser.getId().equals(discountUser.getUserId())) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "优惠券无法赠送");
        }
        if (giveUser.getPhone().equals(phone)) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "不可以赠送给自己");
        }
        // 查询接收人
        BsUser user = userService.getUser(phone);
        if (user == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "手机号未在系统中注册，无法赠送！");
        }
        discountUser.setUserId(user.getId());
        discountUser.setUserPhone(user.getPhone());
        editData(discountUser);
        // 记录
        BsDiscountUserGiveRecord giveRecord = new BsDiscountUserGiveRecord();
        giveRecord.setDiscountUserId(discountUser.getId());
        giveRecord.setDiscountStockCode(discountUser.getDiscountStockCode());
        giveRecord.setSendUserId(giveUser.getId());
        giveRecord.setSendUserPhone(giveUser.getPhone());
        giveRecord.setReceiveUserId(user.getId());
        giveRecord.setReceiveUserPhone(user.getPhone());
        discountUserGiveRecordService.create(giveRecord);
    }

    @Override
    public BsDiscountUser getDetailByCodeId(Long stockCodeId) {
        BsDiscountUserExample example = new BsDiscountUserExample();
        example.createCriteria().andDiscountStockCodeEqualTo(stockCodeId).andStatusNotEqualTo(DiscountUserStatusEnum.type0.getCode());
        example.setOrderByClause("create_time desc");
        List<BsDiscountUser> list = discountUserMapper.selectByExample(example);
        if (!list.isEmpty()) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public BsDiscountUser getDetailByUserCodeId(String phone, Long stockCodeId) {
        BsDiscountUserExample example = new BsDiscountUserExample();
        example.createCriteria()
                .andUserPhoneEqualTo(phone)
                .andDiscountStockCodeEqualTo(stockCodeId)
                .andStatusNotEqualTo(DiscountUserStatusEnum.type0.getCode());
        example.setOrderByClause("create_time desc");
        List<BsDiscountUser> list = discountUserMapper.selectByExample(example);
        if (!list.isEmpty()) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public List<DiscountUserModel> getUserDiscountList(Map<String, Object> param) {
        return discountUserMapper.queryUserDiscountList(param);
    }

    @Override
    public void expiration() {
        discountUserMapper.expiration();
    }
}
