package cn.iocoder.mall.product.service;

import cn.iocoder.common.framework.constant.DeletedStatusEnum;
import cn.iocoder.common.framework.util.CollectionUtil;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.util.StringUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.product.api.ProductSpuService;
import cn.iocoder.mall.product.api.bo.*;
import cn.iocoder.mall.product.api.constant.ProductErrorCodeEnum;
import cn.iocoder.mall.product.api.constant.ProductSpuConstants;
import cn.iocoder.mall.product.api.dto.ProductSkuAddOrUpdateDTO;
import cn.iocoder.mall.product.api.dto.ProductSpuAddDTO;
import cn.iocoder.mall.product.api.dto.ProductSpuPageDTO;
import cn.iocoder.mall.product.api.dto.ProductSpuUpdateDTO;
import cn.iocoder.mall.product.api.message.ProductUpdateMessage;
import cn.iocoder.mall.product.convert.ProductSpuConvert;
import cn.iocoder.mall.product.dao.ProductSkuMapper;
import cn.iocoder.mall.product.dao.ProductSpuMapper;
import cn.iocoder.mall.product.dataobject.ProductCategoryDO;
import cn.iocoder.mall.product.dataobject.ProductSkuDO;
import cn.iocoder.mall.product.dataobject.ProductSpuDO;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service // 实际上不用添加。添加的原因是，必须 Spring 报错提示
@org.apache.dubbo.config.annotation.Service(validation = "true")
public class ProductSpuServiceImpl implements ProductSpuService {

    @Autowired
    private ProductSpuMapper productSpuMapper;
    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private ProductCategoryServiceImpl productCategoryService;
    @Autowired
    private ProductAttrServiceImpl productAttrService;

    @Resource
    private RocketMQTemplate rocketMQTemplate;

//    @Override
//    public ProductSpuBO getProductSpuDetail(Integer id) {
//        ProductSpuDO productSpuDO = productSpuMapper.selectById(id);
//        // 转换成 BO
//        return ProductSpuConvert.INSTANCE.convert(productSpuDO);
//    }

    @Override
    public CommonResult<ProductSpuDetailBO> getProductSpuDetail(Integer id) {
        // 校验商品 spu 存在
        ProductSpuDO spu = productSpuMapper.selectById(id);
        if (spu == null) {
            return ServiceExceptionUtil.error(ProductErrorCodeEnum.PRODUCT_SPU_NOT_EXISTS.getCode());
        }
        // 获得商品分类分类
        ProductCategoryDO category = productCategoryService.getProductCategory(spu.getCid());
        Assert.notNull(category, String.format("分类编号(%d) 对应", spu.getCid()));
        // 获得商品 sku 数组
        List<ProductSkuDO> skus = productSkuMapper.selectListBySpuIdAndStatus(id, ProductSpuConstants.SKU_STATUS_ENABLE);
        // 获得规格
        Set<Integer> productAttrValueIds = new HashSet<>();
        skus.forEach(sku -> productAttrValueIds.addAll(StringUtil.splitToInt(sku.getAttrs(), ",")));
        CommonResult<List<ProductAttrAndValuePairBO>> validAttrResult = productAttrService.validProductAttrAndValue(productAttrValueIds,
                false); // 读取规格时，不考虑规格是否被禁用
        // 返回成功
        return CommonResult.success(ProductSpuConvert.INSTANCE.convert2(spu, skus, validAttrResult.getData(), category));
    }

    @Override
    public CommonResult<List<ProductSpuDetailBO>> getProductSpuDetailListForSync(Integer lastId, Integer limit) {
        // TODO 芋艿，这里目前是一个一个进行计算，后续需要优化下
        // 查询下一批商品编号集合
        List<Integer> spuIds = productSpuMapper.selectIdListByIdGt(lastId, limit);
        if (spuIds.isEmpty()) {
            return CommonResult.success(Collections.emptyList());
        }
        // 查询每个商品明细
        List<ProductSpuDetailBO> spus = spuIds.stream().map(id -> getProductSpuDetail(id).getData()).collect(Collectors.toList()); // TODO 芋艿，此处相当于是 N 个查询，后续要优化。
        return CommonResult.success(spus);
    }

