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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
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.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.distribution.service.ITemplateService;
import com.fzm.mall.server.admin.exception.MyException;
import com.fzm.mall.server.admin.goods_center.entity.dto.*;
import com.fzm.mall.server.admin.goods_center.entity.vo.*;
import com.fzm.mall.server.admin.goods_center.enums.status.SpuStatusEnum;
import com.fzm.mall.server.admin.goods_center.enums.type.CommodityPassTypeEnum;
import com.fzm.mall.server.admin.goods_center.enums.type.GoodsTypeEnum;
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.mapper.SpuMapper;
import com.fzm.mall.server.admin.goods_center.model.*;
import com.fzm.mall.server.admin.goods_center.service.*;
import com.fzm.mall.server.admin.home_page.enums.type.AdminTypeEnum;
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.opration.service.IFloorGoodsService;
import com.fzm.mall.server.admin.util.UUIDUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.fzm.mall.server.admin.goods_center.enums.type.GoodsTypeEnum.*;

/**
 * @author wangp
 * @date 2021/3/4 16:06
 * @description 商品表 服务实现类
 * @since JDK 1.8
 */
@Service
@Slf4j
@Transactional
public class SpuServiceImpl extends ServiceImpl<SpuMapper, Spu> implements ISpuService {

    @Autowired
    private ICategorySpuService categorySpuService;
    @Autowired
    private IPreSaleService preSaleService;
    @Autowired
    private IAuctionService auctionService;
    @Autowired
    private DozerBeanMapper beanMapper;
    @Autowired
    private ISkuService skuService;
    @Autowired
    private ICountService countService;
    @Autowired
    private ITemplateService templateService;
    @Autowired
    private IMerchantService merchantService;
    @Autowired
    private SpuMapper spuMapper;
    @Autowired
    private IGoodsPackageService goodsPackageService;
    @Autowired
    private IGoodsNftService nftService;
    @Autowired
    private IExternalTokenService externalTokenService;
    @Autowired
    private ChainUtil chainUtil;

    @Override
    @Transactional
    public void add(GoodsSpuDTO pDTO, String merchantId, String templateId, String addr) {
        String goodsId = pDTO.getGoodsId();
        Spu spu = getByGoodsId(goodsId);
        long timestamp = System.currentTimeMillis();

        Integer pDTOType = pDTO.getType();
        Integer salesType = pDTO.getSalesType();

        //对goodsId重新赋值并且清除预售，拍卖，规格信息
        //商品类目表删除
        //商品销量删除
        if (spu != null) {
            //还原外部绑定nft-token   //TODO 加个判断
            if (spu.getCommodityPass().equals(CommodityPassTypeEnum.OUT.getType())) {
                releaseExternalToken(goodsId);
            }

            nftService.removeByGoodsId(goodsId);
            skuService.removeByGoodsId(goodsId);
            categorySpuService.removeByGoodsId(goodsId);
            countService.removeByGoodsId(goodsId);
            removeById(spu);
        } else {
            goodsId = UUIDUtil.getUUID();
        }

        log.info("salesType:" + pDTO.getSalesType());
        if (pDTO.getSalesType() != null && pDTO.getSalesType().equals(SalesTypeEnum.ORDINARY.getType())) {
            nftService.add(pDTO.getNftdto(), goodsId, pDTO.getName(), addr, merchantId, timestamp);
            if (pDTO.getNftdto().getTzType().equals(1)) {
                List<GoodsSkuDTO> objects = pDTO.getSkuList().stream().peek(v -> {
                    v.setCoinName(MALLGlobalConfig.NFT_PREFIX + timestamp);
                    v.setStock(pDTO.getNftdto().getNumber());
                }).collect(Collectors.toList());
                pDTO.setSkuList(objects);
            }
        }

        //商品类目表入库
        JSONArray ids = JSON.parseArray(pDTO.getCategoryId());
        categorySpuService.add(ids, goodsId, merchantId);

        //商品规格入库
        skuService.add(pDTO.getSkuList(), goodsId, pDTO.getName(), merchantId, addr, pDTO.getCommodityPass(), pDTO.getThumb(), pDTOType, pDTO.getSalesType(), pDTO.getBlindBoxPrice(), pDTO.getNftdto().getPrice());

        //初始化商品信息
        spu = beanMapper.map(pDTO, Spu.class);
        spu.setGoodsId(goodsId);
        spu.setMerchantId(merchantId);
        spu.setBlindBoxRule("无");
        Integer status = SpuStatusEnum.WAIT_CHECK.getStatus();
        if ((pDTO.getCommodityPass().equals(CommodityPassTypeEnum.COPY.getType()) && pDTO.getSkuList().get(0).getNftSalesType().equals(NFTSalesTypeEnum.COPY.getType())) || pDTO.getCommodityPass().equals(CommodityPassTypeEnum.INNER.getType())) {
            status = SpuStatusEnum.ISSUE.getStatus();
        }
        spu.setStatus(status);
        //设置商品最大最小值
        if (pDTO.getSalesType() != null && pDTO.getSalesType().equals(SalesTypeEnum.BLIND_BOX.getType())) {
            spu.setMaxPrice(pDTO.getBlindBoxPrice());
            spu.setDefaultPrice(pDTO.getBlindBoxPrice());
        } else {
            BigDecimal price = pDTO.getNftdto().getPrice();
            spu.setMaxPrice(price);
            spu.setDefaultPrice(price);
        }
        if (pDTO.getCommodityPass() == 3) {
            spu.setMaxPrice(pDTO.getSkuList().get(0).getOriginalPrice());
            spu.setDefaultPrice(pDTO.getSkuList().get(0).getOriginalPrice());
        }

        //商品销量初始化
        countService.init(goodsId);

        //商品入库
        save(spu);
    }

