package com.fzm.mall.server.admin.goods_center.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fzm.mall.server.admin.block_chain.util.ChainUtil;
import com.fzm.mall.server.admin.constant.MALLGlobalConfig;
import com.fzm.mall.server.admin.constant.MallResponseError;
import com.fzm.mall.server.admin.exception.MyException;
import com.fzm.mall.server.admin.finance_center.enums.type.AssetsFlowTypeEnum;
import com.fzm.mall.server.admin.goods_center.entity.dto.GoodsSkuDTO;
import com.fzm.mall.server.admin.goods_center.entity.vo.GoodsPackageSkuVO;
import com.fzm.mall.server.admin.goods_center.entity.vo.GoodsSkuVO;
import com.fzm.mall.server.admin.goods_center.entity.vo.SkuPropertyVO;
import com.fzm.mall.server.admin.goods_center.enums.type.CommodityPassTypeEnum;
import com.fzm.mall.server.admin.goods_center.enums.type.NFTSalesTypeEnum;
import com.fzm.mall.server.admin.goods_center.enums.type.SalesTypeEnum;
import com.fzm.mall.server.admin.goods_center.model.ExternalToken;
import com.fzm.mall.server.admin.goods_center.service.IExternalTokenService;
import com.fzm.mall.server.admin.mq.enums.QueueTTLTypeEnum;
import com.fzm.mall.server.admin.goods_center.enums.status.SkuStatusEnum;
import com.fzm.mall.server.admin.mq.MsgProducer;
import com.fzm.mall.server.admin.goods_center.model.Sku;
import com.fzm.mall.server.admin.goods_center.mapper.SkuMapper;
import com.fzm.mall.server.admin.goods_center.service.ISkuPropService;
import com.fzm.mall.server.admin.goods_center.service.ISkuService;
import com.fzm.mall.server.admin.home_page.model.Merchant;
import com.fzm.mall.server.admin.home_page.service.IMerchantService;
import com.fzm.mall.server.admin.order.model.OrderDetail;
import com.fzm.mall.server.admin.util.UUIDUtil;
import io.swagger.models.auth.In;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.dozer.DozerBeanMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangp
 * @date 2021/3/4 16:06
 * @description 商品规格表 服务实现类
 * @since JDK 1.8
 */
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements ISkuService {

    @Autowired
    private IMerchantService merchantService;
    @Autowired
    private ISkuPropService skuPropService;
    @Autowired
    private MsgProducer producer;
    @Autowired
    private DozerBeanMapper beanMapper;
    @Autowired
    private IExternalTokenService externalTokenService;
    @Autowired
    private ChainUtil chainUtil;

    @Override
    public void add(List<GoodsSkuDTO> kDTOs, String goodsId, String goodsName, String merchantId, String addr, Integer commodityPass, String thumb, Integer pDTOType, Integer salesType) {
        List<Sku> skus = new ArrayList<>();
        int total = kDTOs.size();
        int subCount = 0;

        for (GoodsSkuDTO kDTO : kDTOs) {
            //规格入库
            Sku sku = beanMapper.map(kDTO, Sku.class);
            String skuId = UUIDUtil.getUUID();
            sku.setThumb(kDTO.getImage());
            if (StringUtils.isBlank(kDTO.getImage())) {
                sku.setThumb(thumb);
            }
            sku.setSkuId(skuId);
            sku.setGoodsId(goodsId);

            //规格属性入库
            skuPropService.add(kDTO.getPropertyList(), skuId);

            if (salesType.equals(SalesTypeEnum.BLIND_BOX.getType())) {
                if (commodityPass.equals(CommodityPassTypeEnum.INNER.getType())) {
                    long tokenId = System.currentTimeMillis();
                    sku.setCoinName(MALLGlobalConfig.NFT_PREFIX + tokenId);
                    sku.setTokenId(tokenId);
                    //发行NFT
                    log.info("【goods release】：" + addr + "," + tokenId + "," + kDTO.getStock());
                    String hash = chainUtil.evmPublishTokenSplit(addr, tokenId, kDTO.getStock());
                    log.info("【goods release hash】：" + hash);

                    //异步确认
                    Map<String, Object> map = new HashMap<>();
                    map.put("hash", hash);
                    map.put("stock", kDTO.getStock());
                    map.put("goodsId", goodsId);
                    map.put("skuId", skuId);
                    map.put("subCount", ++subCount);
                    map.put("total", total);
                    producer.sendTTL(map, QueueTTLTypeEnum.ISSUE_COINS_TTL, 5 * 1000);
                    //外部绑定
                    // ||||
                } else if (commodityPass.equals(CommodityPassTypeEnum.OUT.getType()) || commodityPass.equals(CommodityPassTypeEnum.COPY.getType())) {
                    ExternalToken externalToken = externalTokenService.queryByComId(kDTO.getCommodityPassId());
                    externalToken.setBindNumber(new BigDecimal(kDTO.getStock()));
                    externalToken.setGoodsId(goodsId);
                    externalToken.setGoodsName(goodsName);
                    externalToken.setStatus(1);
                    externalTokenService.updateById(externalToken);
                    sku.setCoinName(externalToken.getLabelName());
                    sku.setTokenId(externalToken.getTokenId());
                }
            }

            if (commodityPass.equals(CommodityPassTypeEnum.COPY.getType())) {
                LambdaUpdateWrapper<ExternalToken> uw = new LambdaUpdateWrapper<>();
                uw.set(ExternalToken::getBindNumber, sku.getStock());
                uw.set(ExternalToken::getGoodsId, goodsId);
                uw.set(ExternalToken::getGoodsName, goodsName);
                uw.set(ExternalToken::getStatus, 1);
                uw.eq(ExternalToken::getCommodityPassId, sku.getCommodityPassId());
                externalTokenService.update(uw);

                if (kDTO.getNftSalesType().equals(2)) {
                    //发行NFT
                    ExternalToken externalToken = externalTokenService.queryByLabelNameAndMerchantId(kDTO.getCoinName(), merchantId);

                    String tokenId = externalToken.getTokenId().toString();
                    long[] nos = new long[Math.toIntExact(kDTO.getStock())];
                    String[] hashs = new String[Math.toIntExact(kDTO.getStock())];
                    for (int i = 0; i < kDTO.getStock(); i++) {
                        String suffix = i + 1 + "";
                        Long suffixAll = Long.parseLong(tokenId + "00000000".substring(0, 8 - suffix.length()) + suffix);
                        String hash = chainUtil.evmPublishTokenSplit(addr, suffixAll, 1L);
                        nos[i] = suffixAll;
                        hashs[i] = hash;
                    }

                    Map<String, Object> map = new HashMap<>();
                    map.put("goodsId", goodsId);
                    map.put("hashs", hashs);
                    map.put("prefix", tokenId);
                    map.put("merchantId", merchantId);
                    map.put("commodityPassId", externalToken.getCommodityPassId());
                    map.put("nos", JSONArray.toJSON(nos));
                    producer.sendTTL(map, QueueTTLTypeEnum.NFT_USU_TTL, 10 * 1000);
                }
            }
            skus.add(sku);
        }

        saveBatch(skus);
    }

    @Override
    public void draft(List<GoodsSkuDTO> kDTOs, String goodsId) {
        List<Sku> skus = new ArrayList<>();

        for (GoodsSkuDTO kDTO : kDTOs) {
            Sku sku = beanMapper.map(kDTO, Sku.class);
            String skuId = UUIDUtil.getUUID();

            //规格属性入库
            if (kDTO.getPropertyList() != null) {
                skuPropService.add(kDTO.getPropertyList(), skuId);
            }

            //规格入库
            sku.setThumb(kDTO.getImage());
            sku.setSkuId(skuId);
            sku.setGoodsId(goodsId);
            skus.add(sku);
        }

        saveBatch(skus);
    }

    @Override
    public void edit(List<GoodsSkuDTO> kDTOs, String goodsId, String merchantId, Integer commodityPass) {
        List<Sku> skus = new ArrayList<>();
        Merchant merchant = merchantService.getByMerchantId(merchantId);

        for (GoodsSkuDTO kDTO : kDTOs) {
            String skuId = kDTO.getSkuId();
            Sku sku = getBySkuId(skuId);
            Long oldStock = sku.getStock();
            Long sales = sku.getSales();

            if (commodityPass.equals(CommodityPassTypeEnum.INNER.getType())) {
                sku.setStock(kDTO.getStock());

                if (kDTO.getStock() - oldStock < 0) {
//                    throw new MyException(MallResponseError.STOCK_NOT_CORN);
                }

                //发行NFT
                String hash = chainUtil.evmPublishTokenSplit(merchant.getTokenIssueAddr(), sku.getTokenId(), kDTO.getStock() - oldStock);
                //异步确认
                Map<String, Object> map = new HashMap<>();
                map.put("hash", hash);
                map.put("skuId", skuId);
                map.put("cir", sku.getCirculation() + kDTO.getStock() - oldStock);
                producer.sendTTL(map, QueueTTLTypeEnum.CHANGE_COINS_TTL, 1000);
            }
            updateById(sku);
        }
    }

    @Override
    public void republish(List<GoodsSkuDTO> kDTOs, String goodsId, String name, String merchantId, Integer commodityPass, Integer pDTOType) {
        Merchant merchant = merchantService.getByMerchantId(merchantId);
        /*int subCount = 0;
        int total = 0;
        if (pDTOType.equals(6)) {
            total = (int) kDTOs.stream().filter(GoodsSkuDTO::getRepublish).count();
        }

        //删除旧数据
        List<String> skuID = kDTOs.stream().map(GoodsSkuDTO::getSkuId).collect(Collectors.toList());
        List<String> skuIDAll = getByGoodsId(goodsId).stream().map(GoodsSkuVO::getSkuId).collect(Collectors.toList());

        if (skuID.size() == 0) {
            skuIDAll.forEach(v -> {
                UpdateWrapper<Sku> uw = new UpdateWrapper<>();
                uw.lambda().set(Sku::getStatus, 0).eq(Sku::getSkuId, v);
                update(uw);
            });
        } else {
            skuIDAll.forEach(v -> {
                if (!skuID.contains(v)) {
                    UpdateWrapper<Sku> uw = new UpdateWrapper<>();
                    uw.lambda().set(Sku::getStatus, 0).eq(Sku::getSkuId, v);
                    update(uw);
                }
            });
        }*/

        for (GoodsSkuDTO kDTO : kDTOs) {
            String skuId = kDTO.getSkuId();

            Boolean republish = kDTO.getRepublish();
            String coinName = kDTO.getCoinName().toUpperCase();
            String issueAddr = merchant.getTokenIssueAddr();
            String addrKey = merchant.getIssueAddrKey();

            if (skuId != null) {
                //编辑
                //TODO 数据库操作安全
                Sku one = getOne(new QueryWrapper<Sku>().lambda().eq(Sku::getSkuId, skuId));
                Long oldStock = one.getStock();
                one.setOriginalPrice(kDTO.getOriginalPrice());
                one.setStock(kDTO.getStock());
                one.setWeight(kDTO.getWeight());
                one.setCoinName(kDTO.getCoinName().toUpperCase());
                one.setStatus(1);
//                skus.add(one);

                /*if (commodityPass.equals(1) && !pDTOType.equals(6)) {
                    if (republish) {
                        //发行商品币
                        if (getOne(new QueryWrapper<Sku>().lambda().eq(Sku::getCoinName, coinName)) != null) {
                            throw new MyException(MallResponseError.COIN_HAS_EXIST);
                        }

                        Map<String, Object> map = new HashMap<>();
                        map.put("coinName", coinName);
                        map.put("stock", kDTO.getStock());
                        map.put("tokenAddr", merchant.getTokenIssueAddr());
                        map.put("goodsId", goodsId);
                        map.put("skuId", skuId);
                        map.put("subCount", ++subCount);
                        map.put("total", total);
                        producer.sendTTL(map, QueueTTLTypeEnum.ISSUE_COINS_TTL, 5 * 1000);
                    } else {
                        //增减货币
                        long change = oldStock - kDTO.getStock();
                        Map<String, Object> map = new HashMap<>();
                        map.put("coinName", coinName);
                        map.put("change", change);
                        map.put("issueAddr", issueAddr);
                        map.put("addrKey", addrKey);
                        map.put("goodsId", goodsId);
                        map.put("skuId", skuId);
                        producer.sendTTL(map, QueueTTLTypeEnum.CHANGE_COINS_TTL, 5 * 1000);
                    }
                }
                if (commodityPass.equals(2) && !pDTOType.equals(6)) {
                    LambdaQueryWrapper<ExternalToken> qw = new LambdaQueryWrapper<>();
                    qw.eq(ExternalToken::getCommodityPassId, kDTO.getCommodityPassId());
                    ExternalToken externalToken = externalTokenService.getOne(qw);
                    externalToken.setBindNumber(externalToken.getBindNumber().add(new BigDecimal(kDTO.getStock() - oldStock)));
                    externalTokenService.updateById(externalToken);
                }*/
                if (commodityPass.equals(1) && pDTOType.equals(6)) {
                    String tokenId = one.getCoinName().substring(4);
                    long balance = chainUtil.evmBalance(merchant.getTokenIssueAddr(), Long.parseLong(tokenId));
                    String hash = "";
                    if (balance != 0) {
                        hash = chainUtil.evmPublishTokenSplit(merchant.getTokenIssueAddr(), Long.parseLong(tokenId), one.getStock());
                        Map<String, Object> map = new HashMap<>();
                        map.put("goodsId", goodsId);
                        map.put("hash", hash);
                        producer.sendTTL(map, QueueTTLTypeEnum.NFT_ISSUE_TTL, 10 * 1000);
                    }
                }

                if (commodityPass.equals(3)) {
                    if (kDTO.getNftSalesType().equals(2)) {
                        //发行NFT
                        ExternalToken externalToken = externalTokenService.queryByLabelNameAndMerchantId(kDTO.getCoinName(), merchantId);

                        String tokenId = externalToken.getTokenId().toString();
                        long[] nos = new long[Math.toIntExact(kDTO.getStock())];
                        String[] hashs = new String[Math.toIntExact(kDTO.getStock())];
                        for (int i = 0; i < kDTO.getStock(); i++) {
                            String suffix = i + 1 + "";
                            Long suffixAll = Long.parseLong(tokenId + "00000000".substring(0, 8 - suffix.length()) + suffix);
                            String hash = chainUtil.evmPublishTokenSplit(merchant.getTokenIssueAddr(), suffixAll, 1L);
                            nos[i] = suffixAll;
                            hashs[i] = hash;
                        }

                        Map<String, Object> map = new HashMap<>();
                        map.put("goodsId", goodsId);
                        map.put("hashs", hashs);
                        map.put("prefix", tokenId);
                        map.put("merchantId", merchantId);
                        map.put("commodityPassId", externalToken.getCommodityPassId());
                        map.put("nos", JSONArray.toJSON(nos));
                        producer.sendTTL(map, QueueTTLTypeEnum.NFT_USU_TTL, 10 * 1000);
                    }
                }

                updateById(one);

            } else {
                /*if (coinName.equals("RMB")) {
                    throw new MyException(MallResponseError.COIN_NOT_RMB);
                }

                String newSkuId = UUIDUtil.getUUID();

                //规格属性入库
                skuPropService.add(kDTO.getPropertyList(), newSkuId);

                //规格入库
                Sku sku = beanMapper.map(kDTO, Sku.class);
                sku.setThumb(kDTO.getImage());
                sku.setSkuId(newSkuId);
                sku.setGoodsId(goodsId);
                sku.setCoinName(coinName);

                if (commodityPass.equals(1)) {
                    //发行商品币
                    if (getOne(new QueryWrapper<Sku>().lambda().eq(Sku::getCoinName, coinName)) != null) {
                        throw new MyException(MallResponseError.COIN_HAS_EXIST);
                    }

                    Map<String, Object> map = new HashMap<>();
                    map.put("coinName", coinName);
                    map.put("stock", sku.getStock());
                    map.put("tokenAddr", merchant.getTokenIssueAddr());
                    map.put("goodsId", goodsId);
                    map.put("skuId", skuId);
                    map.put("subCount", ++subCount);
                    map.put("total", total);
                    producer.sendTTL(map, QueueTTLTypeEnum.ISSUE_COINS_TTL, 5 * 1000);
                }
                if (commodityPass.equals(2)) {
                    LambdaUpdateWrapper<ExternalToken> uw = new LambdaUpdateWrapper<>();
                    uw.set(ExternalToken::getBindNumber, sku.getStock());
                    uw.set(ExternalToken::getGoodsId, goodsId);
                    uw.set(ExternalToken::getGoodsName, name);
                    uw.set(ExternalToken::getStatus, 1);
                    uw.eq(ExternalToken::getCommodityPassId, sku.getCommodityPassId());
                    externalTokenService.update(uw);
                }
                save(sku);*/
            }
        }
    }

    @Override
    public void updateCirculation(String skuId, Integer num) {
        Sku sku = getBySkuId(skuId);
        UpdateWrapper<Sku> uw = new UpdateWrapper<>();
        uw.lambda()
                .set(Sku::getCirculation, sku.getCirculation() + num)
                .eq(Sku::getSkuId, skuId);
        if (!update(uw)) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }
    }

    @Override
    public void updateStatus(String skuId, Integer status, String errMsg) {
        UpdateWrapper<Sku> uw = new UpdateWrapper<>();
        uw.lambda()
                .set(Sku::getStatus, status)
                .set(Sku::getErrMsg, errMsg)
                .eq(Sku::getSkuId, skuId);
        if (!update(uw)) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }
    }

    @Override
    public Map<String, Object> getMaxAndMinPrice(String goodsId) {
        QueryWrapper<Sku> qw = new QueryWrapper<>();
        qw.select("max(original_price) as maxPrice, min(original_price) as minPrice")
                .eq("goods_id", goodsId);
        return getMap(qw);
    }

    @Override
    public List<GoodsSkuVO> getByGoodsId(String goodsId) {
        QueryWrapper<Sku> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(Sku::getGoodsId, goodsId)
                .ne(Sku::getStatus, SkuStatusEnum.DELETE.getStatus());
        List<GoodsSkuVO> skuVOS = list(qw).stream()
                .map(v -> {
                    GoodsSkuVO vo = beanMapper.map(v, GoodsSkuVO.class);
                    vo.setImage(v.getThumb());
                    return vo;
                })
                .peek(v -> v.setPropertyList(skuPropService.getBySkuId(v.getSkuId())))
                .peek(v -> {
                    if (!StringUtils.isBlank(v.getCommodityPassId())) {
                        ExternalToken externalToken = externalTokenService.getOne(new QueryWrapper<ExternalToken>().lambda()
                                .eq(ExternalToken::getCommodityPassId, v.getCommodityPassId()));
                        BigDecimal subtract = externalToken.getAvailableNumber().subtract(externalToken.getBindNumber());
                        v.setBalanceNum(subtract);
                    }
                })
                .collect(Collectors.toList());
        return skuVOS;
    }

    @Override
    public Sku getBySkuId(String skuId) {
        QueryWrapper<Sku> qw = new QueryWrapper<>();
        qw.lambda()
                .ne(Sku::getStatus, SkuStatusEnum.DELETE.getStatus())
                .eq(Sku::getSkuId, skuId);
        return getOne(qw);
    }

    @Override
    public Map<String, Object> getStockAndSalesSum(String goodsId) {
        QueryWrapper<Sku> qw = new QueryWrapper<>();
        qw.select("sum(stock) as stock, sum(sales) as sales")
                .eq("goods_id", goodsId);
        return getMap(qw);
    }

    /**
     * 商品订单退款-库存还原
     */
    @Override
    public void restoreInventory(List<OrderDetail> orderDetailList) {
        orderDetailList.forEach(orderDetail -> {
            Sku sku = getOne(new QueryWrapper<Sku>().eq("goods_id", orderDetail.getGoodsId()).eq("sku_id", orderDetail.getSkuId()));
            //减销量
            Long sales = sku.getSales() - orderDetail.getNumber();
            sku.setSales(sales > 0 ? sales : 0);
            //加库存
            sku.setStock(sku.getStock() + orderDetail.getNumber());
            boolean update = update(sku, new UpdateWrapper<Sku>().eq("goods_id", orderDetail.getGoodsId())
                    .eq("sku_id", orderDetail.getSkuId()).eq("update_time", sku.getUpdateTime()));
            if (!update) {
                throw new MyException(MallResponseError.UPDATE_FAIL);
            }
        });
    }

    @Override
    public List<GoodsPackageSkuVO> getSkuByGoodsIdForPackage(String goodsId) {
        LambdaQueryWrapper<Sku> qw = new LambdaQueryWrapper<>();
        qw.eq(Sku::getGoodsId, goodsId);
        List<Sku> skuList = list(qw);

        List<GoodsPackageSkuVO> list = skuList.stream()
                .map(v -> beanMapper.map(v, GoodsPackageSkuVO.class))
                .peek(v -> {
                    StringBuilder name = new StringBuilder();
                    List<SkuPropertyVO> skuPropList = skuPropService.getBySkuId(v.getSkuId());
                    for (SkuPropertyVO vo : skuPropList) {
                        name.append(vo.getPropKey())
                                .append(":")
                                .append(vo.getPropVal())
                                .append(",");
                    }
                    String subname = name.substring(0, name.length() - 1);
                    v.setName(subname);
                }).collect(Collectors.toList());
        return list;
    }

    @Override
    public Sku getByCoin(String coin) {
        QueryWrapper<Sku> qw = new QueryWrapper<>();
        qw.lambda().eq(Sku::getCoinName, coin);
        return getOne(qw);
    }

    @Override
    public void removeByGoodsId(String goodsId) {
        LambdaQueryWrapper<Sku> qw = new LambdaQueryWrapper<>();
        qw.eq(Sku::getGoodsId, goodsId);
        List<Sku> skuList = list(qw);
        skuList.forEach(v -> {
            skuPropService.removeBySkuId(v.getSkuId());
            removeById(v.getId());
        });
    }
}
