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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONWriter;
import com.czg.config.RabbitPublisher;
import com.czg.constant.CacheConstant;
import com.czg.constants.SystemConstants;
import com.czg.excel.ExcelExportUtil;
import com.czg.excel.SheetData;
import com.czg.exception.CzgException;
import com.czg.product.dto.*;
import com.czg.product.entity.*;
import com.czg.product.enums.*;
import com.czg.product.param.*;
import com.czg.product.service.*;
import com.czg.product.vo.ProductGroupVo;
import com.czg.product.vo.ProductStatisticsVo;
import com.czg.product.vo.ProductStockVO;
import com.czg.sa.StpKit;
import com.czg.service.RedisService;
import com.czg.service.product.mapper.*;
import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.TextStyle;
import java.util.*;

import static com.czg.constant.CacheConstant.ADMIN_CLIENT_PRODUCT_LIST;
import static com.czg.product.entity.table.ProductTableDef.PRODUCT;
import static com.czg.product.entity.table.ShopProdCategoryTableDef.SHOP_PROD_CATEGORY;
import static com.czg.product.entity.table.ShopProdSpecTableDef.SHOP_PROD_SPEC;
import static com.czg.product.entity.table.ShopProdUnitTableDef.SHOP_PROD_UNIT;

/**
 * 商品
 *
 * @author Tankaikai tankaikai@aliyun.com
 * @since 1.0 2025-02-16
 */
