package com.slodon.b2b2c.controller.promotion.front;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.slodon.b2b2c.core.constant.DrawConst;
import com.slodon.b2b2c.core.constant.RedisConst;
import com.slodon.b2b2c.core.controller.BaseController;
import com.slodon.b2b2c.core.response.JsonResult;
import com.slodon.b2b2c.core.response.PageVO;
import com.slodon.b2b2c.core.response.PagerInfo;
import com.slodon.b2b2c.core.response.SldResponse;
import com.slodon.b2b2c.core.uid.GoodsIdGenerator;
import com.slodon.b2b2c.core.util.AssertUtil;
import com.slodon.b2b2c.core.util.StringUtil;
import com.slodon.b2b2c.core.util.UserUtil;
import com.slodon.b2b2c.core.util.WebUtil;
import com.slodon.b2b2c.member.example.MemberExample;
import com.slodon.b2b2c.member.pojo.Member;
import com.slodon.b2b2c.model.member.MemberModel;
import com.slodon.b2b2c.model.promotion.DrawModel;
import com.slodon.b2b2c.model.promotion.DrawPrizeModel;
import com.slodon.b2b2c.model.promotion.DrawRecordModel;
import com.slodon.b2b2c.promotion.example.DrawPrizeExample;
import com.slodon.b2b2c.promotion.example.DrawRecordExample;
import com.slodon.b2b2c.promotion.pojo.Draw;
import com.slodon.b2b2c.promotion.pojo.DrawPrize;
import com.slodon.b2b2c.promotion.pojo.DrawRecord;
import com.slodon.b2b2c.starter.redisson.SlodonLock;
import com.slodon.b2b2c.starter.redisson.constant.LockKeyConst;
import com.slodon.b2b2c.vo.promotion.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Api(tags = "front-抽奖")
@RestController
@Slf4j
@RequestMapping("v3/promotion/front/draw")
public class FrontDrawController extends BaseController {