    @Override
    public CommonResult<ProductSpuDetailBO> addProductSpu(Integer adminId, ProductSpuAddDTO productSpuAddDTO) {
        CommonResult<ProductSpuDetailBO> result = addProductSpu0(adminId, productSpuAddDTO);
        // 如果新增生成，发送创建商品 Topic 消息
        if (result.isSuccess()) {
            // TODO 芋艿，先不考虑事务的问题。等后面的 fescar 一起搞
            sendProductUpdateMessage(result.getData().getId());
        }
        return result;
    }

    @SuppressWarnings("Duplicates")
    @Transactional
    public CommonResult<ProductSpuDetailBO> addProductSpu0(Integer adminId, ProductSpuAddDTO productSpuAddDTO) {
        // 校验商品分类分类存在
        CommonResult<ProductCategoryDO> validCategoryResult = productCategoryService.validProductCategory(productSpuAddDTO.getCid());
        if (validCategoryResult.isError()) {
            return CommonResult.error(validCategoryResult);
        }
        // 校验规格是否存在
        Set<Integer> productAttrValueIds = new HashSet<>();
        productSpuAddDTO.getSkus().forEach(productSkuAddDTO -> productAttrValueIds.addAll(productSkuAddDTO.getAttrs()));
        CommonResult<List<ProductAttrAndValuePairBO>> validAttrResult = productAttrService.validProductAttrAndValue(productAttrValueIds
            , true); // 读取规格时，需要考虑规格是否被禁用
        if (validAttrResult.isError()) {
            return CommonResult.error(validAttrResult);
        }
        // 保存 Spu
        ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(productSpuAddDTO)
                .setPicUrls(StringUtil.join(productSpuAddDTO.getPicUrls(), ","))
                .setSort(0); // 排序为 0
        spu.setCreateTime(new Date());
        spu.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
        initSpuFromSkus(spu, productSpuAddDTO.getSkus()); // 初始化 sku 相关信息到 spu 中
        productSpuMapper.insert(spu);
        // 保存 Sku
        List<ProductSkuDO> skus = productSpuAddDTO.getSkus().stream().map(productSkuAddDTO -> {
            ProductSkuDO sku = ProductSpuConvert.INSTANCE.convert(productSkuAddDTO)
                    .setSpuId(spu.getId())
                    .setStatus(ProductSpuConstants.SKU_STATUS_ENABLE)
                    .setAttrs(StringUtil.join(productSkuAddDTO.getAttrs(), ","));
            sku.setCreateTime(new Date());
            sku.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
            return sku;
        }).collect(Collectors.toList());
        // 校验 Sku 规格
        CommonResult<Boolean> validProductSkuResult = validProductSku(productSpuAddDTO.getSkus(), validAttrResult.getData());
        if (validProductSkuResult.isError()) {
//            return CommonResult.error(validProductSkuResult);
            throw ServiceExceptionUtil.exception(validProductSkuResult.getCode());
        }
        productSkuMapper.insertList(skus);
        // 返回成功
        return CommonResult.success(ProductSpuConvert.INSTANCE.convert2(spu, skus, validAttrResult.getData(),
                validCategoryResult.getData()));
    }

    @Override
    public CommonResult<Boolean> updateProductSpu(Integer adminId, ProductSpuUpdateDTO productSpuUpdateDTO) {
        CommonResult<Boolean> result = updateProductSpu0(adminId, productSpuUpdateDTO);
        if (result.isSuccess()) {
            // TODO 芋艿，先不考虑事务的问题。等后面的 fescar 一起搞
            sendProductUpdateMessage(productSpuUpdateDTO.getId());
        }
        return result;
    }