@Slf4j
@AllArgsConstructor
@DubboService
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

    private final ProdSkuMapper prodSkuMapper;
    private final ProdConsRelationService prodConsRelationService;
    private final ConsInfoMapper consInfoMapper;
    private final ProductStockFlowMapper productStockFlowMapper;
    private final ConsStockFlowService consStockFlowService;
    private final SensitiveOperationService sensitiveOperationService;
    private final ProdGroupRelationMapper prodGroupRelationMapper;
    private final ProductStockFlowService productStockFlowService;
    private final RedisService redisService;
    @Resource
    private RabbitPublisher rabbitPublisher;

    private QueryWrapper buildQueryWrapper(ProductDTO param) {
        QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
        if (ObjUtil.isNotNull(param.getId())) {
            queryWrapper.eq(Product::getId, param.getId());
        }
        if (StrUtil.isNotEmpty(param.getName())) {
            queryWrapper.like(Product::getName, param.getName());
        }
        if (ObjUtil.isNotNull(param.getCategoryId())) {
            queryWrapper.eq(Product::getCategoryId, param.getCategoryId());
        }
        if (ObjUtil.isNotNull(param.getSpecId())) {
            queryWrapper.eq(Product::getSpecId, param.getSpecId());
        }
        if (StrUtil.isNotEmpty(param.getType())) {
            queryWrapper.eq(Product::getType, param.getType());
        }
        if (StrUtil.isNotEmpty(param.getCreateBeginTime())) {
            queryWrapper.ge(Product::getCreateTime, param.getCreateBeginTime());
        }
        if (StrUtil.isNotEmpty(param.getCreateEndTime())) {
            queryWrapper.le(Product::getCreateTime, param.getCreateEndTime());
        }
        if (StrUtil.isNotEmpty(param.getStatus())) {
            if (ProductSaleStatusEnum.SOLD_OUT.getValue().equals(param.getStatus())) {
                queryWrapper.eq(Product::getIsSoldStock, SystemConstants.OneZero.ONE);
            } else if (ProductSaleStatusEnum.ON_SALE.getValue().equals(param.getStatus())) {
                queryWrapper.eq(Product::getIsSoldStock, SystemConstants.OneZero.ZERO);
                queryWrapper.eq(Product::getIsSale, SystemConstants.OneZero.ONE);
            } else if (ProductSaleStatusEnum.OFF_SALE.getValue().equals(param.getStatus())) {
                queryWrapper.eq(Product::getIsSale, SystemConstants.OneZero.ZERO);
            }
        }
        if (param.getShopId() == null) {
            Long shopId = StpKit.USER.getShopId(0L);
            param.setShopId(shopId);
        }
        queryWrapper.eq(Product::getShopId, param.getShopId());
        queryWrapper.eq(Product::getIsDel, SystemConstants.OneZero.ZERO);
        queryWrapper.orderBy(Product::getIsSoldStock, true);
        queryWrapper.orderBy(Product::getIsSale, false);
        queryWrapper.orderBy(Product::getSort, false);
        queryWrapper.orderBy(Product::getId, false);
        return queryWrapper;
    }

    private QueryWrapper buildFullQueryWrapper(ProductDTO param) {
        QueryWrapper queryWrapper = buildQueryWrapper(param);
        queryWrapper.select(PRODUCT.DEFAULT_COLUMNS)
                .select(SHOP_PROD_UNIT.NAME.as(ProductDTO::getUnitName))
                .select(SHOP_PROD_CATEGORY.NAME.as(ProductDTO::getCategoryName))
                .select(SHOP_PROD_SPEC.NAME.as(ProductDTO::getSpecName), SHOP_PROD_SPEC.FULL_NAME.as(ProductDTO::getSpecFullName))
                .from(PRODUCT)
                .leftJoin(SHOP_PROD_UNIT).on(SHOP_PROD_UNIT.ID.eq(PRODUCT.UNIT_ID))
                .leftJoin(SHOP_PROD_CATEGORY).on(SHOP_PROD_CATEGORY.ID.eq(PRODUCT.CATEGORY_ID))
                .leftJoin(SHOP_PROD_SPEC).on(SHOP_PROD_SPEC.ID.eq(PRODUCT.SPEC_ID));
        return queryWrapper;
    }

    private void buildProductExtInfo(List<ProductDTO> records) {
        records.forEach(record -> {
            record.setIsSaleTime(calcIsSaleTime(record.getDays(), record.getStartTime(), record.getEndTime()));
            record.setProGroupVo(JSONArray.parseArray(record.getGroupSnap().toString(), ProductGroupVo.class));
            List<ProdSkuDTO> skuList = prodSkuMapper.selectListByQueryAs(query().eq(ProdSku::getProductId, record.getId()).eq(ProdSku::getIsDel, SystemConstants.OneZero.ZERO), ProdSkuDTO.class);
            if (CollUtil.isNotEmpty(skuList)) {
                Optional<BigDecimal> lowPriceIsPresent = skuList.stream().map(obj -> NumberUtil.nullToZero(obj.getSalePrice())).min(BigDecimal::compareTo);
                lowPriceIsPresent.ifPresent(record::setLowPrice);

                Optional<BigDecimal> lowMemberPriceIsPresent = skuList.stream().map(obj -> NumberUtil.nullToZero(obj.getMemberPrice())).min(BigDecimal::compareTo);
                lowMemberPriceIsPresent.ifPresent(record::setLowMemberPrice);
            }
            record.setSkuList(skuList);
            List<ProdConsRelationDTO> consList = prodConsRelationService.selectListByProdId(record.getId());
            record.setConsList(consList);
            if (CollUtil.isNotEmpty(consList)) {
                List<Long> consIds = consList.stream().map(ProdConsRelationDTO::getConsInfoId).distinct().toList();
                String consName = consInfoMapper.selectOneByQueryAs(query().select("GROUP_CONCAT(con_name SEPARATOR '、')").in(ConsInfo::getId, consIds), String.class);
                record.setConsName(consName);
            }
        });
    }

    @Override
    public Page<ProductDTO> getProductPage(ProductDTO param) {
        QueryWrapper queryWrapper = buildFullQueryWrapper(param);
        Page<ProductDTO> page = super.pageAs(PageUtil.buildPage(), queryWrapper, ProductDTO.class);
        List<ProductDTO> records = page.getRecords();
        buildProductExtInfo(records);
        return page;
    }

    @Override
    public List<ProductDTO> getProductList(ProductDTO param) {
        QueryWrapper queryWrapper = buildFullQueryWrapper(param);
        List<ProductDTO> records = super.listAs(queryWrapper, ProductDTO.class);
        records.forEach(record -> {
            record.setProGroupVo(JSONArray.parseArray(record.getGroupSnap().toString(), ProductGroupVo.class));
            List<ProdSkuDTO> skuList = prodSkuMapper.selectListByQueryAs(query().eq(ProdSku::getProductId, record.getId()).eq(ProdSku::getIsDel, SystemConstants.OneZero.ZERO), ProdSkuDTO.class);
            if (CollUtil.isNotEmpty(skuList)) {
                Optional<BigDecimal> lowPriceIsPresent = skuList.stream().map(obj -> NumberUtil.nullToZero(obj.getSalePrice())).min(BigDecimal::compareTo);
                lowPriceIsPresent.ifPresent(record::setLowPrice);

                Optional<BigDecimal> lowMemberPriceIsPresent = skuList.stream().map(obj -> NumberUtil.nullToZero(obj.getMemberPrice())).min(BigDecimal::compareTo);
                lowMemberPriceIsPresent.ifPresent(record::setLowMemberPrice);
            }
            record.setSkuList(skuList);
            record.setConsList(prodConsRelationService.listAs(query()
                    .eq(ProdConsRelation::getProductId, record.getId())
                    .eq(ProdConsRelation::getShopId, record.getShopId()), ProdConsRelationDTO.class));
        });
        return records;
    }

    @Override
    public void exportProductList(ProductDTO param, HttpServletResponse response) {
        // 1. 查询并构建完整数据
        QueryWrapper queryWrapper = buildFullQueryWrapper(param);
        List<ProductDTO> records = super.listAs(queryWrapper, ProductDTO.class);
        buildProductExtInfo(records);

        // 2. 分别处理普通商品和套餐商品
        SheetData normalSheet = buildNormalProductSheet(records);
        SheetData packageSheet = buildPackageProductSheet(records);

        // 3. 导出
        List<SheetData> dataList = List.of(normalSheet, packageSheet);
        ExcelExportUtil.exportMultipleSheetsToResponse(dataList, "商品列表", response);
    }

    // -----------------------------
    // 普通商品处理
    // -----------------------------
    private SheetData buildNormalProductSheet(List<ProductDTO> records) {
        List<SheetWriteHandler> handlers = new ArrayList<>();
        List<ProductExportDTO> dataList = new ArrayList<>();

        for (ProductDTO dto : records) {
            if ("package".equals(dto.getType())) continue; // 跳过套餐

            int firstRow = dataList.size() + 1;

            if (dto.getSkuList() != null && !dto.getSkuList().isEmpty()) {
                for (ProdSkuDTO sku : dto.getSkuList()) {
                    ProductExportDTO exportDto = new ProductExportDTO();
                    BeanUtil.copyProperties(dto, exportDto);
                    exportDto.setSpecFullName(sku.getSpecInfo());
                    exportDto.setPrice(sku.getSalePrice());
                    exportDto.setMemberPrice(sku.getMemberPrice());
                    exportDto.setIsSale(sku.getIsGrounding());
                    exportDto.setBarCode(sku.getBarCode());
                    exportDto.setCostPrice(sku.getCostPrice());
                    dataList.add(exportDto);
                }

                int skuCount = dto.getSkuList().size();
                if (skuCount > 1) {
                    mergeColumns(handlers, firstRow, firstRow + skuCount - 1,
                            0, 1, 7, 8, 9, 10, 11); // 多列合并
                }
            } else {
                dataList.add(BeanUtil.copyProperties(dto, ProductExportDTO.class));
            }
        }

        return new SheetData()
                .setSheetName("普通商品")
                .setData(dataList)
                .setClazz(ProductExportDTO.class)
                .setHandlers(handlers);
    }

    // -----------------------------
    // 套餐商品处理
    // -----------------------------
    private SheetData buildPackageProductSheet(List<ProductDTO> records) {
        List<SheetWriteHandler> handlers = new ArrayList<>();
        List<ProductPackageExportDTO> dataList = new ArrayList<>();

        for (ProductDTO exportDTO : records) {
            if (!"package".equals(exportDTO.getType())) continue;

            if (exportDTO.getProGroupVo() == null || exportDTO.getProGroupVo().isEmpty()) {
                dataList.add(BeanUtil.copyProperties(exportDTO, ProductPackageExportDTO.class));
                continue;
            }

            int sheetFirstRow = dataList.size() + 1;
            boolean needOuterMerge = exportDTO.getProGroupVo().size() > 1;

            for (ProductGroupVo proGroupDTO : exportDTO.getProGroupVo()) {
                int groupFirstRow = dataList.size() + 1;
                List<ProductGroupVo.Food> goods = proGroupDTO.getGoods();
                int groupSize = goods.size();

                // 添加每条商品记录
                for (ProductGroupVo.Food good : goods) {
                    ProductPackageExportDTO pkgDto = new ProductPackageExportDTO()
                            .setName(exportDTO.getName())
                            .setCategoryName(exportDTO.getCategoryName())
                            .setUnitName(exportDTO.getUnitName())
                            .setPrice(getMainSkuPrice(exportDTO))
                            .setMemberPrice(getMainSkuMemberPrice(exportDTO))
                            .setType(exportDTO.getType())
                            .setGroupType(exportDTO.getGroupType())
                            .setIsSale(getMainSkuIsSale(exportDTO))
                            .setGroupTitleName(proGroupDTO.getTitle())
                            .setGroupProductNumber(Optional.ofNullable(proGroupDTO.getNumber()).map(String::valueOf).orElse(""))
                            .setGroupProductName(good.getProName() + "-" + good.getSkuName());
                    dataList.add(pkgDto);
                }

                // 组内合并：如果该组有多个商品
                if (groupSize > 1) {
                    needOuterMerge = true;
                    mergeColumns(handlers, groupFirstRow, groupFirstRow + groupSize - 1, 5, 8);
                }
            }

            // 外层合并：整个套餐的信息（名称、分类等）
            if (needOuterMerge) {
                int lastRow = dataList.size();
                mergeColumns(handlers, sheetFirstRow, lastRow, 0, 1, 2, 3, 4);
            }
        }

        return new SheetData()
                .setSheetName("套餐商品")
                .setData(dataList)
                .setClazz(ProductPackageExportDTO.class)
                .setHandlers(handlers);
    }

    // -----------------------------
    // 辅助方法：提取主 SKU 信息（避免重复 getFirst()）
    // -----------------------------
    private BigDecimal getMainSkuPrice(ProductDTO dto) {
        return dto.getSkuList().isEmpty() ? null : dto.getSkuList().getFirst().getSalePrice();
    }

    private BigDecimal getMainSkuMemberPrice(ProductDTO dto) {
        return dto.getSkuList().isEmpty() ? null : dto.getSkuList().getFirst().getMemberPrice();
    }

    private Integer getMainSkuIsSale(ProductDTO dto) {
        return dto.getSkuList().isEmpty() ? null : dto.getSkuList().getFirst().getIsSale();
    }

    // -----------------------------
    // 合并工具方法：支持多列合并
    // -----------------------------
    private void mergeColumns(List<SheetWriteHandler> handlers, int firstRow, int lastRow, int... columns) {
        for (int col : columns) {
            addMergeHandler(handlers, firstRow, lastRow, col, col);
        }
    }

    private void addMergeHandler(List<SheetWriteHandler> handlers, int firstRow, int lastRow, int firstCol, int lastCol) {
        handlers.add(ExcelExportUtil.createMergeStrategy(firstRow, lastRow, firstCol, lastCol));
    }

    @Override
    public List<ProductDTO> getProductCacheList(Long shopId, Long categoryId) {
        String key = ADMIN_CLIENT_PRODUCT_LIST + "::" + shopId;
        List<ProductDTO> list;
        if (categoryId == null) {
            if (!redisService.hasKey(key)) {
                ProductDTO param = new ProductDTO();
                param.setShopId(shopId);
                list = getProductList(param);
                redisService.setJsonStr(key, list);
            } else {
                list = redisService.getJsonToBeanList(key, ProductDTO.class);
            }
        } else {
            key = key + "::" + categoryId;
            if (!redisService.hasKey(key)) {
                list = initProductCacheByCategoryId(shopId, categoryId);
            } else {
                list = redisService.getJsonToBeanList(key, ProductDTO.class);
            }
            // 重新进行排序
            list = list.stream()
                    .sorted(Comparator.comparingInt(ProductDTO::getIsSoldStock))
                    .sorted(Comparator.comparingInt(ProductDTO::getIsSale).reversed())
                    .sorted(Comparator.comparingInt(ProductDTO::getSort).reversed())
                    .sorted(Comparator.comparingLong(ProductDTO::getId).reversed())
                    .toList();
        }
        return list;
    }

    /**
     * 初始化商品缓存数据
     */
    private List<ProductDTO> initProductCacheByCategoryId(Long shopId, Long categoryId) {
        ProductDTO param = new ProductDTO();
        param.setShopId(shopId);
        param.setCategoryId(categoryId);
        List<ProductDTO> productList = getProductList(param);
        String key = ADMIN_CLIENT_PRODUCT_LIST + "::" + shopId + "::" + categoryId;
        redisService.setJsonStr(key, productList);
        return productList;
    }

    /**
     * 清除某个商品分类的缓存
     */
    @Override
    public void clearProductCache(Long... categoryIds) {
        Long shopId = StpKit.USER.getShopId(0L);
        //分类缓存
        String prefix = ADMIN_CLIENT_PRODUCT_LIST + "::" + shopId + "::";
        for (Long categoryId : categoryIds) {
            redisService.del(prefix + categoryId);
        }
        redisService.del(ADMIN_CLIENT_PRODUCT_LIST + "::" + shopId);
        //用户端分组缓存
        redisService.del(CacheConstant.USER_CLIENT_GROUPS_PRODUCT + "::" + shopId);
        //用户端热销缓存
        redisService.del(CacheConstant.USER_CLIENT_HOTS_PRODUCT + "::" + shopId);
    }

    /**
     * 清除某个商品分类的缓存
     */
    private void clearProductAllCache(Long shopId) {
        //分类缓存
        redisService.deleteKeysByPrefixSafe(ADMIN_CLIENT_PRODUCT_LIST + "::" + shopId);
        //用户端分组缓存
        redisService.del(CacheConstant.USER_CLIENT_GROUPS_PRODUCT + "::" + shopId);
        //用户端热销缓存
        redisService.del(CacheConstant.USER_CLIENT_HOTS_PRODUCT + "::" + shopId);
    }


    /**
     * 计算是否在可售时间内
     *
     * @param days      星期几，例如 "Monday,Tuesday"
     * @param startTime 起售时间
     * @param endTime   停售时间
     * @return 是否可售时间 1-是，0-否
     */
    private Integer calcIsSaleTime(String days, LocalTime startTime, LocalTime endTime) {
        if (StrUtil.isBlank(days) || ObjUtil.isNull(startTime) || ObjUtil.isNull(endTime)) {
            return SystemConstants.OneZero.ZERO;
        }
        String today = getWeekDayEnName();
        List<String> dayList = StrUtil.split(days, ",");
        LocalTime now = LocalTime.now().withNano(0);
        if (CollUtil.contains(dayList, today) && now.isAfter(startTime) && now.isBefore(endTime)) {
            return SystemConstants.OneZero.ONE;
        }
        return SystemConstants.OneZero.ZERO;
    }

    /**
     * 获取当前日期是星期几的英文名称
     *
     * @return 星期几的英文名称，例如 "Monday" 或 "Friday"
     */
    private String getWeekDayEnName() {
        // 获取当前日期
        LocalDate currentDate = LocalDate.now();
        // 获取当前日期是星期几，返回一个 DayOfWeek 枚举类型的实例
        DayOfWeek dayOfWeek = currentDate.getDayOfWeek();
        return dayOfWeek.getDisplayName(TextStyle.FULL, Locale.ENGLISH);
    }

    @Override
    public ProductDTO getProductById(Long id) {
        ProductDTO param = new ProductDTO();
        param.setId(id);
        QueryWrapper queryWrapper = buildFullQueryWrapper(param);
        ProductDTO dto = super.getOneAs(queryWrapper, ProductDTO.class);
        if (dto == null) {
            return null;
        }
        List<ProdSkuDTO> skuList = prodSkuMapper.selectListByQueryAs(query().eq(ProdSku::getProductId, id).eq(ProdSku::getIsDel, SystemConstants.OneZero.ZERO), ProdSkuDTO.class);
        dto.setSkuList(skuList);
        List<ProdConsRelationDTO> consList = prodConsRelationService.selectListByProdId(dto.getId());
        dto.setConsList(consList);
        dto.setRelatedRecommendJson(getRelateProductList(dto.getRelatedRecommend()));
        dto.setProGroupIds(prodGroupRelationMapper.selectListByQueryAs(query().select(ProdGroupRelation::getProdGroupId)
                .eq(ProdGroupRelation::getProductId, dto.getId()), Long.class));
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, key = "#dto.shopId", allEntries = true)
    public void addProduct(ProductDTO dto) {
        Long shopId = StpKit.USER.getShopId(0L);
        boolean exists = super.exists(query().eq(Product::getName, dto.getName()).eq(Product::getShopId, shopId).eq(Product::getIsDel, SystemConstants.OneZero.ZERO));
        if (exists) {
            throw new CzgException("名称已存在");
        }

        Product entity = BeanUtil.copyProperties(dto, Product.class);
        entity.setImages(JSON.toJSONString(dto.getImages(), JSONWriter.Feature.WriteMapNullValue));
        entity.setGroupSnap("[]");
        //套餐
        if (ProductTypeEnum.PACKAGE.value().equals(dto.getType())) {
            //套餐内容
            if (CollUtil.isNotEmpty(dto.getProGroupVo())) {
                String groupSnap = JSON.toJSONString(dto.getProGroupVo(), JSONWriter.Feature.WriteMapNullValue);
                entity.setGroupSnap(groupSnap);
            }
        }
        entity.setIsDel(SystemConstants.OneZero.ZERO);
        entity.setShopId(shopId);
        entity.setRelatedRecommend(dto.getRelatedRecommendStr());
        super.save(entity);
        dto.setId(entity.getId());
        List<Long> groupIds = dto.getProGroupIds();
        if (CollUtil.isNotEmpty(groupIds)) {
            List<ProdGroupRelation> prodGroupRelations = new ArrayList<>();
            for (Long groupId : groupIds) {
                ProdGroupRelation prodGroupRelation = new ProdGroupRelation();
                prodGroupRelation.setProductId(entity.getId());
                prodGroupRelation.setProdGroupId(groupId);
                prodGroupRelations.add(prodGroupRelation);
            }
            prodGroupRelationMapper.insertBatch(prodGroupRelations);
        }

        List<ProdSkuDTO> skuList = dto.getSkuList();
        if (CollUtil.isNotEmpty(skuList)) {
            List<ProdSku> prodSkuList = new ArrayList<>();
            for (ProdSkuDTO prodSkuDTO : skuList) {
                ProdSku prodSku = BeanUtil.copyProperties(prodSkuDTO, ProdSku.class);
                prodSku.setShopId(entity.getShopId());
                prodSku.setProductId(entity.getId());
                prodSku.setRealSalesNumber(BigDecimal.ZERO);
                prodSku.setIsPauseSale(SystemConstants.OneZero.ZERO);
                prodSku.setIsGrounding(SystemConstants.OneZero.ONE);
                prodSku.setIsDel(SystemConstants.OneZero.ZERO);
                prodSkuList.add(prodSku);
            }
            prodSkuMapper.insertBatch(prodSkuList);
        }
        ProdConsBindDTO prodConsBindDTO = new ProdConsBindDTO();
        prodConsBindDTO.setId(entity.getId());
        prodConsBindDTO.setConsList(dto.getConsList());
        prodConsRelationService.saveProdConsRelation(prodConsBindDTO);
        // 清除商品分类列表缓存
        clearProductCache(entity.getCategoryId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, key = "#dto.shopId", allEntries = true)
    public void updateProduct(ProductDTO dto) {
        Long shopId = StpKit.USER.getShopId(0L);
        dto.setShopId(shopId);
        boolean exists = super.exists(query().eq(Product::getName, dto.getName()).eq(Product::getShopId, shopId).eq(Product::getIsDel, SystemConstants.OneZero.ZERO).ne(Product::getId, dto.getId()));
        if (exists) {
            throw new CzgException("名称已存在");
        }
        Product old = super.getById(dto.getId());
        Product entity = BeanUtil.copyProperties(dto, Product.class);

        entity.setImages(JSON.toJSONString(dto.getImages(), JSONWriter.Feature.WriteMapNullValue));
        entity.setGroupSnap("[]");
        //套餐
        if (ProductTypeEnum.PACKAGE.value().equals(dto.getType())) {
            //套餐内容
            if (CollUtil.isNotEmpty(dto.getProGroupVo())) {
                entity.setGroupSnap(JSON.toJSONString(dto.getProGroupVo(), JSONWriter.Feature.WriteMapNullValue));
            }
        }
        entity.setIsDel(SystemConstants.OneZero.ZERO);
        entity.setShopId(shopId);
        if (!ProductTypeEnum.SKU.value().equals(entity.getType())) {
            UpdateChain.of(Product.class).set(Product::getSpecId, null).eq(Product::getId, dto.getId()).update();
        }
        entity.setRelatedRecommend(dto.getRelatedRecommendStr());
        super.updateById(entity);

        List<ProdSkuDTO> skuList = dto.getSkuList();
        // 商品SKU-ID列表
        List<Long> skuIdList = prodSkuMapper.selectListByQueryAs(query().select(ProdSku::getId).eq(ProdSku::getProductId, dto.getId()), Long.class);
        dto.setSkuList(skuList);
        if (CollUtil.isNotEmpty(skuList)) {
            for (ProdSkuDTO prodSkuDTO : skuList) {
                ProdSku prodSku = BeanUtil.copyProperties(prodSkuDTO, ProdSku.class);
                if (prodSku.getId() == null) {
                    prodSku.setShopId(entity.getShopId());
                    prodSku.setProductId(entity.getId());
                    prodSku.setRealSalesNumber(BigDecimal.ZERO);
                    prodSku.setIsPauseSale(SystemConstants.OneZero.ZERO);
                    prodSku.setIsGrounding(SystemConstants.OneZero.ONE);
                    prodSku.setIsDel(SystemConstants.OneZero.ZERO);
                    prodSkuMapper.insert(prodSku);
                } else {
                    prodSkuMapper.update(prodSku);
                }
            }
            // 实际提交的商品SKU-ID列表
            List<Long> list = skuList.stream().map(ProdSkuDTO::getId).filter(Objects::nonNull).distinct().toList();
            skuIdList.removeAll(list);
        }
        List<Long> groupIds = dto.getProGroupIds();
        if (CollUtil.isNotEmpty(groupIds)) {
            prodGroupRelationMapper.deleteByQuery(query().eq(ProdGroupRelation::getProductId, entity.getId()));
            List<ProdGroupRelation> prodGroupRelations = new ArrayList<>();
            for (Long groupId : groupIds) {
                ProdGroupRelation prodGroupRelation = new ProdGroupRelation();
                prodGroupRelation.setProductId(entity.getId());
                prodGroupRelation.setProdGroupId(groupId);
                prodGroupRelations.add(prodGroupRelation);
            }
            prodGroupRelationMapper.insertBatch(prodGroupRelations);
        }

        ProdConsBindDTO prodConsBindDTO = new ProdConsBindDTO();
        prodConsBindDTO.setId(dto.getId());
        prodConsBindDTO.setConsList(dto.getConsList());
        prodConsRelationService.saveProdConsRelation(prodConsBindDTO);
        if (CollUtil.isNotEmpty(skuIdList)) {
            // 逻辑删除无用的SKU数据
            UpdateChain.of(ProdSku.class)
                    .set(ProdSku::getIsDel, SystemConstants.OneZero.ONE)
                    .in(ProdSku::getId, skuIdList)
                    .eq(ProdSku::getShopId, shopId)
                    .update();
        }
        // 清除商品分类列表缓存
        if (!old.getCategoryId().equals(dto.getCategoryId())) {
            clearProductCache(old.getCategoryId(), entity.getCategoryId());
        } else {
            clearProductCache(old.getCategoryId());
        }
    }

    @Override
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, key = "#shopId", allEntries = true)
    public void deleteProduct(Long shopId, Long id) {
        Product old = getById(id);
        Long categoryId = old.getCategoryId();
        UpdateChain.of(Product.class)
                .set(Product::getIsDel, SystemConstants.OneZero.ONE)
                .eq(Product::getId, id)
                .eq(Product::getShopId, shopId)
                .update();
        prodGroupRelationMapper.deleteByQuery(query().eq(ProdGroupRelation::getProductId, id));
        prodConsRelationService.remove(query().eq(ProdConsRelation::getProductId, id));
        // 清除商品分类列表缓存
        clearProductCache(categoryId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, key = "#param.shopId", allEntries = true, beforeInvocation = true)
    public void onOffProduct(ProductIsSaleParam param) {
        Long shopId = StpKit.USER.getShopId(0L);
        String type = param.getType();
        Long id = param.getId();
        Integer isSale = param.getIsSale();
        String sensitiveOperation;
        if (isSale == 1) {
            sensitiveOperation = "上架";
        } else {
            sensitiveOperation = "下架";
        }
        Long prodId = null;
        if (ProductIsSaleTypeEnum.SKU.value().equals(type) && "sale".equals(param.getOptType())) {
            ProdSku prodSku = prodSkuMapper.selectOneById(id);
            if (prodSku == null) {
                throw new CzgException("SKU不存在");
            }
            prodId = prodSku.getProductId();
            prodSku.setIsGrounding(isSale);
            prodSkuMapper.update(prodSku);
            long normalCount = prodSkuMapper.selectCountByQuery(QueryWrapper.create()
                    .eq(ProdSku::getProductId, prodSku.getProductId())
                    .eq(ProdSku::getIsGrounding, SystemConstants.OneZero.ONE)
                    .eq(ProdSku::getIsDel, SystemConstants.OneZero.ZERO)
            );
            Product product = mapper.selectOneById(prodId);
            if (normalCount == 0 && product.getIsSale() == SystemConstants.OneZero.ONE) {
                UpdateChain.of(Product.class)
                        .set(Product::getIsSale, SystemConstants.OneZero.ZERO)
                        .eq(Product::getId, prodSku.getProductId())
                        .eq(Product::getShopId, shopId)
                        .update();
            } else if (normalCount > 0 && product.getIsSale() == SystemConstants.OneZero.ZERO) {
                UpdateChain.of(Product.class)
                        .set(Product::getIsSale, SystemConstants.OneZero.ONE)
                        .eq(Product::getId, prodSku.getProductId())
                        .eq(Product::getShopId, shopId)
                        .update();
            }
            sensitiveOperation = sensitiveOperation + "商品：" + product.getName() + " 规格：" + prodSku.getSpecInfo();
        } else if (ProductIsSaleTypeEnum.PRODUCT.value().equals(type)) {
            if ("sale".equals(param.getOptType())) {
                UpdateChain.of(Product.class)
                        .set(Product::getIsSale, isSale)
                        .eq(Product::getId, id)
                        .eq(Product::getShopId, shopId)
                        .update();
                UpdateChain.of(ProdSku.class)
                        .set(ProdSku::getIsGrounding, isSale)
                        .eq(ProdSku::getProductId, id)
                        .eq(ProdSku::getShopId, shopId)
                        .update();
            }
            Product product = mapper.selectOneById(id);
            prodId = product.getId();
            sensitiveOperation = sensitiveOperation + "商品：" + product.getName();
        }
        sensitiveOperationService.send(sensitiveOperation);
        // 清除商品分类列表缓存
        Product old = getById(prodId);
        Long categoryId = old.getCategoryId();
        clearProductCache(categoryId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, key = "#param.shopId", allEntries = true, beforeInvocation = true)
    public void markProductIsSoldOut(ProductIsSoldOutParam param) {
        Long shopId = StpKit.USER.getShopId(0L);
        String type = param.getType();
        Long id = param.getId();
        Integer isSoldOut = param.getIsSoldOut();
        Long prodId = null;
        if (ProductIsSaleTypeEnum.SKU.value().equals(type)) {
            ProdSku prodSku = prodSkuMapper.selectOneById(id);
            if (prodSku == null) {
                throw new CzgException("SKU不存在");
            }
            prodSku.setIsPauseSale(isSoldOut);
            prodSkuMapper.update(prodSku);
            long normalCount = prodSkuMapper.selectCountByQuery(QueryWrapper.create()
                    .eq(ProdSku::getProductId, prodSku.getProductId())
                    .eq(ProdSku::getIsPauseSale, SystemConstants.OneZero.ZERO)
                    .eq(ProdSku::getIsDel, SystemConstants.OneZero.ZERO)
            );
            if (normalCount == 0) {
                UpdateChain.of(Product.class)
                        .set(Product::getIsSoldStock, isSoldOut)
                        .eq(Product::getId, prodSku.getProductId())
                        .eq(Product::getShopId, shopId)
                        .update();
            }
            prodId = prodSku.getProductId();
        } else if (ProductIsSaleTypeEnum.PRODUCT.value().equals(type)) {
            UpdateChain.of(Product.class)
                    .set(Product::getIsSoldStock, isSoldOut)
                    .eq(Product::getId, id)
                    .eq(Product::getShopId, shopId)
                    .update();
            UpdateChain.of(ProdSku.class)
                    .set(ProdSku::getIsPauseSale, isSoldOut)
                    .eq(ProdSku::getProductId, id)
                    .eq(ProdSku::getShopId, shopId)
                    .update();
            prodId = id;
        }
        // 清除商品分类列表缓存
        Product old = getById(prodId);
        Long categoryId = old.getCategoryId();
        clearProductCache(categoryId);
        ThreadUtil.execAsync(() -> rabbitPublisher.sendConsInfoChangeMsg(Convert.toStr(shopId)));
    }

    @Override
    public void markProductIsAutoSoldOut(Long shopId, ProductIsAutoSaleParam param) {
        Product entity = super.getOne(query().eq(Product::getId, param.getId()).eq(Product::getShopId, shopId));
        if (entity == null) {
            throw new CzgException("商品不存在");
        }
        Product update = new Product();
        update.setIsAutoSoldStock(param.getIsAutoSoldStock());
        update(update, query().eq(Product::getId, param.getId()).eq(Product::getShopId, shopId));
        clearProductCache(entity.getCategoryId());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reportDamage(ProductReportDamageParam param) {
        Long shopId = StpKit.USER.getShopId();
        Long createUserId = StpKit.USER.getLoginIdAsLong();
        String createUserName = StpKit.USER.getAccount();
        Product product = mapper.selectOneById(param.getProductId());
        if (product == null) {
            throw new CzgException("商品不存在");
        }
        // 记录商品库存流水
        ProductStockFlow flow = new ProductStockFlow();
        flow.setCreateUserId(createUserId);
        flow.setCreateUserName(createUserName);
        flow.setShopId(shopId);
        flow.setProductId(product.getId());
        flow.setProductName(product.getName());
//        flow.setBeforeNumber(NumberUtil.toBigDecimal(stockNumber));
        flow.setInOutNumber(NumberUtil.sub(BigDecimal.ZERO, NumberUtil.toBigDecimal(param.getNumber())));
//        flow.setAfterNumber(NumberUtil.toBigDecimal(product.getStockNumber()));
        flow.setInOutType(InOutTypeEnum.OUT.value());
        flow.setInOutItem(InOutItemEnum.DAMAGE_OUT.value());
        flow.setImgUrls(JSON.toJSONString(param.getImgUrls()));
        flow.setRemark(param.getRemark());
        productStockFlowService.save(flow);
        List<ProductStockVO> productStockList = new ArrayList<>();
        productStockList.add(new ProductStockVO(param.getProductId(), BigDecimal.valueOf(param.getNumber())));
        consStockByProduct(shopId, InOutTypeEnum.OUT, InOutItemEnum.DAMAGE_OUT, productStockList, null, "【商品报损，自动报损相关耗材】");
    }

    @Override
    public void batchOperate(ProductBatchParam param) {
        Product product = new Product();
        //商品操作
        //category 修改分类
        //isSale 上下架
        //is_sold_stock 售罄
        //isAutoSoldStock 自动售罄
        switch (param.getType()) {
            case "category":
                product.setCategoryId(Long.valueOf(param.getValue()));
                break;
            case "isSale":
                product.setIsSale(Integer.valueOf(param.getValue()));
                break;
            case "is_sold_stock":
                product.setIsSoldStock(Integer.valueOf(param.getValue()));
                break;
            case "isAutoSoldStock":
                product.setIsAutoSoldStock(Integer.valueOf(param.getValue()));
                break;
        }
        update(product, query().eq(Product::getShopId, param.getShopId()).in(Product::getId, param.getIds()));
        clearProductAllCache(param.getShopId());
    }

    @Override
    public ProductStatisticsVo getProductStatistics(ProductInfoParam param) {
        ProductStatisticsVo data = productStockFlowMapper.getProductStatistics(param);
        if (data == null) {
            data = new ProductStatisticsVo();
        }
        return data;
    }

    @Override
    public Page<ProductStockFlow> findProductStockFlowPage(ProductStockFlowParam param) {
        Long shopId = StpKit.USER.getShopId(0L);
        param.setShopId(shopId);
        param.setProductId(param.getProductId());
        PageHelper.startPage(PageUtil.buildPageHelp());
        if (InOutItemEnum.ORDER_IN.value().equals(param.getInOutItem())) {
            param.setInOutType(InOutTypeEnum.OUT.value());
            param.setInOutItem(InOutItemEnum.ORDER_OUT.value());
            param.setIsGreaterZero(SystemConstants.OneZero.ONE);
        }
        return PageUtil.convert(new PageInfo<>(productStockFlowMapper.getProductStockFlowList(param)));
    }

    @Override
    public void consStockByProduct(Long shopId, InOutTypeEnum type, InOutItemEnum item, List<ProductStockVO> products, Long orderId, String remark) {
        if (CollUtil.isEmpty(products)) return;
        boolean isSendUp = false;
        for (ProductStockVO product : products) {
            List<ProdConsRelationDTO> consList = prodConsRelationService.selectStockByProdId(product.getProductId());
            if (CollUtil.isEmpty(consList)) return;
            for (ProdConsRelationDTO consInfo : consList) {
                BigDecimal surplusStock = consInfo.getSurplusStock();
                if (surplusStock == null || surplusStock.compareTo(BigDecimal.ZERO) <= 0) {
                    continue;
                }
                surplusStock = NumberUtil.mul(surplusStock, product.getNumber());
                BigDecimal beforeNumber = consInfo.getStockNumber();
                if (InOutTypeEnum.IN.equals(type)) {
                    consInfo.setStockNumber(NumberUtil.add(beforeNumber, surplusStock));
                } else {
                    consInfo.setStockNumber(NumberUtil.sub(beforeNumber, surplusStock));
                }
                // 更新耗材库存数量
                ConsInfo consInfoUp = new ConsInfo();
                consInfoUp.setStockNumber(consInfo.getStockNumber());
                consInfoMapper.updateByQuery(consInfoUp, query().eq(ConsInfo::getId, consInfo.getConsInfoId()).eq(ConsInfo::getShopId, consInfo.getShopId()));
                ConsStockFlow flow = new ConsStockFlow();
                flow.setInOutType(type.value());
                flow.setInOutItem(item.value());
                flow.setShopId(consInfo.getShopId());
                flow.setInOutDate(LocalDate.now());
                flow.setConId(consInfo.getConsInfoId());
                flow.setConName(consInfo.getConName());
                flow.setUnitName(consInfo.getConUnit());
                flow.setBeforeNumber(beforeNumber);
                flow.setInOutNumber(surplusStock);
                flow.setAfterNumber(consInfo.getStockNumber());
                flow.setPurchasePrice(consInfo.getPrice());
                flow.setSubTotal(BigDecimal.ZERO);
                if (flow.getPurchasePrice() != null && flow.getPurchasePrice().compareTo(BigDecimal.ZERO) > 0) {
                    flow.setSubTotal(NumberUtil.mul(surplusStock, flow.getPurchasePrice()));
                }
                flow.setProductId(product.getProductId());
                flow.setOrderId(orderId);
                flow.setRemark(remark);
                consStockFlowService.save(flow);
                boolean b = consStockFlowService.sendStockMsg(flow, consInfo.getConWarning());
                if (b) isSendUp = b;
            }
        }
        if (isSendUp) {
            ThreadUtil.execAsync(() -> rabbitPublisher.sendConsInfoChangeMsg(Convert.toStr(shopId)));
        }
    }


    private List<RelatedProductDTO> getRelateProductList(String relatedProduct) {
        if (StrUtil.isNotBlank(relatedProduct) && !"[]".equals(relatedProduct)) {
            List<Long> idList = JSONArray.parseArray(relatedProduct, Long.class);

            QueryWrapper wrapper = QueryWrapper.create().select(Product::getId, Product::getName, Product::getCoverImg).eq(Product::getIsDel, SystemConstants.OneZero.ZERO)
                    .in(Product::getId, idList);
            return super.listAs(wrapper, RelatedProductDTO.class);
        }

        return new ArrayList<>();
    }
}
