package com.czg.service.product.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.czg.account.entity.ShopConfig;
import com.czg.account.entity.ShopInfo;
import com.czg.account.service.ShopConfigService;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
import com.czg.account.service.SyncNoticeService;
import com.czg.exception.CzgException;
import com.czg.product.entity.*;
import com.czg.product.service.*;
import com.czg.product.vo.ProductGroupVo;
import com.czg.utils.AssertUtil;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import static com.mybatisflex.core.query.QueryMethods.column;

/**
 * @author ww
 * @description
 */
@Slf4j
@DubboService
public class ShopSyncServiceImpl implements ShopSyncService {
    @DubboReference
    private ShopInfoService shopInfoService;
    @DubboReference
    private ShopConfigService shopConfigService;
    @Resource
    private ShopProdUnitService unitService;
    @Resource
    private ShopProdSpecService specService;
    @Resource
    private ShopProdCategoryService categoryService;
    @Resource
    private ProductService productService;
    @Resource
    private ProdSkuService skuService;
    @Resource
    private ProdGroupService groupService;
    @Resource
    private ProdGroupRelationService prodGroupRelationService;
    @Resource
    private ConsInfoService consInfoService;
    @Resource
    private ConsGroupService consGroupService;
    @Resource
    private ProdConsRelationService prodConsRelationService;
    @Resource
    private ShopUserService shopUserService;
    @DubboReference
    private SyncNoticeService syncNoticeService;

    private void checkShopInfo(Long sourceShopId, Long targetShopId) {
        AssertUtil.isNull(sourceShopId, "{}不能为空", "源店铺ID");
        AssertUtil.isNull(targetShopId, "{}不能为空", "目标店铺ID");
        ShopInfo sourceShop = shopInfoService.getById(sourceShopId);
        if (StrUtil.isBlank(sourceShop.getShopType()) || "only".equals(sourceShop.getShopType())
                || sourceShop.getIsHeadShop() == null || sourceShop.getIsHeadShop() != 1) {
            throw new CzgException("同步失败,源店铺不是主店铺或源店铺是单店");
        }
        ShopInfo targetShop = shopInfoService.getById(targetShopId);
        if (StrUtil.isBlank(targetShop.getShopType()) || "only".equals(targetShop.getShopType())
                || targetShop.getIsHeadShop() == null || targetShop.getIsHeadShop().equals(1)) {
            throw new CzgException("同步失败,目标店铺是主店铺或目标店铺是单店");
        }
    }