    @Override
    public void draft(GoodsSpuDraftDTO pDTO, String merchantId) {
        String goodsId = pDTO.getGoodsId();
        Spu spu = getByGoodsId(goodsId);
        if (spu == null) {
            goodsId = UUIDUtil.getUUID();

            //NFT商品入库
            if (pDTO.getSalesType().equals(SalesTypeEnum.ORDINARY.getType())) {
                nftService.draft(pDTO.getNftdto(), goodsId);
            }

            //商品类目表入库
            JSONArray ids = JSON.parseArray(pDTO.getCategoryId());
            categorySpuService.add(ids, goodsId, merchantId);

            //商品规格入库
            skuService.draft(pDTO.getSkuList(), goodsId);

            //初始化商品信息商品入库
            spu = beanMapper.map(pDTO, Spu.class);
            spu.setGoodsId(goodsId);
            spu.setMerchantId(merchantId);
            spu.setStatus(SpuStatusEnum.DRAFT.getStatus());
            //设置商品最大最小值
            BigDecimal price = pDTO.getNftdto().getPrice();
            if (pDTO.getSalesType().equals(SalesTypeEnum.BLIND_BOX.getType())) {
                spu.setMaxPrice(pDTO.getBlindBoxPrice());
                spu.setDefaultPrice(pDTO.getBlindBoxPrice());
            } else {
                spu.setMaxPrice(price);
                spu.setDefaultPrice(price);
            }
            if (pDTO.getCommodityPass() == 3) {
                spu.setMaxPrice(pDTO.getSkuList().get(0).getOriginalPrice());
                spu.setDefaultPrice(pDTO.getSkuList().get(0).getOriginalPrice());
            }
            save(spu);
        } else {
            if (spu.getStatus().equals(SpuStatusEnum.CHECK_PASS.getStatus()) || spu.getStatus().equals(SpuStatusEnum.CHECK_FAIL.getStatus())) {
                //还原外部绑定nft-token
                if (spu.getCommodityPass().equals(CommodityPassTypeEnum.OUT.getType())) {
                    releaseExternalToken(goodsId);
                }
            }

            //清除NFT信息
            nftService.removeByGoodsId(goodsId);

            //商品类目表删除
            categorySpuService.removeByGoodsId(goodsId);

            //清除规格信息
            skuService.removeByGoodsId(goodsId);

            //商品销量删除
            countService.removeByGoodsId(goodsId);

            //NFT商品入库
            if (spu.getSalesType().equals(SalesTypeEnum.ORDINARY.getType())) {
                nftService.draft(pDTO.getNftdto(), goodsId);
            }

            //商品类目表入库
            JSONArray ids = JSON.parseArray(pDTO.getCategoryId());
            categorySpuService.add(ids, goodsId, merchantId);

            //商品规格入库
            skuService.draft(pDTO.getSkuList(), goodsId);

            //初始化商品信息更新
            Spu spu2 = beanMapper.map(pDTO, Spu.class);
            spu2.setId(spu.getId());
            spu2.setStatus(SpuStatusEnum.DRAFT.getStatus());
            //设置商品最大最小值
            BigDecimal price = pDTO.getNftdto().getPrice();
            if (pDTO.getSalesType().equals(SalesTypeEnum.BLIND_BOX.getType())) {
                spu.setMaxPrice(pDTO.getBlindBoxPrice());
                spu.setDefaultPrice(pDTO.getBlindBoxPrice());
            } else {
                spu.setMaxPrice(price);
                spu.setDefaultPrice(price);
            }
            if (pDTO.getCommodityPass() == 3) {
                spu.setMaxPrice(pDTO.getSkuList().get(0).getOriginalPrice());
                spu.setDefaultPrice(pDTO.getSkuList().get(0).getOriginalPrice());
            }
            updateById(spu2);
        }
    }