    @SuppressWarnings("Duplicates")
    @Transactional
    public CommonResult<Boolean> updateProductSpu0(Integer adminId, ProductSpuUpdateDTO productSpuUpdateDTO) {
        // 校验 Spu 是否存在
        if (productSpuMapper.selectById(productSpuUpdateDTO.getId()) == null) {
            return ServiceExceptionUtil.error(ProductErrorCodeEnum.PRODUCT_SPU_NOT_EXISTS.getCode());
        }
        // 校验商品分类分类存在
        CommonResult<ProductCategoryDO> validCategoryResult = productCategoryService.validProductCategory(productSpuUpdateDTO.getCid());
        if (validCategoryResult.isError()) {
            return CommonResult.error(validCategoryResult);
        }
        // 校验规格是否存在
        Set<Integer> productAttrValueIds = new HashSet<>();
        productSpuUpdateDTO.getSkus().forEach(productSkuAddDTO -> productAttrValueIds.addAll(productSkuAddDTO.getAttrs()));
        CommonResult<List<ProductAttrAndValuePairBO>> validAttrResult = productAttrService.validProductAttrAndValue(productAttrValueIds,
                true); // 读取规格时，需要考虑规格是否被禁用
        if (validAttrResult.isError()) {
            return CommonResult.error(validAttrResult);
        }
        // 校验 Sku 规格
        CommonResult<Boolean> validProductSkuResult = validProductSku(productSpuUpdateDTO.getSkus(), validAttrResult.getData());
        if (validProductSkuResult.isError()) {
            return CommonResult.error(validProductSkuResult);
        }
        // 更新 Spu
        ProductSpuDO updateSpu = ProductSpuConvert.INSTANCE.convert(productSpuUpdateDTO)
                .setPicUrls(StringUtil.join(productSpuUpdateDTO.getPicUrls(), ","));
        initSpuFromSkus(updateSpu, productSpuUpdateDTO.getSkus()); // 初始化 sku 相关信息到 spu 中
        productSpuMapper.update(updateSpu);
        // 修改 Sku
        List<ProductSkuDO> existsSkus = productSkuMapper.selectListBySpuIdAndStatus(productSpuUpdateDTO.getId(), ProductSpuConstants.SKU_STATUS_ENABLE);
        List<ProductSkuDO> insertSkus = new ArrayList<>(0); // 1、找不到，进行插入
        List<Integer> deleteSkus = new ArrayList<>(0); // 2、多余的，删除
        List<ProductSkuDO> updateSkus = new ArrayList<>(0); // 3、找的到，进行更新。
        for (ProductSkuAddOrUpdateDTO skuUpdateDTO : productSpuUpdateDTO.getSkus()) {
            ProductSkuDO existsSku = findProductSku(skuUpdateDTO.getAttrs(), existsSkus);
            // 3、找的到，进行更新。
            if (existsSku != null) {
                // 移除
                existsSkus.remove(existsSku);
                // 创建 ProductSkuDO
                updateSkus.add(ProductSpuConvert.INSTANCE.convert(skuUpdateDTO).setId(existsSku.getId()));
                continue;
            }
            // 1、找不到，进行插入
            ProductSkuDO insertSku = ProductSpuConvert.INSTANCE.convert(skuUpdateDTO)
                    .setSpuId(productSpuUpdateDTO.getId()).setStatus(ProductSpuConstants.SKU_STATUS_ENABLE).setAttrs(StringUtil.join(skuUpdateDTO.getAttrs(), ","));
            insertSku.setCreateTime(new Date());
            insertSku.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
            insertSkus.add(insertSku);
        }
        // 2、多余的，删除
        if (!existsSkus.isEmpty()) {
            deleteSkus.addAll(existsSkus.stream().map(ProductSkuDO::getId).collect(Collectors.toList()));
        }
        // 执行修改 Sku
        if (!insertSkus.isEmpty()) {
            productSkuMapper.insertList(insertSkus);
        }
        if (!updateSkus.isEmpty()) {
            updateSkus.forEach(productSkuDO -> productSkuMapper.update(productSkuDO));
        }
        if (!deleteSkus.isEmpty()) {
            productSkuMapper.updateToDeleted(deleteSkus);
        }
        return CommonResult.success(true);
    }

