package com.zt.plat.module.base.service.materialhasproperties;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.zt.plat.module.base.controller.admin.materialhasproperties.vo.*;
import com.zt.plat.module.base.dal.dao.materialhasproperties.MaterialHasPropertiesMapper;
import com.zt.plat.module.base.dal.dao.materialproperties.MaterialPropertiesMapper;
import com.zt.plat.module.base.dal.dataobject.materialhasproperties.MaterialHasPropertiesDO;
import com.zt.plat.module.base.dal.dataobject.materialproperties.MaterialPropertiesDO;
import com.zt.plat.framework.common.pojo.PageParam;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList;
import static com.zt.plat.framework.common.util.collection.CollectionUtils.diffList;
import static com.zt.plat.module.base.enums.ErrorCodeConstants.*;

/**
 * 物料持有属性 Service 实现类
 *
 * @author 后台管理-1
 */
@Service
@Validated
public class MaterialHasPropertiesServiceImpl implements MaterialHasPropertiesService {

    @Resource
    private MaterialHasPropertiesMapper materialHasPropertiesMapper;

    @Resource
    private MaterialPropertiesMapper materialPropertiesMapper;

    @Override
    public MaterialHasPropertiesRespVO createMaterialHasProperties(MaterialHasPropertiesSaveReqVO createReqVO) {
        validatePropertyCodeUnique(createReqVO.getInfomationId(), createReqVO.getPropertiesId(), null);
        // 插入
        MaterialHasPropertiesDO materialHasProperties = BeanUtils.toBean(createReqVO, MaterialHasPropertiesDO.class);
        materialHasPropertiesMapper.insert(materialHasProperties);
        // 返回
        return BeanUtils.toBean(materialHasProperties, MaterialHasPropertiesRespVO.class);
    }

    @Override
    public void updateMaterialHasProperties(MaterialHasPropertiesSaveReqVO updateReqVO) {
        // 校验存在
        validateMaterialHasPropertiesExists(updateReqVO.getId());
        validatePropertyCodeUnique(updateReqVO.getInfomationId(), updateReqVO.getPropertiesId(), updateReqVO.getId());
        // 更新
        MaterialHasPropertiesDO updateObj = BeanUtils.toBean(updateReqVO, MaterialHasPropertiesDO.class);
        materialHasPropertiesMapper.updateById(updateObj);
    }

    @Override
    public void deleteMaterialHasProperties(Long id) {
        // 校验存在
        validateMaterialHasPropertiesExists(id);
        // 删除
        materialHasPropertiesMapper.deleteById(id);
    }

    @Override
    public void deleteMaterialHasPropertiesListByIds(List<Long> ids) {
        // 校验存在
        validateMaterialHasPropertiesExists(ids);
        // 删除
        materialHasPropertiesMapper.deleteByIds(ids);
    }