    @Override
    @Transactional
    public void edit(GoodsEditDTO pDTO, String merchantId) {
        String goodsId = pDTO.getGoodsId();
        Spu one = getByGoodsId(goodsId);

        //更新规格
        Integer salesType = one.getSalesType();
        Integer commodityPass = one.getCommodityPass();
        if (salesType.equals(SalesTypeEnum.BLIND_BOX.getType()) && commodityPass.equals(CommodityPassTypeEnum.INNER.getType())) {
            skuService.edit(pDTO.getSkuList(), goodsId, merchantId, commodityPass);
        }
    }

    @Override
    public void republish(GoodsRepublishUpdateDTO eDTO, String merchantId) {
        String goodsId = eDTO.getGoodsId();

        QueryWrapper<Spu> qw = new QueryWrapper<>();
        qw.lambda().eq(Spu::getGoodsId, goodsId);
        Spu one = getOne(qw);
        Integer type = one.getType();

        //商品类目表入库
        JSONArray ids = JSON.parseArray(eDTO.getCategoryId());
        categorySpuService.delByGoodsIds(goodsId);
        categorySpuService.add(ids, goodsId, merchantId);

        //预售拍卖编辑
        if (type.equals(PRE_SALE.getType())) {
            preSaleService.edit(eDTO.getPreSaleDTO(), goodsId);
        } else if (type.equals(AUCTION.getType())) {
            auctionService.edit(eDTO.getAuctionDTO(), goodsId);
        } else if (type.equals(PACK.getType())) {
            goodsPackageService.edit(eDTO.getPackageDTO(), goodsId);
        } else if (type.equals(NFT.getType()) && !one.getCommodityPass().equals(CommodityPassTypeEnum.COPY.getType())) {
            nftService.edit(eDTO.getNftdto(), goodsId);
        }

        //更新规格
        if (!type.equals(PACK.getType())) {
            skuService.republish(eDTO.getSkuList(), goodsId, one.getName(), merchantId, one.getCommodityPass(), type);
        }

        Spu spu = beanMapper.map(eDTO, Spu.class);
        spu.setId(one.getId());
        if (eDTO.getCommodityPass() == null || eDTO.getCommodityPass().equals(0) || eDTO.getCommodityPass().equals(2) || (eDTO.getSkuList().get(0) != null && eDTO.getSkuList().get(0).getNftSalesType() == 1)) {
            spu.setStatus(SpuStatusEnum.WAIT_CHECK.getStatus());
        } else {
            spu.setStatus(SpuStatusEnum.ISSUE.getStatus());
        }
        //设置商品最大最小值
        if (type.equals(PACK.getType())) {
            BigDecimal price = eDTO.getPackageDTO().getPrice();
            spu.setMaxPrice(price);
            spu.setDefaultPrice(price);
        } else if (type.equals(NFT.getType())) {
            BigDecimal price = eDTO.getNftdto().getPrice();
            spu.setMaxPrice(price);
            spu.setDefaultPrice(price);
            if (one.getCommodityPass() == 3) {
                spu.setMaxPrice(eDTO.getSkuList().get(0).getOriginalPrice());
                spu.setDefaultPrice(eDTO.getSkuList().get(0).getOriginalPrice());
            }
        } else {
            Map<String, Object> price = skuService.getMaxAndMinPrice(goodsId);
            spu.setMaxPrice((BigDecimal) (price.get("maxPrice")));
            spu.setDefaultPrice((BigDecimal) (price.get("minPrice")));
        }

        updateById(spu);
    }