    @Override
    public CommonResult<Boolean> updateProductSpuSort(Integer adminId, Integer spuId, Integer sort) {
        // 校验 Spu 是否存在
        if (productSpuMapper.selectById(spuId) == null) {
            return ServiceExceptionUtil.error(ProductErrorCodeEnum.PRODUCT_SPU_NOT_EXISTS.getCode());
        }
        // 更新排序
        ProductSpuDO updateSpu = new ProductSpuDO().setId(spuId).setSort(sort);
        productSpuMapper.update(updateSpu);
        // 修改成功，发送商品 Topic 消息
        sendProductUpdateMessage(spuId);
        // 返回成功
        return CommonResult.success(true);
    }

    @Override
    public CommonResult<ProductSpuPageBO> getProductSpuPage(ProductSpuPageDTO productSpuPageDTO) {
        ProductSpuPageBO productSpuPage = new ProductSpuPageBO();
        // 查询分页数据
        int offset = (productSpuPageDTO.getPageNo() - 1) * productSpuPageDTO.getPageSize();
        productSpuPage.setSpus(ProductSpuConvert.INSTANCE.convert(productSpuMapper.selectListByNameLikeOrderBySortAsc(
                productSpuPageDTO.getName(), productSpuPageDTO.getCid(), productSpuPageDTO.getVisible(),
            offset, productSpuPageDTO.getPageSize())));
        // 查询分页总数
        productSpuPage.setCount(productSpuMapper.selectCountByNameLike(productSpuPageDTO.getName(), productSpuPageDTO.getCid(), productSpuPageDTO.getVisible()));
        // 返回结果
        return CommonResult.success(productSpuPage);
    }

    @Override
    public CommonResult<List<ProductSpuBO>> getProductSpuList(Collection<Integer> ids) {
        List<ProductSpuDO> spus = productSpuMapper.selectByIds(ids);
        return CommonResult.success(ProductSpuConvert.INSTANCE.convert(spus));
    }

    @Override
    public CommonResult<ProductSkuBO> getProductSku(Integer id) {
        ProductSkuDO sku = productSkuMapper.selectById(id);
        return CommonResult.success(ProductSpuConvert.INSTANCE.convert4(sku));
    }

    @Override
    public CommonResult<List<ProductSkuDetailBO>> getProductSkuDetailList(Collection<Integer> ids) {
        // 查询 SKU 数组
        List<ProductSkuDO> skus = productSkuMapper.selectByIds(ids);
        if (skus.isEmpty()) {
            return CommonResult.success(Collections.emptyList());
        }
        // 查询 SPU 数组
        List<ProductSpuDO> spus = productSpuMapper.selectByIds(skus.stream().map(ProductSkuDO::getSpuId).collect(Collectors.toSet()));
        if (spus.isEmpty()) {
            return CommonResult.success(Collections.emptyList());
        }
        // 获得规格
        Set<Integer> productAttrValueIds = new HashSet<>();
        skus.forEach(sku -> productAttrValueIds.addAll(StringUtil.splitToInt(sku.getAttrs(), ",")));
        CommonResult<List<ProductAttrAndValuePairBO>> validAttrResult = productAttrService.validProductAttrAndValue(productAttrValueIds,
                false); // 读取规格时，不考虑规格是否被禁用
        // 返回成功
        return CommonResult.success(ProductSpuConvert.INSTANCE.convert3(skus, spus, validAttrResult.getData()));
    }

