package com.seamew.lottery.application.process.impl;

import com.seamew.lottery.application.mq.producer.KafkaProducer;
import com.seamew.lottery.application.process.IActivityProcess;
import com.seamew.lottery.application.process.req.DrawProcessReq;
import com.seamew.lottery.application.process.res.DrawProcessResult;
import com.seamew.lottery.application.process.res.RuleQuantificationCrowdResult;
import com.seamew.lottery.common.Constants;
import com.seamew.lottery.common.Result;
import com.seamew.lottery.domain.activity.model.req.PartakeReq;
import com.seamew.lottery.domain.activity.model.res.PartakeResult;
import com.seamew.lottery.domain.activity.model.vo.ActivityPartakeRecordVO;
import com.seamew.lottery.domain.activity.model.vo.DrawOrderVO;
import com.seamew.lottery.domain.activity.model.vo.InvoiceVO;
import com.seamew.lottery.domain.activity.service.partake.IActivityPartake;
import com.seamew.lottery.domain.rule.model.req.DecisionMatterReq;
import com.seamew.lottery.domain.rule.model.res.EngineResult;
import com.seamew.lottery.domain.rule.service.engine.EngineFilter;
import com.seamew.lottery.domain.strategy.model.req.DrawReq;
import com.seamew.lottery.domain.strategy.model.res.DrawResult;
import com.seamew.lottery.domain.strategy.model.vo.AwardDeductStockVO;
import com.seamew.lottery.domain.strategy.model.vo.DrawAwardVO;
import com.seamew.lottery.domain.strategy.service.draw.IDrawExec;
import com.seamew.lottery.domain.support.ids.IIdGenerator;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFutureCallback;

import javax.annotation.Resource;
import java.util.Map;

/**
 * @Author: seamew
 * @Title: ActivityProcessImpl
 * @CreateTime: 2023年02月24日 16:20:00
 * @Description: 活动抽奖流程编排
 * @Version: 1.0
 */
@Service
public class ActivityProcessImpl implements IActivityProcess {
    @Resource
    private IActivityPartake activityPartake;

    @Resource
    private IDrawExec drawExec;

    @Resource
    private EngineFilter engineFilter;

    @Resource
    private Map<Constants.Ids, IIdGenerator> idGeneratorMap;

    @Resource
    private KafkaProducer kafkaProducer;

    @Override
    public DrawProcessResult doDrawProcess(DrawProcessReq req) {
        // 1. 领取活动
        PartakeResult partakeResult = activityPartake.doPartake(new PartakeReq(req.getUId(), req.getActivityId()));
        if (!Constants.ResponseCode.SUCCESS.getCode().equals(partakeResult.getCode()) && !Constants.ResponseCode.NOT_CONSUMED_TAKE.getCode().equals(partakeResult.getCode())) {
            return new DrawProcessResult(partakeResult.getCode(), partakeResult.getInfo());
        }

        // 2. 首次成功领取活动，发送 MQ 消息
        if (Constants.ResponseCode.SUCCESS.getCode().equals(partakeResult.getCode())) {
            ActivityPartakeRecordVO activityPartakeRecord = new ActivityPartakeRecordVO();
            activityPartakeRecord.setUId(req.getUId());
            activityPartakeRecord.setActivityId(req.getActivityId());
            activityPartakeRecord.setStockCount(partakeResult.getStockCount());
            activityPartakeRecord.setStockSurplusCount(partakeResult.getStockSurplusCount());
            // 发送 MQ 消息
            kafkaProducer.sendLotteryActivityPartakeRecord(activityPartakeRecord);
        }


        Long strategyId = partakeResult.getStrategyId();
        Long takeId = partakeResult.getTakeId();

        // 3. 执行抽奖
        DrawResult drawResult = drawExec.doDrawExec(new DrawReq(req.getUId(), strategyId));
        if (!Constants.DrawState.SUCCESS.getCode().equals(drawResult.getDrawState())) {
            drawExec.recoverAwardCacheStockByRedis(drawResult.getStockTokenKey());
            if (Constants.DrawState.FAIL.getCode().equals(drawResult.getDrawState())) {
                // 未中奖也要将记录锁住
                activityPartake.lockTackActivity(req.getUId(), req.getActivityId(), takeId);
            }
            return new DrawProcessResult(drawResult.getCode(), drawResult.getInfo());
        }
        // 4. 削减奖品库存，发送MQ
        DrawAwardVO drawAwardVO = drawResult.getDrawAwardInfo();
        AwardDeductStockVO awardDeductStockVO = new AwardDeductStockVO();
        awardDeductStockVO.setStrategyId(drawResult.getStrategyId());
        awardDeductStockVO.setAwardSurplusCount(drawAwardVO.getAwardSurplusCount());
        awardDeductStockVO.setAwardId(drawAwardVO.getAwardId());
        kafkaProducer.sendLotteryAwardDeductStock(awardDeductStockVO);

        // 5. 结果落库，
        DrawOrderVO drawOrderVO = buildDrawOrderVO(req, strategyId, takeId, drawAwardVO);
        Result recordResult = activityPartake.recordDrawOrder(drawOrderVO);
        drawExec.recoverAwardCacheStockByRedis(drawResult.getStockTokenKey());
        if (!Constants.ResponseCode.SUCCESS.getCode().equals(recordResult.getCode())) {
            return new DrawProcessResult(recordResult.getCode(), recordResult.getInfo());
        }

        // 6. 发送MQ，触发发奖流程
        InvoiceVO invoiceVO = buildInvoiceVO(drawOrderVO);
        kafkaProducer
                .sendLotteryInvoice(invoiceVO)
                .addCallback(success -> {
                    // 4.1 MQ 消息发送完成，更新数据库表 user_strategy_export.mq_state = 1
                    activityPartake.updateInvoiceMqState(invoiceVO.getUId(), invoiceVO.getOrderId(), Constants.MQState.COMPLETE.getCode());
                }, error -> {
                    // 4.2 MQ 消息发送失败，更新数据库表 user_strategy_export.mq_state = 2 【等待定时任务扫码补偿MQ消息】
                    activityPartake.updateInvoiceMqState(invoiceVO.getUId(), invoiceVO.getOrderId(), Constants.MQState.FAIL.getCode());
                });
        // 7. 返回结果
        return new DrawProcessResult(Constants.ResponseCode.SUCCESS.getCode(), Constants.ResponseCode.SUCCESS.getInfo(), drawAwardVO);
    }