    private void validateMaterialHasPropertiesExists(List<Long> ids) {
        List<MaterialHasPropertiesDO> list = materialHasPropertiesMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_HAS_PROPERTIES_NOT_EXISTS);
        }
    }

    private void validateMaterialHasPropertiesExists(Long id) {
        if (materialHasPropertiesMapper.selectById(id) == null) {
            throw exception(MATERIAL_HAS_PROPERTIES_NOT_EXISTS);
        }
    }

    @Override
    public MaterialHasPropertiesDO getMaterialHasProperties(Long id) {
        return materialHasPropertiesMapper.selectById(id);
    }

    @Override
    public PageResult<MaterialHasPropertiesDO> getMaterialHasPropertiesPage(MaterialHasPropertiesPageReqVO pageReqVO) {
        return materialHasPropertiesMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MaterialHasPropertiesBatchSaveRespVO batchSave(MaterialHasPropertiesBatchSaveReqVO batchReqVO, Long deptId) {
        MaterialHasPropertiesBatchSaveRespVO resp = new MaterialHasPropertiesBatchSaveRespVO();
        Long infoId = batchReqVO.getInfomationId();
        if (infoId == null) {
            resp.getRowErrors().add(new RowValidationErrorVO(0, "物料信息 ID 不能为空"));
            return resp;
        }
        // 全量替换：先删除该物料的已有属性
        LambdaQueryWrapperX<MaterialHasPropertiesDO> delQuery = new LambdaQueryWrapperX<>();
        delQuery.eq(MaterialHasPropertiesDO::getInfomationId, infoId);
        if (deptId == null) {
            delQuery.isNull(MaterialHasPropertiesDO::getDeptId);
        } else {
            delQuery.eq(MaterialHasPropertiesDO::getDeptId, deptId);
        }
        materialHasPropertiesMapper.delete(delQuery);

        List<MaterialHasPropertiesBatchItemReqVO> properties = batchReqVO.getProperties();
        if (CollUtil.isEmpty(properties)) {
            return resp;
        }

        // 去重并按提交顺序插入
        Set<String> dedupCodes = new LinkedHashSet<>();
        Set<Long> propertiesIds = properties.stream()
                .map(MaterialHasPropertiesBatchItemReqVO::getPropertiesId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, MaterialPropertiesDO> propertyMap = propertiesIds.isEmpty()
                ? Map.of()
                : materialPropertiesMapper.selectBatchIds(propertiesIds).stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(MaterialPropertiesDO::getId, Function.identity()));

        for (int i = 0; i < properties.size(); i++) {
            MaterialHasPropertiesBatchItemReqVO item = properties.get(i);
            String propIdStr = item.getPropertiesId() == null ? null : String.valueOf(item.getPropertiesId());
            if (StrUtil.isBlank(propIdStr)) {
                resp.getRowErrors().add(new RowValidationErrorVO(i + 1, "属性 ID 不能为空"));
                continue;
            }
            MaterialPropertiesDO property = propertyMap.get(item.getPropertiesId());
            String code = property != null ? property.getCode() : null;
            if (StrUtil.isBlank(code)) {
                resp.getRowErrors().add(new RowValidationErrorVO(i + 1, "属性编码不能为空"));
                continue;
            }
            if (StrUtil.isBlank(item.getValue())) {
                resp.getRowErrors().add(new RowValidationErrorVO(i + 1, "属性值不能为空"));
                continue;
            }
            if (!dedupCodes.add(code)) {
                resp.getRowErrors().add(new RowValidationErrorVO(i + 1, "同一物料下属性编码重复"));
                continue;
            }
            MaterialHasPropertiesDO entity = MaterialHasPropertiesDO.builder()
                    .infomationId(infoId)
                    .propertiesId(item.getPropertiesId())
                    .unitId(item.getUnitId())
                    .value(item.getValue())
                    .isKey(item.getIsKey())
                    .isMetering(item.getIsMetering())
                    .sort(item.getSort() == null ? (long) (i + 1) : item.getSort())
                    .deptId(deptId)
                    .build();
            materialHasPropertiesMapper.insert(entity);
        }

        return resp;
    }

    /**
     * 事务内校验：同一物料下有效属性编码唯一
     */
    private void validatePropertyCodeUnique(Long infoId, Long propertiesId, Long excludeId) {
        if (infoId == null || propertiesId == null) {
            return;
        }
        MaterialPropertiesDO property = materialPropertiesMapper.selectById(propertiesId);
        if (property == null || StrUtil.isBlank(property.getCode())) {
            return;
        }
        String code = property.getCode();
        List<MaterialPropertiesDO> sameCodeProps = materialPropertiesMapper.selectList(
                new LambdaQueryWrapperX<MaterialPropertiesDO>().eq(MaterialPropertiesDO::getCode, code));
        if (CollUtil.isEmpty(sameCodeProps)) {
            return;
        }
        Set<Long> samePropertyIds = sameCodeProps.stream()
                .map(MaterialPropertiesDO::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (samePropertyIds.isEmpty()) {
            return;
        }

        Long exists = materialHasPropertiesMapper.selectCount(new LambdaQueryWrapperX<MaterialHasPropertiesDO>()
                .eq(MaterialHasPropertiesDO::getInfomationId, infoId)
                .in(MaterialHasPropertiesDO::getPropertiesId, samePropertyIds)
                .neIfPresent(MaterialHasPropertiesDO::getId, excludeId));
        if (exists != null && exists > 0) {
            throw exception(MATERIAL_HAS_PROPERTIES_CODE_DUPLICATE);
        }
    }

}