    /**
     * 校验 sku 是否合法
     *
     * @param productSkuAddDTOs sku 添加或修改信息
     * @param productAttrDetailBOs 商品规格明细数组
     * @return 是否校验通过
     */
    private CommonResult<Boolean> validProductSku(List<ProductSkuAddOrUpdateDTO> productSkuAddDTOs, List<ProductAttrAndValuePairBO> productAttrDetailBOs) {
        // 创建 ProductAttrDetailBO 的映射。其中，KEY 为 ProductAttrDetailBO.attrValueId ，即规格值的编号
        Map<Integer, ProductAttrAndValuePairBO> productAttrDetailBOMap = productAttrDetailBOs.stream().collect(
                Collectors.toMap(ProductAttrAndValuePairBO::getAttrValueId, productAttrDetailBO -> productAttrDetailBO));
        // 1. 先校验，一个 Sku 下，没有重复的规格。校验方式是，遍历每个 Sku ，看看是否有重复的规格 attrId
        for (ProductSkuAddOrUpdateDTO sku : productSkuAddDTOs) {
            Set<Integer> attrIds = sku.getAttrs().stream().map(attrValueId -> productAttrDetailBOMap.get(attrValueId).getAttrId()).collect(Collectors.toSet());
            if (attrIds.size() != sku.getAttrs().size()) {
                return ServiceExceptionUtil.error(ProductErrorCodeEnum.PRODUCT_SKU_ATTR_CANT_NOT_DUPLICATE.getCode());
            }
        }
        // 2. 再校验，每个 Sku 的规格值的数量，是一致的。
        int attrSize = productSkuAddDTOs.get(0).getAttrs().size();
        for (int i = 1; i < productSkuAddDTOs.size(); i++) {
            if (attrSize != productSkuAddDTOs.get(i).getAttrs().size()) {
                return ServiceExceptionUtil.error(ProductErrorCodeEnum.PRODUCT_SPU_ATTR_NUMBERS_MUST_BE_EQUALS.getCode());
            }
        }
        // 3. 最后校验，每个 Sku 之间不是重复的
        Set<Set<Integer>> skuAttrValues = new HashSet<>(); // 每个元素，都是一个 Sku 的 attrValueId 集合。这样，通过最外层的 Set ，判断是否有重复的.
        for (ProductSkuAddOrUpdateDTO sku : productSkuAddDTOs) {
            if (!skuAttrValues.add(new HashSet<>(sku.getAttrs()))) { // 添加失败，说明重复
                return ServiceExceptionUtil.error(ProductErrorCodeEnum.PRODUCT_SPU_SKU__NOT_DUPLICATE.getCode());
            }
        }
        // 校验通过
        return CommonResult.success(true);
    }

    /**
     * 获得 sku 数组中，指定规格的 sku
     *
     * @param attrs 指定规格
     * @param skus sku 数组
     * @return 符合条件的 sku
     */
    private ProductSkuDO findProductSku(Collection<Integer> attrs, List<ProductSkuDO> skus) {
        if (CollectionUtil.isEmpty(skus)) {
            return null;
        }
        // 创建成 Set ，方便后面比较
        attrs = new HashSet<>(attrs);
        for (ProductSkuDO sku : skus) {
            Set<Integer> skuAttrs = StringUtil.split(sku.getAttrs(), ",").stream().map(Integer::parseInt).collect(Collectors.toSet());
            if (attrs.equals(skuAttrs)) {
                return sku;
            }
        }
        return null;
    }

    /**
     * 根据 sku 数组，计算相关的字段到 spu 中。
     *
     * @param spu spu
     * @param skus sku 数组
     */
    private void initSpuFromSkus(ProductSpuDO spu, List<ProductSkuAddOrUpdateDTO> skus) {
        assert skus.size() > 0; // 写个断言，避免下面警告
        spu.setPrice(skus.stream().min(Comparator.comparing(ProductSkuAddOrUpdateDTO::getPrice)).get().getPrice()); // 求最小价格
        spu.setQuantity(skus.stream().mapToInt(ProductSkuAddOrUpdateDTO::getQuantity).sum()); // 求库存之和
    }

    private void sendProductUpdateMessage(Integer id) {
        rocketMQTemplate.convertAndSend(ProductUpdateMessage.TOPIC, new ProductUpdateMessage().setId(id));
    }

}
