package com.liquidnet.service.goblin.controller;

import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.DESUtils;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.GoblinUserNftAccInfoVo;
import com.liquidnet.service.goblin.dto.GoblinUserSafeConfigDto;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.service.IGoblinUserDigitalArtworkService;
import com.liquidnet.service.goblin.util.GoblinRedisUtils;
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.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@ApiSupport(order = 142000)
@Api(tags = "我的藏品管理")
@Slf4j
@Validated
@RestController
@RequestMapping("artwork")
public class GoblinUserDigitalArtworkController {
    @Value("${liquidnet.secret.passwd-salt}")
    private String passwdSalt;
    @Autowired
    GoblinRedisUtils goblinRedisUtils;
    @Autowired
    IGoblinUserDigitalArtworkService goblinUserDigitalArtworkService;

    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "藏品列表")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "pageNum", value = "索引页数"),
            @ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "pageSize", value = "显示记录数[默认5，暂定最大也为5]"),
    })
    @PostMapping("list")
    public ResponseDto<GoblinUserDigitalArtworkPageVo> list(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
                                                            @RequestParam(required = false, defaultValue = "5") Integer pageSize) {
        pageNum = pageNum < 1 ? 1 : pageNum;
        pageSize = pageSize < 1 ? 5 : (pageSize > 5 ? 5 : pageSize);
        String currentUid = CurrentUtil.getCurrentUid();
        return ResponseDto.success(goblinUserDigitalArtworkService.page(currentUid, pageNum, pageSize));
    }

    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "藏品详情")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "artworkId", value = "藏品ID"),
    })
    @PostMapping("info")
    public ResponseDto<GoblinUserDigitalArtworkInfoVo> info(@NotBlank(message = "藏品ID不能为空") @RequestParam String artworkId) {
        return ResponseDto.success(goblinUserDigitalArtworkService.info(CurrentUtil.getCurrentUid(), artworkId));
    }

    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "开启盲盒")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "artworkId", value = "藏品ID"),
    })
    @PostMapping("unboxing")
    public ResponseDto<GoblinUserDigitalArtworkListVo> unboxing(@NotBlank(message = "藏品ID不能为空")
                                                            @RequestParam String artworkId) {
        String currentUid = CurrentUtil.getCurrentUid();
        GoblinUserDigitalArtworkVo userDigitalArtworkVo = goblinRedisUtils.getUserDigitalArtworkVo(artworkId);
        if (null == userDigitalArtworkVo || !currentUid.equals(userDigitalArtworkVo.getUid()) || userDigitalArtworkVo.getDelFlg().equals("1")) {
            return ResponseDto.failure(ErrorMapping.get("140100"));
        }
        GoblinGoodsSkuInfoVo goodsSkuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(userDigitalArtworkVo.getSkuId());
        if (null == goodsSkuInfoVo || !"1".equals(goodsSkuInfoVo.getUnbox())) {
            return ResponseDto.failure(ErrorMapping.get("140100"));
        }
        if (LocalDateTime.now().isBefore(goodsSkuInfoVo.getOpeningTime())) {
            return ResponseDto.failure(ErrorMapping.get("140101"));
        }
        if (1 == userDigitalArtworkVo.getState()) {// 盲盒已开启
            GoblinNftOrderVo goblinNftOrder = goblinRedisUtils.getGoblinNftOrder(userDigitalArtworkVo.getOrderId());
            GoblinGoodsSkuInfoVo unboxSkuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(goblinNftOrder.getBoxSkuId());
            GoblinUserDigitalArtworkListVo unboxDigitalArtworkListVo = GoblinUserDigitalArtworkListVo.getNew();
            unboxDigitalArtworkListVo.setArtworkId(userDigitalArtworkVo.getHitArtworkId());
            unboxDigitalArtworkListVo.setName(unboxSkuInfoVo.getName());
            unboxDigitalArtworkListVo.setSubtitle(unboxSkuInfoVo.getSubtitle());
            unboxDigitalArtworkListVo.setCoverPic(unboxSkuInfoVo.getSkuPic());
            return ResponseDto.success(unboxDigitalArtworkListVo);
        } else if (goblinRedisUtils.markUnboxUserDigitalArtwork(artworkId) > 1) {
            return ResponseDto.failure(ErrorMapping.get("140000"));
        }
        goblinRedisUtils.expire(GoblinRedisConst.USER_DIGITAL_ARTWORK_UNBOX.concat(artworkId), 30);
        GoblinUserDigitalArtworkListVo unboxingRstVo = null;
        try {
            switch (userDigitalArtworkVo.getSource()) {
                case 1:
                case 2:
                case 31:
                    unboxingRstVo = goblinUserDigitalArtworkService.unboxingForBuyOrExchange(currentUid, userDigitalArtworkVo);
                    break;
                case 3:
                default:
                    log.warn("Invalid operation[UID={},artworkId={}]", currentUid, artworkId);
            }
        } catch (Exception e) {
            log.error("Ex.开启盲盒异常[artworkId={},uid={}]", artworkId, currentUid);
        }
        goblinRedisUtils.del(GoblinRedisConst.USER_DIGITAL_ARTWORK_UNBOX.concat(artworkId));
        return null == unboxingRstVo ? ResponseDto.failure(ErrorMapping.get("140102")) : ResponseDto.success(unboxingRstVo);
    }

    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "收取藏品", notes = "空投藏品，收下后执行上链操作。<br />响应【data】值描述：藏品状态 0-生成中|1-已生成|2-生成失败")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "artworkId", value = "藏品ID"),
    })
    @PostMapping("accept")
    public ResponseDto<Integer> accept(@NotBlank(message = "藏品ID不能为空")
                                                              @RequestParam String artworkId) {
        String currentUid = CurrentUtil.getCurrentUid();
        GoblinUserDigitalArtworkVo userDigitalArtworkVo = goblinRedisUtils.getUserDigitalArtworkVo(artworkId);
        if (null == userDigitalArtworkVo || !currentUid.equals(userDigitalArtworkVo.getUid())
                || userDigitalArtworkVo.getDelFlg().equals("1") || userDigitalArtworkVo.getSource() != 31) {
            return ResponseDto.failure(ErrorMapping.get("140105"));
        }
        Integer acceptState = userDigitalArtworkVo.getState();
        if (5 != acceptState) {// 非待收取状态，直接返回藏品状态
            return ResponseDto.success(acceptState);
        } else if (goblinRedisUtils.markUnboxUserDigitalArtwork(artworkId) > 1) {
            return ResponseDto.failure(ErrorMapping.get("140000"));
        }
        goblinRedisUtils.expire(GoblinRedisConst.USER_DIGITAL_ARTWORK_UNBOX.concat(artworkId), 30);
        boolean acceptRstFlg = goblinUserDigitalArtworkService.accept(currentUid, userDigitalArtworkVo);
        goblinRedisUtils.del(GoblinRedisConst.USER_DIGITAL_ARTWORK_UNBOX.concat(artworkId));
        return acceptRstFlg ? ResponseDto.success(0) : ResponseDto.failure(ErrorMapping.get("140002"));
    }

    @ApiOperationSupport(order = 5)
    @ApiOperation(value = "藏品转赠", notes = "<br />响应【data】值描述：转赠状态[PENDING|SUCCESS]")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "safePasswd", value = "安全密码:MD5(******)"),
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "artworkId", value = "藏品ID"),
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "receiver", value = "受赠人"),
    })
    @PostMapping("transfer")
    public ResponseDto<String> transfer(@NotBlank(message = "安全密码不能为空") @RequestParam String safePasswd,
                                        @NotBlank(message = "藏品ID不能为空") @RequestParam String artworkId,
                                        @NotBlank(message = "受赠人不能为空") @RequestParam String receiver) {
        String currentUid = CurrentUtil.getCurrentUid();
        GoblinUserSafeConfigDto userSafeConfigDto = goblinRedisUtils.getUserSafeConfigDto(currentUid);
        if (null == userSafeConfigDto) return ResponseDto.failure(ErrorMapping.get("140021"));// 未设置安全密码
        if (!userSafeConfigDto.validPasswd(safePasswd.concat(passwdSalt))) return ResponseDto.failure(ErrorMapping.get("140022"));// 安全密码不正确

        GoblinUserDigitalArtworkVo userDigitalArtworkVo = goblinRedisUtils.getUserDigitalArtworkVo(artworkId);
        if (null == userDigitalArtworkVo || !currentUid.equals(userDigitalArtworkVo.getUid()) || userDigitalArtworkVo.getDelFlg().equals("1")) {
            return ResponseDto.failure(ErrorMapping.get("140105"));
        }
        GoblinGoodsSkuInfoVo goodsSkuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(userDigitalArtworkVo.getSkuId());
        if ("1".equals(goodsSkuInfoVo.getUnbox())) return ResponseDto.failure(ErrorMapping.get("140106"));// 盲盒
        if (1 != userDigitalArtworkVo.getState()) return ResponseDto.failure(ErrorMapping.get("140107"));// 未上链
        Map<String, Long> artworkTransferConfMap = goblinRedisUtils.getUserDigitalArtworkTransferConfMap();
        LocalDateTime transferAllowTime = DateUtil.Formatter.yyyyMMddHHmmss.parse(userDigitalArtworkVo.getTradingAt()).plusSeconds(artworkTransferConfMap.get(goodsSkuInfoVo.getRouteType()));
        if (!artworkTransferConfMap.containsKey(goodsSkuInfoVo.getRouteType())) return ResponseDto.failure(ErrorMapping.get("140111"));// 区块链路由未开放转赠
        if (transferAllowTime.isAfter(LocalDateTime.now())) return ResponseDto.failure(ErrorMapping.get("140112"));// 转赠过渡期内不可转赠
        String transferState = userDigitalArtworkVo.getTransferState();
        if (StringUtils.isNotEmpty(transferState)) return ResponseDto.success(transferState);// 转赠状态不为空，标记已转赠或转赠中
        String[] receiverArr;
        try {
            receiverArr = DESUtils.DES().decrypt(receiver).split(",");
        } catch (Exception e) {
            log.error("Ex.藏品转赠受赠人解析异常[artworkId={},receiver={}]", artworkId, receiver);
            return ResponseDto.failure(ErrorMapping.get("140108"));// 受赠人信息有误
        }
        if (CurrentUtil.getCurrentUid().equals(receiverArr[0])) return ResponseDto.failure(ErrorMapping.get("140109"));// 受赠人不能是本人
        GoblinUserNftAccInfoVo receiverAcctInfo = goblinRedisUtils.getOpenAccountInfo(receiverArr[0]);
        if (null == receiverAcctInfo || !"2000".equals(receiverAcctInfo.getCode())) return ResponseDto.failure(ErrorMapping.get("140110"));// 未开通数字账户

        String lockKey = GoblinRedisConst.USER_DIGITAL_ARTWORK.concat("lk").concat(artworkId);
        if (goblinRedisUtils.incr(lockKey, 1) > 1) return ResponseDto.failure(ErrorMapping.get("140000"));
        goblinRedisUtils.expire(lockKey, 30);
        boolean transferRstFlg = goblinUserDigitalArtworkService.transfer(userDigitalArtworkVo, goodsSkuInfoVo.getRouteType(), receiverArr);
        goblinRedisUtils.del(lockKey);
        return transferRstFlg ? ResponseDto.success(GoblinStatusConst.TransferState.PENDING.name()) : ResponseDto.failure(ErrorMapping.get("140002"));
    }
}