    @Resource
    private DrawModel drawModel;
    @Resource
    private DrawPrizeModel drawPrizeModel;
    @Resource
    private DrawRecordModel drawRecordModel;
    @Resource
    private MemberModel memberModel;
    @Resource
    private SlodonLock slodonLock;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @ApiOperation("中奖列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "drawId", value = "抽奖活动id", paramType = "query", required = true),
            @ApiImplicitParam(name = "isMyPrize", value = "是否为我的中奖纪录,true 是,false 否", paramType = "query", required = true),
            @ApiImplicitParam(name = "pageSize", value = "分页大小", defaultValue = "5", paramType = "query"),
            @ApiImplicitParam(name = "current", value = "当前页面位置", defaultValue = "1", paramType = "query")
    })
    @GetMapping("drawRecordList")
    public JsonResult<PageVO<DrawRecordVO>> drawRecordList(HttpServletRequest request, Integer drawId, Boolean isMyPrize) {
        AssertUtil.notNullOrZero(drawId, "抽奖活动id不能为空");
        Member member = UserUtil.getUser(request, Member.class);

        PagerInfo pager = WebUtil.handlerPagerInfo(request);
        DrawRecordExample example = new DrawRecordExample();
        example.setDrawId(drawId);
        example.setIsPrize(DrawConst.IS_PRIZE_1);
        if (isMyPrize) {
            //我的中奖纪录
            example.setMemberId(member.getMemberId());
        }
        List<DrawRecordVO> vos = new ArrayList<>();
        List<DrawRecord> list = drawRecordModel.getDrawRecordList(example, pager);
        if (!CollectionUtils.isEmpty(list)) {
            list.forEach(drawRecord -> {
                vos.add(new DrawRecordVO(drawRecord));
            });
        }
        return SldResponse.success(new PageVO<>(vos, pager));
    }

    @ApiOperation("抽奖详情")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "drawId", value = "抽奖活动id", paramType = "query", required = true)
    })
    @GetMapping("detail")
    public JsonResult<DrawDetailVO> detail(HttpServletRequest request, Integer drawId) {
        AssertUtil.notNullOrZero(drawId, "抽奖活动id不能为空");
        Draw draw = drawModel.getDrawByDrawId(drawId);
        AssertUtil.notNull(draw, "查询的抽奖信息为空");

        DrawDetailVO vo = new DrawDetailVO(draw);
        DrawPrizeExample example = new DrawPrizeExample();
        example.setDrawId(drawId);
        List<DrawPrize> list = drawPrizeModel.getDrawPrizeList(example, null);
        List<DrawPrizeVO> drawPrizeVOList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(list)) {
            list.forEach(drawPrize -> {
                drawPrizeVOList.add(new DrawPrizeVO(drawPrize));
            });
            vo.setDrawPrizeVOList(drawPrizeVOList);
        }
        return SldResponse.success(vo);
    }

    @ApiOperation("我的积分")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "drawId", value = "抽奖活动id", paramType = "query", required = true)
    })
    @GetMapping("integral")
    public JsonResult getIntegral(HttpServletRequest request, Integer drawId) {
        AssertUtil.notNullOrZero(drawId, "抽奖活动id不能为空");
        Member member = UserUtil.getUser(request, Member.class);

        Map<String, Object> map = new HashMap<>();
        if (!StringUtil.isNullOrZero(member.getMemberId())) {
            Draw draw = drawModel.getDrawByDrawId(drawId);
            AssertUtil.notNull(draw, "查询的抽奖信息为空");
            //会员积分
            Member memberDb = memberModel.getMemberByMemberId(member.getMemberId());
            AssertUtil.notNull(memberDb, "查询的会员信息为空");
            map.put("integral", memberDb.getMemberIntegral());
            //剩余抽奖次数
            DrawRecordExample example = new DrawRecordExample();
            example.setDrawId(drawId);
            example.setMemberId(member.getMemberId());
            if (draw.getRuleType() == DrawConst.RULE_TYPE_1) {
                example.setRecordTimeAfter(DateUtil.beginOfDay(new Date()));
            }
            Integer recordCount = drawRecordModel.getDrawRecordCount(example);
            map.put("remainNum", Math.max(draw.getRuleNum() - recordCount, 0));
        }
        return SldResponse.success(map);
    }

    @ApiOperation("奖品详情")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "recordId", value = "抽奖记录id", paramType = "query", required = true)
    })
    @GetMapping("prizeDetail")
    public JsonResult<DrawRecordDetailVO> prizeDetail(HttpServletRequest request, Integer recordId) {
        AssertUtil.notNullOrZero(recordId, "抽奖记录id不能为空");
        DrawRecord drawRecord = drawRecordModel.getDrawRecordByRecordId(recordId);
        AssertUtil.notNull(drawRecord, "查询的抽奖记录信息为空");
        DrawRecordDetailVO vo = new DrawRecordDetailVO(drawRecord);
        DrawPrize drawPrize = drawPrizeModel.getDrawPrizeByPrizeId(drawRecord.getPrizeId());
        AssertUtil.notNull(drawPrize, "查询的抽奖奖品信息为空");
        vo.setCouponId(drawPrize.getCouponId());
        return SldResponse.success(vo);
    }

    @ApiOperation("去抽奖")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "drawId", value = "抽奖活动id", paramType = "query", required = true)
    })
    @PostMapping("doDraw")
    public JsonResult doDraw(HttpServletRequest request, Integer drawId) {
        AssertUtil.notNullOrZero(drawId, "抽奖活动id不能为空");
        Member member = UserUtil.getUser(request, Member.class);

        //查询活动信息
        Draw draw = drawModel.getDrawByDrawId(drawId);
        AssertUtil.notNull(draw, "查询的抽奖活动信息为空");
        AssertUtil.isTrue(draw.getStartTime().after(new Date()), "抽奖活动未开始");
        AssertUtil.isTrue(draw.getEndTime().before(new Date()), "抽奖活动已结束");

        //查询会员信息
        Member memberDb = memberModel.getMemberByMemberId(member.getMemberId());
        AssertUtil.isTrue(memberDb.getMemberIntegral() < draw.getIntegralUse(), "积分不足");

        //查询会员抽奖记录
        DrawRecordExample example = new DrawRecordExample();
        example.setDrawId(drawId);
        example.setMemberId(member.getMemberId());
        if (draw.getRuleType() == DrawConst.RULE_TYPE_1) {
            example.setRecordTimeAfter(DateUtil.beginOfDay(new Date()));
        }
        Integer recordCount = drawRecordModel.getDrawRecordCount(example);
        AssertUtil.isTrue(recordCount >= draw.getRuleNum(), "您的抽奖机会已用完");


        //对用户加锁，防止同一用户在不同客户端重复抽奖
        String memberLockKey = LockKeyConst.MEMBER_DRAW_PREFIX + member.getMemberId();
        slodonLock.lock(memberLockKey);
        MemberDrawInfoVO vo;
        try {
            vo = drawModel.doDraw(draw, memberDb);
        } finally {
            slodonLock.unlock(memberLockKey);
        }
        return SldResponse.success("抽奖成功", vo);
    }

    @ApiOperation("中奖名单列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "drawId", value = "抽奖活动id", required = true)
    })
    @GetMapping("winList")
    public JsonResult<List<DrawWinVO>> winList(HttpServletRequest request, Integer drawId) {
        AssertUtil.notNullOrZero(drawId, "抽奖活动id不能为空");

        //查询活动信息
        Draw draw = drawModel.getDrawByDrawId(drawId);
        AssertUtil.notNull(draw, "查询的抽奖活动信息为空");
        List<DrawWinVO> vos = new ArrayList<>();
        if (draw.getStartTime().before(new Date())) {
            Set<ZSetOperations.TypedTuple<String>> drawSet = stringRedisTemplate.opsForZSet().rangeWithScores(RedisConst.DRAW_PREFIX + drawId, -20, -1);
            if (CollectionUtils.isEmpty(drawSet)) {
                //是否开启虚拟中奖
                if (draw.getOpenVirtual() == DrawConst.OPEN_VIRTUAL_1) {
                    //查询20条会员数据.根据余额排序
                    PagerInfo pager = new PagerInfo(20, 1);
                    MemberExample memberExample = new MemberExample();
                    memberExample.setOrderBy("balance_available DESC");
                    List<Member> memberList = memberModel.getMemberList(memberExample, pager);
                    //查询奖品信息
                    DrawPrizeExample example = new DrawPrizeExample();
                    example.setDrawId(drawId);
                    List<DrawPrize> list = drawPrizeModel.getDrawPrizeList(example, null);
                    //key-奖品id，value-奖品对象
                    Map<Integer, DrawPrize> map = new HashMap<>();
                    list.forEach(drawPrize -> {
                        map.put(drawPrize.getPrizeId(), drawPrize);
                    });
                    //生成20次虚拟数据
                    for (int i = 0; i < 20; i++) {
                        //获取会员信息
                        Member member = memberList.get(i);
                        //返回奖品id
                        Integer prizeId = drawModel.getRandomPrize(map);
                        DrawPrize drawPrize = map.get(prizeId);
                        DrawWinVO vo = new DrawWinVO();
                        vo.setWinId(UUID.randomUUID().toString());
                        vo.setPrizeId(prizeId);
                        vo.setPrizeName(drawPrize.getPrizeName());
                        if (member != null) {
                            vo.setMemberId(member.getMemberId());
                            String memberName = member.getMemberName();
                            vo.setMemberName(memberName);
                            vo.setMemberNameHide(DrawRecordVO.dealMemberNameHide(memberName));
                        } else {
                            vo.setMemberId(0);
                            String memberName = GoodsIdGenerator.getMemberName();
                            vo.setMemberName(memberName);
                            vo.setMemberNameHide(DrawRecordVO.dealMemberNameHide(memberName));
                        }
                        vo.setPrizeType(drawPrize.getPrizeType());
                        vo.setIsVirtualData(DrawConst.IS_VIRTUAL_DATA_0);
                        vos.add(vo);
                        //存入redis
                        stringRedisTemplate.opsForZSet().add(RedisConst.DRAW_PREFIX + drawId, JSON.toJSONString(vo), System.currentTimeMillis());
                        //设置过期时间
                        long remainedTime = draw.getEndTime().getTime() - System.currentTimeMillis();
                        stringRedisTemplate.expire(RedisConst.DRAW_PREFIX + drawId, remainedTime, TimeUnit.MILLISECONDS);
                    }
                }
            } else {
                for (ZSetOperations.TypedTuple<String> json : drawSet) {
                    DrawWinVO vo = JSONObject.parseObject(json.getValue(), DrawWinVO.class);
                    //如果关闭虚拟中奖,过滤虚拟数据
                    if (vo != null && draw.getOpenVirtual() == DrawConst.OPEN_VIRTUAL_0) {
                        if (vo.getIsVirtualData() == DrawConst.IS_VIRTUAL_DATA_0) {
                            continue;
                        }
                    }
                    vos.add(0, vo);
                }
            }
        }
        return SldResponse.success(vos);
    }

}
