package com.fdkankan.ucenter.service.impl;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fdkankan.common.constant.CommonStatus;
import com.fdkankan.common.constant.ErrorCode;
import com.fdkankan.common.constant.SceneKind;
import com.fdkankan.rabbitmq.util.RabbitMqProducer;
import com.fdkankan.redis.constant.RedisKey;
import com.fdkankan.redis.constant.RedisLockKey;
import com.fdkankan.redis.util.RedisLockUtil;
import com.fdkankan.redis.util.RedisUtil;
import com.fdkankan.ucenter.bean.SceneUpgradeProgressBean;
import com.fdkankan.ucenter.common.Result;
import com.fdkankan.ucenter.entity.ScenePlus;
import com.fdkankan.ucenter.entity.ScenePlusExt;
import com.fdkankan.ucenter.entity.ScenePro;
import com.fdkankan.ucenter.entity.SceneRepairLog;
import com.fdkankan.ucenter.mapper.ISceneUpgradeMapper;
import com.fdkankan.ucenter.service.IScenePlusExtService;
import com.fdkankan.ucenter.service.IScenePlusService;
import com.fdkankan.ucenter.service.ISceneProService;
import com.fdkankan.ucenter.service.ISceneRepairLogService;
import com.fdkankan.ucenter.service.ISceneUpgradeToV4Service;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * <p>
 * TODO
 * </p>
 *
 * @author dengsixing
 * @since 2022/4/21
 **/
@Slf4j
@Service
public class SceneUpgradeToV4Service implements ISceneUpgradeToV4Service {

    @Value("${queue.scene.upgrade-to-v4}")
    private String upgradeToV4;

    @Autowired
    private ISceneRepairLogService sceneRepairLogService;
    @Autowired
    private ISceneProService sceneProService;
    @Autowired
    private ISceneUpgradeMapper sceneUpgradeMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedisLockUtil redisLockUtil;
    @Autowired
    private RabbitMqProducer rabbitMqProducer;
    @Autowired
    private IScenePlusService scenePlusService;
    @Autowired
    private IScenePlusExtService scenePlusExtService;

    @Override
    public Result upgrade(String num)  throws Exception{

        boolean reUpgrade = false;
        ScenePro scenePro = sceneProService.getOne(new LambdaQueryWrapper<ScenePro>().eq(ScenePro::getNum, num));
        if(scenePro.getIsUpgrade() == CommonStatus.YES.code().intValue()){
            reUpgrade = true;
        }

        //加锁
        String lockKey = String.format(RedisLockKey.LOCK_SCENE_UPGRADE_V4, num);
        boolean lock = redisLockUtil.lock(lockKey,RedisKey.EXPIRE_TIME_2_HOUR);
        if(!lock){
            return Result.failure(ErrorCode.FAILURE_CODE_7019.code(), ErrorCode.FAILURE_CODE_7019.message());
        }

        SceneRepairLog sceneRepairLog = null;
        try {
            //查询升级日志，如果已经升级成功，不允许重复升级
            sceneRepairLog = sceneRepairLogService.getOne(
                new LambdaQueryWrapper<SceneRepairLog>()
                    .eq(SceneRepairLog::getNum, num)
                    .orderByDesc(SceneRepairLog::getId)
                    .last("limit 1"));
            if(Objects.nonNull(sceneRepairLog)){
                if(sceneRepairLog.getState() == 0){
                    return Result.failure(ErrorCode.FAILURE_CODE_7019.code(), ErrorCode.FAILURE_CODE_7019.message());
                }
            }
            scenePro.setIsUpgrade(2);       //升级中
            sceneProService.updateById(scenePro);

            //写入升级日志
            this.upgradeLog(null, num, 0, null);

            //同步到scenePlus、scenePlus
            ScenePlus scenePlus = scenePlusService.getByNum(num);
            if(Objects.nonNull(scenePlus)){
                sceneUpgradeMapper.deleteScenePlus(num);
                sceneUpgradeMapper.deleteScenePlusExt(scenePlus.getId());
            }
            sceneUpgradeMapper.transferScenePlus(num);
            scenePlus = scenePlusService.getByNum(num);
            String sceneKind = scenePro.getSceneScheme() == 3 ? SceneKind.FACE.code():SceneKind.TILES.code();
            sceneUpgradeMapper.transferScenePlusExt(num, scenePlus.getId(), sceneKind);

            //写入进度条
            Map<String, Integer> progress = new HashMap<>();
            progress.put("status", 0);
            progress.put("progress", 0);
            redisUtil.set(RedisKey.scene_upgrade_progress_num, JSON.toJSONString(progress));

            //发送mq
            HashMap<String, Object> msg = new HashMap<>();
            msg.put("sceneProId", scenePro.getId());
            msg.put("reUpgrade", reUpgrade);
            rabbitMqProducer.sendByWorkQueue(upgradeToV4, msg);

        }catch (Exception e){
            log.error("场景升级失败", e);
            redisLockUtil.unlockLua(lockKey);
            this.upgradeLog(sceneRepairLog, num, 2, ExceptionUtil.stacktraceToString(e, 3000));
            return Result.failure(ErrorCode.FAILURE_CODE_7021.code(), ErrorCode.FAILURE_CODE_7021.message());
        }

        return Result.success();

    }

    public Result getUpgradeToV4Progress(String num){
        String key = String.format(RedisKey.scene_upgrade_progress_num, num);
        String progress = redisUtil.get(key);
        SceneUpgradeProgressBean progressBean = null;
        if(StrUtil.isNotEmpty(progress)){
            progressBean = JSON.parseObject(progress, SceneUpgradeProgressBean.class);
        }else{
            progressBean = SceneUpgradeProgressBean.builder().num(num).status(0).progress(0).build();
        }
        return Result.success(progressBean);
    }

    private void upgradeLog(SceneRepairLog sceneRepairLog, String num, int status, String reason){
        //写入升级日志
        if(Objects.isNull(sceneRepairLog)){
            sceneRepairLog = new SceneRepairLog();
            sceneRepairLog.setNum(num);
        }
        sceneRepairLog.setState(status);
        sceneRepairLog.setReason(reason);
        sceneRepairLog.setCreateTime(Calendar.getInstance().getTime());
        sceneRepairLogService.saveOrUpdate(sceneRepairLog);
    }
}