    @Override
    public RuleQuantificationCrowdResult doRuleQuantificationCrowd(DecisionMatterReq req) {
        // 1. 量化决策
        EngineResult engineResult = engineFilter.process(req);

        if (!engineResult.isSuccess()) {
            return new RuleQuantificationCrowdResult(Constants.ResponseCode.RULE_ERR.getCode(), Constants.ResponseCode.RULE_ERR.getInfo());
        }

        // 2. 封装结果
        RuleQuantificationCrowdResult ruleQuantificationCrowdResult = new RuleQuantificationCrowdResult(Constants.ResponseCode.SUCCESS.getCode(), Constants.ResponseCode.SUCCESS.getInfo());
        ruleQuantificationCrowdResult.setActivityId(Long.valueOf(engineResult.getNodeValue()));

        return ruleQuantificationCrowdResult;
    }


    private DrawOrderVO buildDrawOrderVO(DrawProcessReq req, Long strategyId, Long takeId, DrawAwardVO drawAwardVO) {
        long orderId = idGeneratorMap.get(Constants.Ids.SnowFlake).nextId();
        DrawOrderVO drawOrderVO = new DrawOrderVO();
        drawOrderVO.setUId(req.getUId());
        drawOrderVO.setTakeId(takeId);
        drawOrderVO.setActivityId(req.getActivityId());
        drawOrderVO.setOrderId(orderId);
        drawOrderVO.setStrategyId(strategyId);
        drawOrderVO.setStrategyMode(drawAwardVO.getStrategyMode());
        drawOrderVO.setGrantType(drawAwardVO.getGrantType());
        drawOrderVO.setGrantDate(drawAwardVO.getGrantDate());
        drawOrderVO.setGrantState(Constants.GrantState.INIT.getCode());
        drawOrderVO.setAwardId(drawAwardVO.getAwardId());
        drawOrderVO.setAwardType(drawAwardVO.getAwardType());
        drawOrderVO.setAwardName(drawAwardVO.getAwardName());
        drawOrderVO.setAwardContent(drawAwardVO.getAwardContent());
        return drawOrderVO;
    }

    private InvoiceVO buildInvoiceVO(DrawOrderVO drawOrderVO) {
        InvoiceVO invoiceVO = new InvoiceVO();
        invoiceVO.setUId(drawOrderVO.getUId());
        invoiceVO.setOrderId(drawOrderVO.getOrderId());
        invoiceVO.setAwardId(drawOrderVO.getAwardId());
        invoiceVO.setAwardType(drawOrderVO.getAwardType());
        invoiceVO.setAwardName(drawOrderVO.getAwardName());
        invoiceVO.setAwardContent(drawOrderVO.getAwardContent());
        invoiceVO.setShippingAddress(null);
        invoiceVO.setExtInfo(null);
        return invoiceVO;
    }
}