    @Override
    public void cancel(String goodsId) {
        UpdateWrapper<Spu> uw = new UpdateWrapper<>();
        uw.lambda()
                .set(Spu::getStatus, SpuStatusEnum.CANCEL.getStatus())
                .eq(Spu::getGoodsId, goodsId);
        if (!update(uw)) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }
    }

    @Override
    public void updateStatus(String goodsId, Integer status, String errMsg) {
        LambdaUpdateWrapper<Spu> uw = new LambdaUpdateWrapper<>();
        uw.set(Spu::getStatus, status);
        uw.set(Spu::getCheckFailReason, errMsg);
        uw.eq(Spu::getGoodsId, goodsId);
        if (!update(uw)) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }
    }

    @Override
    public IPage<GoodsSpuVO> page(String merchantId, Integer type, String goodsId, String goodsName, Integer status, Long addTimeHead, Long addTimeTail, Integer goodsType, Integer pageNum, Integer pageSize) {
        IPage<GoodsSpuVO> page = new Page<>(pageNum, pageSize);
        IPage<GoodsSpuVO> iPage = baseMapper.page(page, merchantId, type, goodsId, goodsName, status, addTimeHead, addTimeTail, goodsType);

        List<GoodsSpuVO> records = iPage.getRecords();
        List<GoodsSpuVO> newRecords = new ArrayList<>();

        for (GoodsSpuVO spuVO : records) {
            if (!(spuVO.getStatus().equals(4) || spuVO.getStatus().equals(6))) {
                spuVO.setCheckFailReason(null);
            }

            Map<String, Object> sum = skuService.getStockAndSalesSum(spuVO.getGoodsId());
            if (sum != null) {
                spuVO.setStock(((BigDecimal) sum.get("stock")).longValue());
                spuVO.setSales(((BigDecimal) sum.get("sales")).longValue());
            }
            newRecords.add(spuVO);
        }
        iPage.setRecords(newRecords);
        return iPage;
    }

    @Override
    public Spu getByGoodsId(String goodsId) {
        LambdaQueryWrapper<Spu> qw = new LambdaQueryWrapper<>();
        qw.eq(Spu::getGoodsId, goodsId);
        qw.ne(Spu::getStatus, SpuStatusEnum.DELETE.getStatus());
        return getOne(qw);
    }

    @Override
    public GoodsSpuDetailVO getVoByGoodsId(String goodsId) {
        QueryWrapper<Spu> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(Spu::getGoodsId, goodsId)
                .ne(Spu::getStatus, SpuStatusEnum.DELETE.getStatus());
        Spu spu = getOne(qw);

        GoodsSpuDetailVO vo = beanMapper.map(spu, GoodsSpuDetailVO.class);
        vo.setSkus(skuService.getByGoodsId(goodsId));
        vo.setCategoryName(categorySpuService.getByGoodsId(goodsId));
        vo.setCategoryIds(categorySpuService.getByGoodsIdForId(goodsId));
//        vo.setTemplateName(templateService.getByTemplateId(vo.getTemplateId()).getTemplateName());

        if (spu.getSalesType().equals(SalesTypeEnum.ORDINARY.getType())) {
            vo.setGoodsNftVO(nftService.getByGoodsId(goodsId));
        }
        return vo;
    }

    @Override
    public void offGoods(String goodsId, Integer type, Long preheatEndTime) {
        if (getByGoodsId(goodsId).getType().equals(AUCTION.getType())) {
            QueryWrapper<Auction> qw = new QueryWrapper<>();
            qw.lambda().eq(Auction::getGoodsId, goodsId);
            Auction auction = auctionService.getOne(qw);

            if (auction.getEndTime() <= System.currentTimeMillis()) {
                throw new MyException(MallResponseError.ACTION_GOODS_overdue);
            } else {
                auction.setStartTime(System.currentTimeMillis());
                auctionService.updateById(auction);
            }
        }

        //TODO banner 推荐商品是否删除
        Spu spu = getByGoodsId(goodsId);
        if (!spu.getCommodityPass().equals(1)) {
            LambdaQueryWrapper<Sku> qw1 = new LambdaQueryWrapper<>();
            qw1.eq(Sku::getGoodsId, goodsId);
            Sku sku = skuService.getOne(qw1);
            LambdaQueryWrapper<ExternalToken> qw2 = new LambdaQueryWrapper<>();
            qw2.eq(ExternalToken::getLabelName, sku.getCoinName());
            qw2.eq(ExternalToken::getMerchantId, spu.getMerchantId());
            ExternalToken externalToken = externalTokenService.getOne(qw2);
            spu.setHash(externalToken.getHash());
        }
        if (preheatEndTime != null) {
            spu.setPreheatEndTime(preheatEndTime);
        }
        spu.setStatus(type);
        updateById(spu);
    }

    @Override
    @Transactional
    public void deleteBatch(List<String> goodsIds, String merchantId) {
        for (String goodsId : goodsIds) {
            Spu spu = getByGoodsId(goodsId);
            if (spu == null || !spu.getMerchantId().equals(merchantId)) {
                throw new MyException(MallResponseError.NO_AUTH_ERROR);
            }

            UpdateWrapper<Spu> uw = new UpdateWrapper<>();
            uw.lambda()
                    .set(Spu::getStatus, SpuStatusEnum.DELETE.getStatus())
                    .eq(Spu::getGoodsId, goodsId);
            if (!update(uw)) {
                throw new MyException(MallResponseError.UPDATE_ERROR);
            }


            if (spu.getCommodityPass().equals(2)) {
                GoodsNftVO nftVO = nftService.getByGoodsId(goodsId);

                boolean update = false;
                while (!update) {
                    ExternalToken externalToken = externalTokenService.queryByLabelNameAndMerchantId(nftVO.getTokenId(), merchantId);
                    LambdaUpdateWrapper<ExternalToken> uwT = new LambdaUpdateWrapper<>();
                    uwT.set(ExternalToken::getBindNumber, BigDecimal.ZERO);
                    uwT.set(ExternalToken::getGoodsId, "");
                    uwT.set(ExternalToken::getGoodsName, "");
                    uwT.set(ExternalToken::getStatus, 0);
                    uwT.eq(ExternalToken::getLabelName, nftVO.getTokenId());
                    uwT.eq(ExternalToken::getMerchantId, merchantId);
                    uwT.eq(ExternalToken::getUpdateTime, externalToken.getUpdateTime());
                    update = externalTokenService.update(uwT);
                }
            }
        }
    }

    @Override
    public void goodsCheck(GoodsCheckDTO checkDTO) {
        Spu spu = getByGoodsId(checkDTO.getGoodsId());
        if (spu.getStatus().equals(SpuStatusEnum.CANCEL.getStatus())) {
            throw new MyException(MallResponseError.CANCEL_ERROR);
        }

        UpdateWrapper<Spu> uw = new UpdateWrapper<>();
        uw.lambda()
                .set(Spu::getCheckFailReason, checkDTO.getCheckFailReason())
                .set(Spu::getStatus, checkDTO.getStatus())
                .eq(Spu::getGoodsId, checkDTO.getGoodsId());
        if (!update(uw)) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }
    }

    @Override
    public void goodsCheckBatch(List<GoodsCheckDTO> checkDTOList) {
        for (GoodsCheckDTO checkDTO : checkDTOList) {
            goodsCheck(checkDTO);
        }
    }

    @Override
    public void goodsNameExist(String goodsName) {
        QueryWrapper<Spu> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(Spu::getName, goodsName)
                .ne(Spu::getStatus, SpuStatusEnum.DELETE.getStatus());
        if (getOne(qw) != null) {
            throw new MyException(MallResponseError.GOODS_NAME_EXIST);
        }
    }

    @Override
    public List<GoodsListVO> queryByCategory(List<String> categoryIds, String merchantId, String goods) {
        List<GoodsListVO> vos = baseMapper.queryByCategory(categoryIds, merchantId, goods);
        List<GoodsListVO> list = vos.stream().distinct().collect(Collectors.toList());
        return list;
    }

    @Override
    public List<GoodsListVO> queryByNameOrId(String merchantId, String goods) {
        return spuMapper.queryByNameOrId(merchantId, Spu.SPU_STATUS_ON_SALE, goods);
    }

    @Override
    public List<GoodsListVO> getSpuByVague(String goodsInfo, String merchantId) {
        LambdaQueryWrapper<Spu> qw = new LambdaQueryWrapper<>();
        qw.eq(Spu::getStatus, 1);
        qw.eq(Spu::getMerchantId, merchantId);
        qw.and(qws -> qws.eq(Spu::getGoodsId, goodsInfo).or().like(Spu::getName, goodsInfo));
        List<Spu> spuList = list(qw);

        List<GoodsListVO> list = spuList.stream()
                .map(v -> beanMapper.map(v, GoodsListVO.class))
                .collect(Collectors.toList());

        return list;
    }

    public void releaseExternalToken(String goodsId) {
        List<GoodsSkuVO> skuList = skuService.getByGoodsId(goodsId);
        for (GoodsSkuVO skuVO : skuList) {
            ExternalToken externalToken = externalTokenService.queryByComId(skuVO.getCommodityPassId());
            externalToken.setBindNumber(BigDecimal.ZERO);
            externalToken.setGoodsId("");
            externalToken.setGoodsName("");
            externalToken.setStatus(0);
            externalTokenService.updateById(externalToken);
        }
    }

    @Override
    public List<Spu> listForPreheat() {
        LambdaQueryWrapper<Spu> qw = new LambdaQueryWrapper<>();
        qw.eq(Spu::getStatus, SpuStatusEnum.PREHEAT.getStatus());
        return list(qw);
    }
}

