package cn.newfeifan.mall.module.product.service.skuapply;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.newfeifan.mall.module.product.convert.skuapply.SkuApplyConvert;
import cn.newfeifan.mall.module.product.dal.dataobject.property.ProductPropertyDO;
import cn.newfeifan.mall.module.product.dal.dataobject.property.ProductPropertyValueDO;
import cn.newfeifan.mall.module.product.service.property.ProductPropertyService;
import cn.newfeifan.mall.module.product.service.property.ProductPropertyValueService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import cn.newfeifan.mall.module.product.controller.admin.skuapply.vo.*;
import cn.newfeifan.mall.module.product.dal.dataobject.skuapply.SkuApplyDO;
import cn.newfeifan.mall.framework.common.pojo.PageResult;
import cn.newfeifan.mall.framework.common.util.object.BeanUtils;

import cn.newfeifan.mall.module.product.dal.mysql.skuapply.SkuApplyMapper;

import static cn.newfeifan.mall.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.newfeifan.mall.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.newfeifan.mall.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.newfeifan.mall.module.product.enums.ErrorCodeConstants.*;

/**
 * 商品sku申请 Service 实现类
 *
 * @author 非繁人
 */
@Service
@Validated
public class SkuApplyServiceImpl implements SkuApplyService {

    @Resource
    private SkuApplyMapper skuApplyMapper;

    @Resource
    @Lazy // 循环依赖，避免报错
    private ProductPropertyService productPropertyService;
    @Resource
    private ProductPropertyValueService productPropertyValueService;

    @Override
    public Long createSkuApply(SkuApplySaveReqVO createReqVO) {
        // 插入
        SkuApplyDO skuApply = BeanUtils.toBean(createReqVO, SkuApplyDO.class);
        skuApplyMapper.insert(skuApply);
        // 返回
        return skuApply.getId();
    }

    @Override
    public void deleteSkuApply(Long id) {
        // 校验存在
        validateSkuApplyExists(id);
        // 删除
        skuApplyMapper.deleteById(id);
    }

    private void validateSkuApplyExists(Long id) {
        if (skuApplyMapper.selectById(id) == null) {
            throw exception(SKU_APPLY_NOT_EXISTS);
        }
    }

    @Override
    public SkuApplyDO getSkuApply(Long id) {
        return skuApplyMapper.selectById(id);
    }

    @Override
    public PageResult<SkuApplyDO> getSkuApplyPage(SkuApplyPageReqVO pageReqVO) {
        return skuApplyMapper.selectPage(pageReqVO);
    }

    @Override
    public void createSkuList(Long spuId, List<SkuApplySaveReqVO> skuCreateReqList) {
        List<SkuApplyDO> skus = BeanUtils.toBean(skuCreateReqList, SkuApplyDO.class, sku -> sku.setSpuApplyId(spuId));
        skuApplyMapper.insertBatch(skus);
    }