    /**
     * 校验主店
     *
     * @param sourceShopId 主店铺ID
     * @param type         1-同步商品内容 2-内部耗材内容
     * @return 开启该开关的子店集合
     */
    private Set<Long> checkSourceShopInfo(Long sourceShopId, int type) {
        AssertUtil.isNull(sourceShopId, "{}不能为空", "源店铺ID");
        ShopInfo sourceShop = shopInfoService.getById(sourceShopId);
        if (StrUtil.isBlank(sourceShop.getShopType()) || "only".equals(sourceShop.getShopType())
                || sourceShop.getIsHeadShop() == null || sourceShop.getIsHeadShop() != 1) {
            throw new CzgException("同步失败,源店铺不是主店铺或源店铺是单店");
        }
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.select(column(ShopConfig::getId));
        queryWrapper.eq(ShopConfig::getMainId, sourceShopId);
        switch (type) {
            case 1:
                queryWrapper.eq(ShopConfig::getIsEnableProdSync, 1);
                break;
            case 2:
                queryWrapper.eq(ShopConfig::getIsEnableConsSync, 1);
                break;
        }
        List<Long> ids = shopConfigService.listAs(queryWrapper, Long.class);
        return new HashSet<>(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sync(Long sourceShopId, Long targetShopId, Long sysUserId) {
        log.info("同步开始,源店铺ID:{},目标店铺ID:{}", sourceShopId, targetShopId);
        Map<Long, Long> unitMap;
        Map<Long, Long> specMap;
        Map<Long, Long> categoryMap;
        Map<Long, Long> proMap = new HashMap<>();
        Map<Long, Long> skuMap = new HashMap<>();
        checkShopInfo(sourceShopId, targetShopId);
        List<Map<String, Object>> mainMapList = new ArrayList<>();
        //商品
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            CompletableFuture<Map<Long, Long>> futureUnit = CompletableFuture.supplyAsync(() -> syncUnit(sourceShopId, targetShopId, mainMapList), executor);
            CompletableFuture<Map<Long, Long>> futureSpec = CompletableFuture.supplyAsync(() -> syncSpec(sourceShopId, targetShopId, mainMapList), executor);
            CompletableFuture<Map<Long, Long>> futureCategory = CompletableFuture.supplyAsync(() -> syncCategory(sourceShopId, targetShopId, mainMapList), executor);

            CompletableFuture<Void> allFutures = CompletableFuture.allOf(futureUnit, futureSpec, futureCategory);
            allFutures.join();

            unitMap = futureUnit.join();
            specMap = futureSpec.join();
            categoryMap = futureCategory.join();
        } catch (Exception e) {
            log.error("同步异常", e);
            throw new CzgException("同步失败");
        }
        proMap = syncProduct(sourceShopId, targetShopId, unitMap, specMap, categoryMap, mainMapList);
        skuMap = syncSku(sourceShopId, targetShopId, proMap);
        syncGroup(sourceShopId, targetShopId, proMap, mainMapList);
        syncProductPackage(targetShopId, proMap, skuMap);


        //耗材
        Map<Long, Long> conGroupMap = syncConsGroup(sourceShopId, targetShopId, mainMapList);
        Map<Long, Long> consInfoMap = syncConsInfo(sourceShopId, targetShopId, conGroupMap, mainMapList);
        syncConsPro(sourceShopId, targetShopId, consInfoMap, proMap);
        syncNoticeService.addNotice(targetShopId, sysUserId, "数据同步", JSON.toJSONString(mainMapList), null);
        log.info("同步结束,源店铺ID:{},目标店铺ID:{}", sourceShopId, targetShopId);

    }

    /**
     * 构建消息
     *
     * @param mainMapList 主消息集合
     * @param title       内容title  "XX同步/变动",//商品 单位 规格 分组 耗材 耗材分组
     * @param size        变动数量
     * @param dataMap     变动数据集合<Id,名称>
     */
    private void buildNotice(List<Map<String, Object>> mainMapList, String title, int size, String type, Map<Long, String> dataMap) {
        Map<String, Object> mainJsonMap = new HashMap<>();
        mainJsonMap.put("title", title);
        mainJsonMap.put("number", size + "个");
        if (StrUtil.isNotBlank(type)) {
            mainJsonMap.put("type", type);
        }
        if (CollUtil.isNotEmpty(dataMap)) {
            List<Map<String, Object>> mapList = new ArrayList<>();
            dataMap.forEach((k, v) -> {
                Map<String, Object> innerMap = new HashMap<>();
                innerMap.put("id", k);
                innerMap.put("name", v);
                mapList.add(innerMap);
            });
            mainJsonMap.put("map", mapList);
        }
        mainMapList.add(mainJsonMap);
    }

    public Map<Long, Long> syncUnit(Long sourceShopId, Long pointShopId, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> unitMap = new HashMap<>();
        List<Long> pointShopUnits = new ArrayList<>();
        List<ShopProdUnit> pointUnits = unitService.queryChain().select(ShopProdUnit::getSyncId)
                .eq(ShopProdUnit::getShopId, pointShopId)
                .isNotNull(ShopProdUnit::getSyncId)
                .list();
        pointUnits.stream().forEach(tbShopUnit -> {
            unitMap.put(tbShopUnit.getSyncId(), tbShopUnit.getId());
            pointShopUnits.add(tbShopUnit.getSyncId());
        });
        List<ShopProdUnit> list = unitService.queryChain().eq(ShopProdUnit::getShopId, sourceShopId).list();
        list.forEach(tbShopUnit -> {
            if (CollUtil.isEmpty(pointShopUnits) || !pointShopUnits.contains(tbShopUnit.getId())) {
                ShopProdUnit unitNew = BeanUtil.copyProperties(tbShopUnit, ShopProdUnit.class);
                unitNew.setId(null);
                unitNew.setSyncId(tbShopUnit.getId());
                unitNew.setShopId(pointShopId);
                unitService.save(unitNew);
                unitMap.put(tbShopUnit.getId(), unitNew.getId());
            }
        });
        buildNotice(mainMapList, "单位同步", unitMap.size() - pointShopUnits.size(), null, null);
        log.info("单位同步,源{}个,已有{}个,同步{}个", list.size(), pointShopUnits.size(), unitMap.size() - pointShopUnits.size());
        return unitMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncUnitBySourceShop(Long sourceShopId, Long unitId, Long sysUserId) {
        AssertUtil.isNull(unitId, "{}不能为空", "单位ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ShopProdUnit mainUnit = unitService.getById(unitId);
        AssertUtil.isNull(mainUnit, "需要同步的{}不存在", "单位");
        List<ShopProdUnit> list = unitService.queryChain().eq(ShopProdUnit::getSyncId, unitId).in(ShopProdUnit::getShopId, shopIds).list();
        Map<Long, ShopProdUnit> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ShopProdUnit::getShopId, t -> t));
        }
        Map<Long, ShopProdUnit> finalMap = map;
        shopIds.forEach(shopId -> {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> unitDataMap = new HashMap<>();
            ShopProdUnit newUnit = BeanUtil.copyProperties(mainUnit, ShopProdUnit.class);

            newUnit.setSyncId(mainUnit.getId());
            newUnit.setShopId(shopId);
            if (finalMap.containsKey(shopId)) {
                ShopProdUnit unit = finalMap.get(shopId);
                newUnit.setId(unit.getId());
                unitService.updateById(newUnit);
            } else {
                newUnit.setId(null);
                unitService.save(newUnit);
            }
            unitDataMap.put(newUnit.getId(), newUnit.getName());
            buildNotice(mainMapList, "单位变动", 1, "unit", unitDataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newUnit));
        });
    }

    //规格
    public Map<Long, Long> syncSpec(Long sourceShopId, Long pointShopId, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> specMap = new HashMap<>();
        List<Long> pointShopSpecs = new ArrayList<>();
        List<ShopProdSpec> pointList = specService.queryChain().select(ShopProdSpec::getSyncId)
                .eq(ShopProdSpec::getShopId, pointShopId)
                .isNotNull(ShopProdSpec::getSyncId)
                .list();
        for (ShopProdSpec shopProdSpec : pointList) {
            pointShopSpecs.add(shopProdSpec.getSyncId());
            specMap.put(shopProdSpec.getSyncId(), shopProdSpec.getId());
        }
        List<ShopProdSpec> list = specService.queryChain().eq(ShopProdSpec::getShopId, sourceShopId.toString()).list();
        list.forEach(spec -> {
            if (CollUtil.isEmpty(pointShopSpecs) || !pointShopSpecs.contains(spec.getId())) {
                ShopProdSpec newSpec = BeanUtil.copyProperties(spec, ShopProdSpec.class);
                newSpec.setId(null);
                newSpec.setSyncId(spec.getId());
                newSpec.setShopId(pointShopId);
                specService.save(newSpec);
                specMap.put(spec.getId(), newSpec.getId());
            }
        });
        log.info("规格同步,源{}个,已有{}个,同步{}个", list.size(), pointShopSpecs.size(), specMap.size() - pointShopSpecs.size());
        buildNotice(mainMapList, "规格同步", specMap.size() - pointShopSpecs.size(), null, null);
        return specMap;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncSpecBySourceShop(Long sourceShopId, Long specId, Long sysUserId) {
        AssertUtil.isNull(specId, "{}不能为空", "规格ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ShopProdSpec mainEntity = specService.getById(specId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "规格");
        List<ShopProdSpec> list = specService.queryChain().eq(ShopProdSpec::getSyncId, specId).in(ShopProdSpec::getShopId, shopIds).list();
        Map<Long, ShopProdSpec> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ShopProdSpec::getShopId, t -> t));
        }
        Map<Long, ShopProdSpec> finalMap = map;
        shopIds.forEach(shopId -> {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ShopProdSpec newEntity = BeanUtil.copyProperties(mainEntity, ShopProdSpec.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            if (finalMap.containsKey(shopId)) {
                ShopProdSpec childEntity = finalMap.get(shopId);
                newEntity.setId(childEntity.getId());
                specService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                specService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getName());
            buildNotice(mainMapList, "规格变动", 1, "spec", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
        });
    }

    // 分类
    public Map<Long, Long> syncCategory(Long sourceShopId, Long pointShopId, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> categoryMap = new HashMap<>();
        List<Long> pointShopCateGory = new ArrayList<>();
        List<ShopProdCategory> pointList = categoryService.queryChain().select(ShopProdCategory::getSyncId)
                .eq(ShopProdCategory::getShopId, pointShopId)
                .isNotNull(ShopProdCategory::getSyncId)
                .list();
        for (ShopProdCategory shopProdCategory : pointList) {
            pointShopCateGory.add(shopProdCategory.getSyncId());
            categoryMap.put(shopProdCategory.getSyncId(), shopProdCategory.getId());
        }
        List<ShopProdCategory> tbShopCategories = categoryService.queryChain()
                .eq(ShopProdCategory::getShopId, sourceShopId)
                .eq(ShopProdCategory::getPid, 0)
                .list();
        List<Long> treeIds = new ArrayList<>();
        for (ShopProdCategory shopProdCategory : tbShopCategories) {
            treeIds.add(shopProdCategory.getId());
            if (CollUtil.isEmpty(pointShopCateGory) || !pointShopCateGory.contains(shopProdCategory.getId())) {
                ShopProdCategory tbShopCategoryNew = BeanUtil.copyProperties(shopProdCategory, ShopProdCategory.class);
                tbShopCategoryNew.setId(null);
                tbShopCategoryNew.setShopId(pointShopId);
                tbShopCategoryNew.setSyncId(shopProdCategory.getId());
                categoryService.save(tbShopCategoryNew);
                categoryMap.put(shopProdCategory.getId(), tbShopCategoryNew.getId());
            }
        }
        int childrenSize = 0;
        int syncSize = categoryMap.size();
        if (CollectionUtil.isNotEmpty(treeIds)) {
            List<ShopProdCategory> children = categoryService.queryChain()
                    .eq(ShopProdCategory::getShopId, sourceShopId)
                    .in(ShopProdCategory::getPid, treeIds)
                    .list();
            childrenSize = children.size();
            for (ShopProdCategory child : children) {
                if (CollUtil.isEmpty(pointShopCateGory) || !pointShopCateGory.contains(child.getId())) {
                    ShopProdCategory tbShopCategoryNew = BeanUtil.copyProperties(child, ShopProdCategory.class);
                    tbShopCategoryNew.setId(null);
                    tbShopCategoryNew.setShopId(pointShopId);
                    tbShopCategoryNew.setSyncId(child.getId());
                    tbShopCategoryNew.setPid(categoryMap.get(child.getPid()));
                    categoryService.save(tbShopCategoryNew);
                    categoryMap.put(child.getId(), tbShopCategoryNew.getId());
                }
            }
        }
        log.info("分类同步,源 父类分类{}个,同步{}个", tbShopCategories.size(), syncSize);
        log.info("分类同步,源 子类分类{}个,同步{}个", childrenSize, categoryMap.size() - syncSize);
        buildNotice(mainMapList, "分类同步", categoryMap.size(), null, null);
        return categoryMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncCategoryBySourceShop(Long sourceShopId, Long categoryId, Long sysUserId) {
        AssertUtil.isNull(categoryId, "{}不能为空", "分类ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ShopProdCategory mainEntity = categoryService.getById(categoryId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "分类");
        List<ShopProdCategory> list = categoryService.queryChain().eq(ShopProdCategory::getSyncId, categoryId).in(ShopProdCategory::getShopId, shopIds).list();
        Map<Long, ShopProdCategory> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ShopProdCategory::getShopId, t -> t));
        }
        Map<Long, Long> categoryMap = new HashMap<>();
        if (mainEntity.getPid() != null) {
            List<ShopProdCategory> categoryList = categoryService.queryChain().eq(ShopProdCategory::getSyncId, mainEntity.getPid()).in(ShopProdCategory::getShopId, shopIds).list();
            if (CollUtil.isNotEmpty(categoryList)) {
                categoryMap = categoryList.stream().collect(Collectors.toMap(ShopProdCategory::getShopId, ShopProdCategory::getId));
            }
        }
        Map<Long, ShopProdCategory> finalMap = map;
        for (Long shopId : shopIds) {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ShopProdCategory newEntity = BeanUtil.copyProperties(mainEntity, ShopProdCategory.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            newEntity.setPid(newEntity.getPid() != null ? categoryMap.get(shopId) : null);
            if (finalMap.containsKey(shopId)) {
                ShopProdCategory childEntity = finalMap.get(shopId);
                newEntity.setId(childEntity.getId());
                categoryService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                categoryService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getName());
            buildNotice(mainMapList, "分类变动", 1, "category", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
        }
    }

    public Map<Long, Long> syncProduct(Long sourceShopId, Long pointShopId, Map<Long, Long> unitMap,
                                       Map<Long, Long> specMap, Map<Long, Long> cateGoryMap, List<Map<String, Object>> mainMapList) {
        List<Long> pointProducts = productService.queryChain()
                .select(Product::getSyncId)
                .eq(Product::getShopId, pointShopId)
                .isNotNull(Product::getSyncId)
                .listAs(Long.class);
        Map<Long, Long> proMap = new HashMap<>();
        List<Product> products = productService.queryChain().eq(Product::getShopId, sourceShopId).list();
        if (CollectionUtil.isNotEmpty(products)) {
            for (Product tbProduct : products) {
                if (CollUtil.isEmpty(pointProducts) || !pointProducts.contains(tbProduct.getId())) {
                    Product tbProductNew = BeanUtil.copyProperties(tbProduct, Product.class);
                    tbProductNew.setId(null);
                    tbProductNew.setSyncId(tbProduct.getId());
                    tbProductNew.setShopId(pointShopId);
                    tbProductNew.setCategoryId(tbProduct.getCategoryId() != null ? cateGoryMap.get(tbProduct.getSpecId()) : null);
                    tbProductNew.setSpecId(tbProduct.getSpecId() != null ? specMap.get(tbProduct.getSpecId()) : null);
                    tbProductNew.setUnitId(tbProduct.getUnitId() != null ? unitMap.get(tbProduct.getUnitId()) : null);
                    tbProductNew.setStockNumber(0);
                    if (CollUtil.isNotEmpty(pointProducts)) {
                        tbProductNew.setIsSale(0);
                    }
                    productService.save(tbProductNew);
                    proMap.put(tbProduct.getId(), tbProductNew.getId());
                }
            }
        }
        log.info("商品同步,源{}个,已有{}个,同步{}个", products.size(), pointProducts.size(), proMap.size());
        buildNotice(mainMapList, "商品同步", proMap.size(), null, null);
        return proMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncProductBySourceShop(Long sourceShopId, Long productId, Long sysUserId) {
        AssertUtil.isNull(productId, "{}不能为空", "商品ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        Product mainEntity = productService.getById(productId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "商品");
        List<Product> list = productService.queryChain().eq(Product::getSyncId, productId).in(Product::getShopId, shopIds).list();
        Map<Long, Product> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(Product::getShopId, t -> t));
        }
        Map<Long, Long> specMap = new HashMap<>();
        if (mainEntity.getSpecId() != null) {
            List<ShopProdSpec> specList = specService.queryChain().eq(ShopProdSpec::getSyncId, mainEntity.getSpecId())
                    .in(ShopProdSpec::getShopId, shopIds).list();
            if (CollUtil.isNotEmpty(specList)) {
                specMap = specList.stream().collect(Collectors.toMap(ShopProdSpec::getShopId, ShopProdSpec::getId));
            }
        }
        Map<Long, Long> unitMap = new HashMap<>();
        if (mainEntity.getUnitId() != null) {
            List<ShopProdUnit> unitList = unitService.queryChain().eq(ShopProdUnit::getSyncId, mainEntity.getUnitId())
                    .in(ShopProdUnit::getShopId, shopIds).list();
            if (CollUtil.isNotEmpty(unitList)) {
                unitMap = unitList.stream().collect(Collectors.toMap(ShopProdUnit::getShopId, ShopProdUnit::getId));
            }
        }
        Map<Long, Long> categoryMap = new HashMap<>();
        if (mainEntity.getCategoryId() != null) {
            List<ShopProdCategory> categoryList = categoryService.queryChain().eq(ShopProdCategory::getSyncId, mainEntity.getCategoryId()).in(ShopProdCategory::getShopId, shopIds).list();
            if (CollUtil.isNotEmpty(categoryList)) {
                categoryMap = categoryList.stream().collect(Collectors.toMap(ShopProdCategory::getShopId, ShopProdCategory::getId));
            }
        }
        Map<Long, Product> finalMap = map;
        for (Long shopId : shopIds) {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            Product newEntity = BeanUtil.copyProperties(mainEntity, Product.class);
            newEntity.setSyncId(mainEntity.getSyncId());
            newEntity.setShopId(shopId);
            newEntity.setCategoryId(newEntity.getCategoryId() != null ? categoryMap.get(shopId) : null);
            newEntity.setSpecId(newEntity.getSpecId() != null ? specMap.get(shopId) : null);
            newEntity.setUnitId(newEntity.getUnitId() != null ? unitMap.get(shopId) : null);
            if (finalMap.containsKey(shopId)) {
                Product childEntity = finalMap.get(shopId);
                newEntity.setId(childEntity.getId());
                productService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                newEntity.setIsSale(0);
                newEntity.setStockNumber(0);
                productService.save(newEntity);
            }
            syncSkuBySourceShop(sourceShopId, shopId, productId, newEntity.getId());
            if ("package".equals(newEntity.getType()) || "coupon".equals(newEntity.getType())) {
                syncProductPackageBySourceShop(newEntity, shopId);
            }
            dataMap.put(newEntity.getId(), newEntity.getName());
            buildNotice(mainMapList, "商品变动", 1, "product", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
        }
    }

    public Map<Long, Long> syncGroup(Long sourceShopId, Long pointShopId, Map<Long, Long> pros, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> groupMap = new HashMap<>();
        List<Long> pointGroup = groupService.queryChain().select(ProdGroup::getSyncId).eq(ProdGroup::getShopId, pointShopId)
                .isNotNull(ProdGroup::getSyncId).listAs(Long.class);
        List<ProdGroup> list = groupService.queryChain().eq(ProdGroup::getShopId, sourceShopId).list();
        if (CollUtil.isEmpty(list)) {
            log.info("分组同步,分组数据为空");
            return new HashMap<>();

        }
        for (ProdGroup prodGroup : list) {
            if (CollUtil.isEmpty(pointGroup) || !pointGroup.contains(prodGroup.getId())) {
                ProdGroup tbProductGroupNew = BeanUtil.copyProperties(prodGroup, ProdGroup.class);
                tbProductGroupNew.setId(null);
                tbProductGroupNew.setSyncId(prodGroup.getId());
                tbProductGroupNew.setShopId(pointShopId);
                groupService.save(tbProductGroupNew);
                groupMap.put(prodGroup.getId(), tbProductGroupNew.getId());
            }
        }
        List<ProdGroupRelation> groupRelations = prodGroupRelationService.queryChain().in(ProdGroupRelation::getProdGroupId, groupMap.keySet()).list();
        for (ProdGroupRelation prodGroupRelation : groupRelations) {
            prodGroupRelation.setProdGroupId(groupMap.get(prodGroupRelation.getProdGroupId()));
            prodGroupRelation.setProductId(pros.get(prodGroupRelation.getProductId()));
        }
        prodGroupRelationService.saveOrUpdateBatch(groupRelations, 100);
        log.info("分组同步,源{}个,已有{}个,同步{}个", list.size(), pointGroup.size(), groupMap.size());
        buildNotice(mainMapList, "分组同步", groupMap.size(), null, null);
        return groupMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncGroupBySourceShop(Long sourceShopId, Long groupId, Long sysUserId) {
        AssertUtil.isNull(groupId, "{}不能为空", "分组ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ProdGroup mainEntity = groupService.getById(groupId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "分组");
        List<ProdGroup> list = groupService.queryChain().eq(ProdGroup::getSyncId, groupId).in(ProdGroup::getShopId, shopIds).list();
        Map<Long, ProdGroup> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ProdGroup::getShopId, t -> t));
        }
        List<ProdGroupRelation> groupRelations = prodGroupRelationService.queryChain().eq(ProdGroupRelation::getProdGroupId, mainEntity.getId()).list();
        Map<Long, Integer> groupRelationMap = new HashMap<>();
        if (CollUtil.isNotEmpty(groupRelations)) {
            groupRelationMap = groupRelations.stream().collect(Collectors.toMap(ProdGroupRelation::getProductId, ProdGroupRelation::getSort));
        }
        List<ProdGroupRelation> groupRelationList = new ArrayList<>();
        for (Long shopId : shopIds) {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ProdGroup newEntity = BeanUtil.copyProperties(mainEntity, ProdGroup.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            if (map.containsKey(shopId)) {
                ProdGroup childEntity = map.get(shopId);
                newEntity.setId(childEntity.getId());
                groupService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                groupService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getName());
            buildNotice(mainMapList, "分组变动", 1, "group", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
            if (CollUtil.isEmpty(groupRelationMap)) {
                List<ProdGroupRelation> oldProdGroup = prodGroupRelationService.queryChain().eq(ProdGroupRelation::getProdGroupId, newEntity.getId()).list();
                if (CollUtil.isEmpty(oldProdGroup)) {
                    continue;
                }
                Set<Long> oldProds = oldProdGroup.stream().map(ProdGroupRelation::getProductId).collect(Collectors.toSet());
                List<Long> rmIds = productService.queryChain().select(Product::getId).eq(Product::getShopId, shopId)
                        .in(Product::getId, oldProds).isNotNull(Product::getSyncId).listAs(Long.class);
                if (CollUtil.isEmpty(rmIds)) {
                    continue;
                }
                QueryWrapper queryWrapper = new QueryWrapper();
                queryWrapper.eq(ProdGroupRelation::getProdGroupId, newEntity.getId());
                queryWrapper.in(ProdGroupRelation::getProductId, rmIds);
                prodGroupRelationService.remove(queryWrapper);
            } else {
                List<Product> prods = productService.queryChain().select(Product::getId).eq(Product::getShopId, shopId).in(Product::getSyncId, groupRelationMap.keySet()).list();
                for (Product prod : prods) {
                    ProdGroupRelation newProdGroupRelation = new ProdGroupRelation();
                    newProdGroupRelation.setProdGroupId(newEntity.getId());
                    newProdGroupRelation.setProductId(prod.getId());
                    newProdGroupRelation.setSort(groupRelationMap.get(prod.getSyncId()));
                    groupRelationList.add(newProdGroupRelation);
                }
            }
        }
        if (CollUtil.isNotEmpty(groupRelations)) {
            prodGroupRelationService.saveOrUpdateBatch(groupRelationList, 100);
        }
    }

    //套餐
    public void syncProductPackage(Long shopId, Map<Long, Long> pros, Map<Long, Long> skus) {
        List<Product> list = productService.queryChain()
                .eq(Product::getShopId, shopId)
                .and(column(Product::getType).eq("package").or(column(Product::getType).eq("coupon")))
                .list();
        for (Product product : list) {
            if (StrUtil.isNotBlank(product.getGroupSnap()) && product.getGroupSnap().length() > 5) {
                ProductGroupVo proGroupVo = JSONObject.parseObject(product.getGroupSnap(), ProductGroupVo.class);
                List<ProductGroupVo.Food> goodList = new ArrayList<>();
                for (ProductGroupVo.Food goods : proGroupVo.getGoods()) {
                    if (pros.containsKey(goods.getProId())) {
                        if (goods.getSkuId() != null && skus.containsKey(goods.getSkuId())) {
                            goods.setProId(pros.get(goods.getProId()));
                            goods.setSkuId(skus.get(goods.getSkuId()));
                        } else {
                            goods.setProId(pros.get(goods.getProId()));
                        }
                        goodList.add(goods);
                    }
                }
                proGroupVo.setCount(goodList.size());
                proGroupVo.setGoods(goodList);
                if (proGroupVo.getNumber() != null && proGroupVo.getNumber() > 0) {
                    if (proGroupVo.getNumber() > proGroupVo.getCount()) {
                        proGroupVo.setNumber(proGroupVo.getCount());
                    }
                }
                product.setGroupSnap(JSON.toJSONString(proGroupVo, JSONWriter.Feature.WriteMapNullValue));
            }
        }
        productService.saveOrUpdateBatch(list, 100);
    }

    public void syncProductPackageBySourceShop(Product product, Long pointShopId) {
        if (StrUtil.isNotBlank(product.getGroupSnap()) && product.getGroupSnap().length() > 5) {
            ProductGroupVo proGroupVo = JSONObject.parseObject(product.getGroupSnap(), ProductGroupVo.class);
            List<ProductGroupVo.Food> goodList = new ArrayList<>();
            for (ProductGroupVo.Food goods : proGroupVo.getGoods()) {
                if (goods.getProId() != null) {
                    Product sourceProduct = productService.queryChain().eq(Product::getShopId, pointShopId).eq(Product::getSyncId, goods.getProId()).one();
                    if (sourceProduct != null) {
                        goods.setProId(sourceProduct.getId());
                        goods.setProName(sourceProduct.getName());
                    }
                }
                if (goods.getSkuId() != null) {
                    ProdSku sourceSku = skuService.queryChain().eq(ProdSku::getShopId, pointShopId).eq(ProdSku::getSyncId, goods.getSkuId()).one();
                    goods.setSkuId(sourceSku.getId());
                    goods.setSkuName(sourceSku.getSpecInfo());
                }
                goodList.add(goods);
            }
            proGroupVo.setCount(goodList.size());
            proGroupVo.setGoods(goodList);
            if (proGroupVo.getNumber() != null && proGroupVo.getNumber() > 0) {
                if (proGroupVo.getNumber() > proGroupVo.getCount()) {
                    proGroupVo.setNumber(proGroupVo.getCount());
                }
            }
            product.setGroupSnap(JSON.toJSONString(proGroupVo, JSONWriter.Feature.WriteMapNullValue));
            productService.saveOrUpdate(product);
        }
    }

    //分组
    public Map<Long, Long> syncSku(Long sourceShopId, Long pointShopId, Map<Long, Long> prods) {
        Map<Long, Long> skuMap = new HashMap<>();
        List<Long> pointSkus = skuService.queryChain().select(ProdSku::getSyncId)
                .eq(ProdSku::getShopId, pointShopId)
                .isNotNull(ProdSku::getSyncId)
                .listAs(Long.class);
        List<ProdSku> list = skuService.queryChain().eq(ProdSku::getShopId, sourceShopId).list();
        for (ProdSku prodSku : list) {
            if (prods.containsKey(prodSku.getProductId()) && (CollUtil.isEmpty(pointSkus) || !pointSkus.contains(prodSku.getId()))) {
                ProdSku newSku = BeanUtil.copyProperties(prodSku, ProdSku.class);
                newSku.setId(null);
                newSku.setShopId(pointShopId);
                newSku.setProductId(prods.get(prodSku.getProductId()));
                newSku.setSyncId(prodSku.getId());
                skuService.save(newSku);
                skuMap.put(prodSku.getId(), newSku.getId());
            }
        }
        log.info("商品SKU同步,源{}个,已有{}个,同步{}个", list.size(), pointSkus.size(), skuMap.size());
        return skuMap;
    }

    public void syncSkuBySourceShop(Long sourceShopId, Long pointShopId, Long sourceProductId, Long pointProductId) {
        List<ProdSku> sourceSkus = skuService.queryChain()
                .eq(ProdSku::getShopId, sourceShopId)
                .eq(ProdSku::getProductId, sourceProductId)
                .list();
        if (CollUtil.isEmpty(sourceSkus)) {
            return;
        }
        List<ProdSku> newSaveSkus = new ArrayList<>();
        Set<Long> sourceIds = sourceSkus.stream().map(ProdSku::getId).collect(Collectors.toSet());

        List<ProdSku> childSkus = skuService.queryChain().eq(ProdSku::getShopId, pointShopId).in(ProdSku::getSyncId, sourceIds).list();
        Map<Long, ProdSku> childSkuMap = childSkus.stream().collect(Collectors.toMap(ProdSku::getSyncId, t -> t));
        for (ProdSku skus : sourceSkus) {
            ProdSku newSku = BeanUtil.copyProperties(skus, ProdSku.class);
            newSku.setSyncId(skus.getId());
            newSku.setShopId(pointShopId);
            newSku.setProductId(pointProductId);
            if (childSkuMap.containsKey(skus.getId())) {
                ProdSku childSku = childSkuMap.get(skus.getId());
                newSku.setId(childSku.getId());
            } else {
                newSku.setId(null);
            }
            newSaveSkus.add(newSku);
        }
        skuService.saveOrUpdateBatch(newSaveSkus, 100);
    }

    public Map<Long, Long> syncConsGroup(Long sourceShopId, Long pointShopId, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> consGroupMap = new HashMap<>();
        List<Long> pointConsGroup = new ArrayList<>();
        List<ConsGroup> consGroupList = consGroupService.queryChain()
                .eq(ConsGroup::getShopId, pointShopId)
                .isNotNull(ConsGroup::getSyncId)
                .list();
        for (ConsGroup consGroup : consGroupList) {
            pointConsGroup.add(consGroup.getSyncId());
            consGroupMap.put(consGroup.getSyncId(), consGroup.getId());
        }
        List<ConsGroup> list = consGroupService.queryChain().eq(ConsGroup::getShopId, sourceShopId).list();
        for (ConsGroup consGroup : list) {
            if (CollUtil.isEmpty(pointConsGroup) || !pointConsGroup.contains(consGroup.getId())) {
                ConsGroup newConsGroup = BeanUtil.copyProperties(consGroup, ConsGroup.class);
                newConsGroup.setId(null);
                newConsGroup.setSyncId(consGroup.getId());
                newConsGroup.setShopId(pointShopId);
                consGroupService.save(newConsGroup);
                consGroupMap.put(consGroup.getId(), newConsGroup.getId());
            }
        }
        log.info("耗材分组同步,源{}个,已有{}个,同步{}个", list.size(), pointConsGroup.size(), consGroupMap.size() - pointConsGroup.size());
        buildNotice(mainMapList, "耗材分组同步", consGroupMap.size() - pointConsGroup.size(), null, null);
        return consGroupMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncConsGroupBySourceShop(Long sourceShopId, Long consGroupId, Long sysUserId) {
        AssertUtil.isNull(consGroupId, "{}不能为空", "耗材分组ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 2);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ConsGroup mainEntity = consGroupService.getById(consGroupId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "耗材分组");
        List<ConsGroup> list = consGroupService.queryChain().eq(ConsGroup::getSyncId, consGroupId).in(ConsGroup::getShopId, shopIds).list();
        Map<Long, ConsGroup> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ConsGroup::getShopId, t -> t));
        }
        Map<Long, ConsGroup> finalMap = map;
        shopIds.forEach(shopId -> {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ConsGroup newEntity = BeanUtil.copyProperties(mainEntity, ConsGroup.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            if (finalMap.containsKey(shopId)) {
                ConsGroup childEntity = finalMap.get(shopId);
                newEntity.setId(childEntity.getId());
                consGroupService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                consGroupService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getName());
            buildNotice(mainMapList, "耗材分组变动", 1, "consGroup", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
        });
    }

    // 耗材
    public Map<Long, Long> syncConsInfo(Long sourceShopId, Long pointShopId, Map<Long, Long> consGroupMap, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> consMap = new HashMap<>();
        List<Long> pointConsInfo = consInfoService.queryChain().select(ConsInfo::getSyncId)
                .eq(ConsInfo::getShopId, pointShopId)
                .isNotNull(ConsInfo::getSyncId)
                .listAs(Long.class);
        List<ConsInfo> list = consInfoService.queryChain().eq(ConsInfo::getShopId, sourceShopId).list();
        for (ConsInfo cons : list) {
            if (CollUtil.isEmpty(pointConsInfo) || !pointConsInfo.contains(cons.getId())) {
                ConsInfo conInfo = BeanUtil.copyProperties(cons, ConsInfo.class);
                conInfo.setId(null);
                conInfo.setShopId(pointShopId);
                conInfo.setSyncId(cons.getId());
                conInfo.setConsGroupId(consGroupMap.get(conInfo.getConsGroupId()));
                conInfo.setStockNumber(BigDecimal.ZERO);
                consInfoService.save(conInfo);
                consMap.put(cons.getId(), conInfo.getId());
            }
        }
        log.info("耗材同步,源{}个,已有{}个,同步{}个", list.size(), pointConsInfo.size(), consMap.size());
        buildNotice(mainMapList, "耗材信息同步", consMap.size(), null, null);
        return consMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncConsInfoBySourceShop(Long sourceShopId, Long consInfoId, Long sysUserId) {
        AssertUtil.isNull(consInfoId, "{}不能为空", "耗材ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 2);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ConsInfo mainEntity = consInfoService.getById(consInfoId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "耗材");
        List<ConsInfo> list = consInfoService.queryChain().eq(ConsInfo::getSyncId, consInfoId).in(ConsInfo::getShopId, shopIds).list();
        Map<Long, ConsInfo> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ConsInfo::getShopId, t -> t));
        }
        Map<Long, ConsInfo> finalMap = map;
        for (Long shopId : shopIds) {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ConsInfo newEntity = BeanUtil.copyProperties(mainEntity, ConsInfo.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            if (newEntity.getConsGroupId() != null) {
                Long consGroupId = consGroupService.queryChain().select(ConsGroup::getId)
                        .eq(ConsGroup::getSyncId, newEntity.getConsGroupId()).eq(ConsGroup::getShopId, shopId).oneAs(Long.class);
                newEntity.setConsGroupId(consGroupId);
            }
            if (finalMap.containsKey(shopId)) {
                ConsInfo childEntity = finalMap.get(shopId);
                newEntity.setId(childEntity.getId());
                consInfoService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                newEntity.setStockNumber(BigDecimal.ZERO);
                consInfoService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getConName());
            buildNotice(mainMapList, "耗材信息变动", 1, "consInfo", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
        }
    }

    public void syncConsPro(Long sourceShopId, Long pointShopId, Map<Long, Long> consMap, Map<Long, Long> proMap) {
        List<ProdConsRelation> list = prodConsRelationService.queryChain().eq(ProdConsRelation::getShopId, sourceShopId).list();
        for (ProdConsRelation prodConsRelation : list) {
            prodConsRelation.setShopId(pointShopId);
            prodConsRelation.setProductId(proMap.get(prodConsRelation.getProductId()));
            prodConsRelation.setConsInfoId(consMap.get(prodConsRelation.getConsInfoId()));
        }
        prodConsRelationService.saveOrUpdateBatch(list, 100);
        log.info("耗材与商品关联关系,同步{}个", list.size());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncConsProBySourceShop(Long sourceShopId, Long sourceProdId, Long sysUserId) {
        AssertUtil.isNull(sourceProdId, "{}不能为空", "商品ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 2);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        List<ProdConsRelation> newSaveList = new ArrayList<>();
        List<ProdConsRelation> list = prodConsRelationService.queryChain()
                .eq(ProdConsRelation::getShopId, sourceShopId).eq(ProdConsRelation::getProductId, sourceProdId).list();
        for (Long shopId : shopIds) {
            Product product = productService.queryChain().eq(Product::getSyncId, sourceProdId).eq(Product::getShopId, shopId).one();
            if (product != null) {
                if (CollUtil.isEmpty(list)) {
                    List<Long> prods = prodConsRelationService.queryChain().select(ProdConsRelation::getProductId)
                            .eq(ProdConsRelation::getShopId, shopId).eq(ProdConsRelation::getProductId, product.getId()).listAs(Long.class);
                    if (CollUtil.isNotEmpty(prods)) {
                        List<Long> remIds = productService.queryChain().select(Product::getId).in(Product::getId, prods).isNotNull(Product::getSyncId).listAs(Long.class);
                        if (CollUtil.isNotEmpty(remIds)) {
                            prodConsRelationService.remove(new QueryWrapper().eq(ProdConsRelation::getShopId, shopId).in(ProdConsRelation::getProductId, remIds));
                        }
                    }
                } else {
                    Map<Long, BigDecimal> map = list.stream().collect(Collectors.toMap(ProdConsRelation::getConsInfoId, ProdConsRelation::getSurplusStock));
                    List<ConsInfo> newCons = consInfoService.queryChain().select(ConsInfo::getId)
                            .in(ConsInfo::getSyncId, map.keySet()).eq(ConsInfo::getShopId, shopId).list();
                    for (ConsInfo newCon : newCons) {
                        ProdConsRelation prodConsRelation = new ProdConsRelation();
                        prodConsRelation.setShopId(shopId);
                        prodConsRelation.setProductId(product.getId());
                        prodConsRelation.setConsInfoId(newCon.getId());
                        prodConsRelation.setSurplusStock(map.get(newCon.getSyncId()));
                        newSaveList.add(prodConsRelation);
                    }
                }
            }
        }
        if (CollUtil.isNotEmpty(newSaveList)) {
            prodConsRelationService.saveOrUpdateBatch(newSaveList, 100);
        }
    }
}
