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

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.constant.MallResponseError;
import com.fzm.mall.server.admin.exception.MyException;
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.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 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.*;
import java.util.stream.Collectors;

/**
 * @author wangp
 * @date 2021/3/4 16:06
 * @description 商品规格表 服务实现类
 * @since JDK 1.8
 */
@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;

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

        List<Sku> skus = new ArrayList<>();
        Merchant merchant = merchantService.getByMerchantId(merchantId);

        for (GoodsSkuDTO kDTO : kDTOs) {
            String skuId = UUIDUtil.getUUID();
            String coinName = kDTO.getCoinName().toUpperCase();

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

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

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

            if (commodityPass.equals(1) && !pDTOType.equals(6)) {
                //发行商品币
                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) && !pDTOType.equals(6)) {
                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);
            }
        }

        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();
            String coinName = kDTO.getCoinName().toUpperCase();
            String issueAddr = merchant.getTokenIssueAddr();
            String addrKey = merchant.getIssueAddrKey();

            //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());
            skus.add(one);

            if (commodityPass.equals(1)) {
                //增减货币
                long change = one.getStock() - 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)) {
                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 (!updateBatchById(skus)) {
            throw new MyException(MallResponseError.UPDATE_ERROR);
        }
    }

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