    @Override
    public List<SkuApplyDO> getSkuListBySpuId(Long spuApplyId) {
        return skuApplyMapper.selectListBySpuId(spuApplyId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSkuList(Long spuApplyId, List<SkuApplySaveReqVO> skus) {
        // 构建属性与 SKU 的映射关系;
        Map<String, Long> existsSkuMap = convertMap(skuApplyMapper.selectListBySpuId(spuApplyId),
                SkuApplyConvert.INSTANCE::buildPropertyKey, SkuApplyDO::getId);

        // 拆分三个集合，新插入的、需要更新的、需要删除的
        List<SkuApplyDO> insertSkus = new ArrayList<>();
        List<SkuApplyDO> updateSkus = new ArrayList<>();
        List<SkuApplyDO> allUpdateSkus = BeanUtils.toBean(skus, SkuApplyDO.class, sku -> sku.setSpuApplyId(spuApplyId));
        allUpdateSkus.forEach(sku -> {
            String propertiesKey = SkuApplyConvert.INSTANCE.buildPropertyKey(sku);
            // 1、找得到的，进行更新
            Long existsSkuId = existsSkuMap.remove(propertiesKey);
            if (existsSkuId != null) {
                sku.setId(existsSkuId);
                updateSkus.add(sku);
                return;
            }
            // 2、找不到，进行插入
            sku.setSpuApplyId(spuApplyId);
            insertSkus.add(sku);
        });

        // 执行最终的批量操作
        if (CollUtil.isNotEmpty(insertSkus)) {
            skuApplyMapper.insertBatch(insertSkus);
        }
        if (CollUtil.isNotEmpty(updateSkus)) {
            updateSkus.forEach(sku -> skuApplyMapper.updateById(sku));
        }
        if (CollUtil.isNotEmpty(existsSkuMap)) {
            skuApplyMapper.deleteBatchIds(existsSkuMap.values());
        }
    }

    @Override
    public void validateSkuList(List<SkuApplySaveReqVO> skus, Boolean specType) {
        // 0、校验skus是否为空
        if (CollUtil.isEmpty(skus)) {
            throw exception(SKU_NOT_EXISTS);
        }

        // 校验结算价是否大于成本价
        for (SkuApplySaveReqVO sku : skus) {
            // 如果是高精度商品
            if (sku.getSettlementPrice() > sku.getCostPrice()) {
                throw exception(HIGH_PRECISION_PRICE_NOT_ENOUGH);
            }
        }

        // 单规格，赋予单规格默认属性
        if (ObjectUtil.equal(specType, false)) {
            SkuApplySaveReqVO skuVO = skus.get(0);
            List<SkuApplySaveReqVO.Property> properties = new ArrayList<>();
            SkuApplySaveReqVO.Property property = new SkuApplySaveReqVO.Property()
                    .setPropertyId(ProductPropertyDO.ID_DEFAULT).setPropertyName(ProductPropertyDO.NAME_DEFAULT)
                    .setValueId(ProductPropertyValueDO.ID_DEFAULT).setValueName(ProductPropertyValueDO.NAME_DEFAULT);
            properties.add(property);
            skuVO.setProperties(properties);
            return; // 单规格不需要后续的校验
        }

        // 1、校验属性项存在
        Set<Long> propertyIds = skus.stream().filter(p -> p.getProperties() != null)
                // 遍历多个 Property 属性
                .flatMap(p -> p.getProperties().stream())
                // 将每个 Property 转换成对应的 propertyId，最后形成集合
                .map(SkuApplySaveReqVO.Property::getPropertyId)
                .collect(Collectors.toSet());
        List<ProductPropertyDO> propertyList = productPropertyService.getPropertyList(propertyIds);
        if (propertyList.size() != propertyIds.size()) {
            throw exception(PROPERTY_NOT_EXISTS);
        }

        // 2. 校验，一个 SKU 下，没有重复的属性。校验方式是，遍历每个 SKU ，看看是否有重复的属性 propertyId
        Map<Long, ProductPropertyValueDO> propertyValueMap = convertMap(productPropertyValueService.getPropertyValueListByPropertyId(propertyIds), ProductPropertyValueDO::getId);
        skus.forEach(sku -> {
            Set<Long> skuPropertyIds = convertSet(sku.getProperties(), propertyItem -> propertyValueMap.get(propertyItem.getValueId()).getPropertyId());
            if (skuPropertyIds.size() != sku.getProperties().size()) {
                throw exception(SKU_PROPERTIES_DUPLICATED);
            }
        });

        // 3. 再校验，每个 Sku 的属性值的数量，是一致的。
        int attrValueIdsSize = skus.get(0).getProperties().size();
        for (int i = 1; i < skus.size(); i++) {
            if (attrValueIdsSize != skus.get(i).getProperties().size()) {
                throw exception(SPU_ATTR_NUMBERS_MUST_BE_EQUALS);
            }
        }

        // 4. 最后校验，每个 Sku 之间不是重复的
        // 每个元素，都是一个 Sku 的 attrValueId 集合。这样，通过最外层的 Set ，判断是否有重复的.
        Set<Set<Long>> skuAttrValues = new HashSet<>();
        for (SkuApplySaveReqVO sku : skus) {
            // 添加失败，说明重复
            if (!skuAttrValues.add(convertSet(sku.getProperties(), SkuApplySaveReqVO.Property::getValueId))) {
                throw exception(SPU_SKU_NOT_DUPLICATE);
            }
        }
    }

}