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

import com.alibaba.fastjson.JSON;
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.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.GoodsTypeEnum;
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 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
@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 goodsId = UUIDUtil.getUUID();
        Integer pDTOType = pDTO.getType();
        long timestamp = System.currentTimeMillis();

        //预售拍卖入库
        if (pDTOType.equals(PRE_SALE.getType())) {
            preSaleService.add(pDTO.getPreSaleDTO(), goodsId);
        } else if (pDTOType.equals(AUCTION.getType())) {
            auctionService.add(pDTO.getAuctionDTO(), goodsId);
        } else if (pDTOType.equals(PACK.getType())) {
            goodsPackageService.add(pDTO.getPackageDTO(), goodsId);
        } else if (pDTOType.equals(NFT.getType())) {
            nftService.add(pDTO.getNftdto(), goodsId, pDTO.getName(),
                    merchantService.getByMerchantId(merchantId).getNftAddr(),
                    merchantId, timestamp);

            if (pDTO.getNftdto().getTzType().equals(1)) {
                List<GoodsSkuDTO> objects = pDTO.getSkuList().stream().peek(v -> v.setCoinName(MALLGlobalConfig.NFT_PREFIX + timestamp))
                        .collect(Collectors.toList());
                pDTO.setSkuList(objects);
            }
        }

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

        //商品规格入库
        if (!pDTOType.equals(PACK.getType())) {
            skuService.add(pDTO.getSkuList(), goodsId, pDTO.getName(), merchantId, pDTO.getCommodityPass(), pDTOType);
        }

        Spu spu = beanMapper.map(pDTO, Spu.class);
        spu.setGoodsId(goodsId);
        spu.setMerchantId(merchantId);
        if (pDTO.getCommodityPass() == null || pDTO.getCommodityPass().equals(0) || pDTO.getCommodityPass().equals(2)) {
            spu.setStatus(SpuStatusEnum.WAIT_CHECK.getStatus());
        } else {
            spu.setStatus(SpuStatusEnum.ISSUE.getStatus());
        }
//        spu.setTemplateId(templateId);
        //设置商品最大最小值
        if (pDTOType.equals(PACK.getType())) {
            BigDecimal price = pDTO.getPackageDTO().getPrice();
            spu.setMaxPrice(price);
            spu.setDefaultPrice(price);
        } else if (pDTOType.equals(NFT.getType())) {
            BigDecimal price = pDTO.getNftdto().getPrice();
            spu.setMaxPrice(price);
            spu.setDefaultPrice(price);
        } else {
            Map<String, Object> price = skuService.getMaxAndMinPrice(goodsId);
            spu.setMaxPrice((BigDecimal) (price.get("maxPrice")));
            spu.setDefaultPrice((BigDecimal) (price.get("minPrice")));
        }

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

        //商品入库
        if (!save(spu)) {
            throw new MyException(MallResponseError.SAVE_ERROR);
        }
    }

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

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

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

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

        Spu spu = beanMapper.map(pDTO, Spu.class);
        //设置商品最大最小值
        if (type.equals(PACK.getType())) {
            BigDecimal price = pDTO.getPackageDTO().getPrice();
            spu.setMaxPrice(price);
            spu.setDefaultPrice(price);
        } else if (type.equals(NFT.getType())) {
            BigDecimal price = pDTO.getNftdto().getPrice();
            spu.setMaxPrice(price);
            spu.setDefaultPrice(price);
        } else {
            Map<String, Object> price = skuService.getMaxAndMinPrice(goodsId);
            spu.setMaxPrice((BigDecimal) (price.get("maxPrice")));
            spu.setDefaultPrice((BigDecimal) (price.get("minPrice")));
        }

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

    @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())) {
            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);
        if (eDTO.getCommodityPass() == null || eDTO.getCommodityPass().equals(0) || eDTO.getCommodityPass().equals(2) || type.equals(6)) {
            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);
        } else {
            Map<String, Object> price = skuService.getMaxAndMinPrice(goodsId);
            spu.setMaxPrice((BigDecimal) (price.get("maxPrice")));
            spu.setDefaultPrice((BigDecimal) (price.get("minPrice")));
        }

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

    @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);
            }

            if (spuVO.getType().equals(5)) {
                GoodsPackage goodsIdInner = goodsPackageService.getByGoodsIdInner(spuVO.getGoodsId());
                spuVO.setStock(goodsIdInner.getStock());
                spuVO.setSales(goodsIdInner.getSales());
            } else if (spuVO.getType().equals(6)) {
                Map<String, Object> sum = skuService.getStockAndSalesSum(spuVO.getGoodsId());
                spuVO.setStock(((BigDecimal) sum.get("stock")).longValue());
                spuVO.setSales(((BigDecimal) sum.get("sales")).longValue());
            } else {
                Map<String, Object> sum = skuService.getStockAndSalesSum(spuVO.getGoodsId());
                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());

        Integer type = spu.getType();
        if (type.equals(PRE_SALE.getType())) {
            vo.setPreSaleVO(preSaleService.getByGoodsId(goodsId));
        } else if (type.equals(AUCTION.getType())) {
            vo.setAuctionVO(auctionService.getByGoodsId(goodsId));
        } else if (type.equals(PACK.getType())) {
            vo.setGoodsPackageVO(goodsPackageService.getByGoodsId(goodsId));
        } else if (type.equals(NFT.getType())) {
            vo.setGoodsNftVO(nftService.getByGoodsId(goodsId));
        }
        return vo;
    }

    @Override
    public void offGoods(String goodsId, Integer type) {
        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(2)) {
            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);
            String hash = chainUtil.getGoodsHash(externalToken.getTokenId(), merchantService.getByMerchantId(spu.getMerchantId()).getNftAddr());
            spu.setHash(hash);
        }
        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;
    }
}
