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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.zt.plat.framework.common.pojo.CommonResult;
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 com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationPageReqVO;
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationRespVO;
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationSaveReqVO;
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationSimplePageReqVO;
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationSimpleRespVO;
import com.zt.plat.module.base.dal.dao.materialclasses.MaterialClassesMapper;
import com.zt.plat.module.base.dal.dao.materialhasclasses.MaterialHasClassesMapper;
import com.zt.plat.module.base.dal.dataobject.base.MaterialInfomationDO;
import com.zt.plat.module.base.dal.dataobject.materialclasses.MaterialClassesDO;
import com.zt.plat.module.base.dal.dataobject.materialhasclasses.MaterialHasClassesDO;
import com.zt.plat.module.base.dal.mysql.base.MaterialInfomationMapper;
import com.zt.plat.module.erp.api.ErpExternalApi;
import com.zt.plat.module.erp.api.dto.ErpProductiveVersionReqDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.base.enums.ErrorCodeConstants.MATERIAL_CLASSES_LEVEL3_REQUIRED_FOR_MATERIAL;
import static com.zt.plat.module.base.enums.ErrorCodeConstants.MATERIAL_CLASSES_NOT_EXISTS;
import static com.zt.plat.module.base.enums.ErrorCodeConstants.MATERIAL_INFOMATION_NOT_EXISTS;

/**
 * 物料信息 Service 实现类
 *
 * @author 后台管理
 */
@Service
@Validated
public class MaterialInfomationServiceImpl implements MaterialInfomationService {

	@Resource
	private MaterialInfomationMapper materialInfomationMapper;

	@Resource
	private ErpExternalApi erpExternalApi;

	@Resource
	private MaterialHasClassesMapper materialHasClassesMapper;

	@Resource
	private MaterialClassesMapper materialClassesMapper;

	@Override
	@Transactional(rollbackFor = Exception.class)
	public MaterialInfomationRespVO createMaterialInfomation(MaterialInfomationSaveReqVO createReqVO) {
		validateMaterialClassForBinding(createReqVO.getClassesId());
		MaterialInfomationDO materialInfomation = BeanUtils.toBean(createReqVO, MaterialInfomationDO.class);
		materialInfomationMapper.insert(materialInfomation);
		if (createReqVO.getClassesId() != null) {
			MaterialHasClassesDO relation = MaterialHasClassesDO.builder()
					.classesId(createReqVO.getClassesId())
					.infomationId(materialInfomation.getId())
					.build();
			materialHasClassesMapper.insert(relation);
			materialInfomation.setClassesId(createReqVO.getClassesId());
		}
		return CollUtil.getFirst(buildRespList(Collections.singletonList(materialInfomation)));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateMaterialInfomation(MaterialInfomationSaveReqVO updateReqVO) {
		validateMaterialInfomationExists(updateReqVO.getId());
		validateMaterialClassForBinding(updateReqVO.getClassesId());
		MaterialInfomationDO updateObj = BeanUtils.toBean(updateReqVO, MaterialInfomationDO.class);
		materialInfomationMapper.updateById(updateObj);
		materialHasClassesMapper.delete(new LambdaUpdateWrapper<MaterialHasClassesDO>()
				.eq(MaterialHasClassesDO::getInfomationId, updateReqVO.getId()));
		if (updateReqVO.getClassesId() != null) {
			MaterialHasClassesDO relation = MaterialHasClassesDO.builder()
					.classesId(updateReqVO.getClassesId())
					.infomationId(updateReqVO.getId())
					.build();
			materialHasClassesMapper.insert(relation);
		}
	}

	@Override
	public void deleteMaterialInfomation(Long id) {
		validateMaterialInfomationExists(id);
		materialInfomationMapper.deleteById(id);
		materialHasClassesMapper.delete(new LambdaUpdateWrapper<MaterialHasClassesDO>()
				.eq(MaterialHasClassesDO::getInfomationId, id));
	}

	@Override
	public void deleteMaterialInfomationListByIds(List<Long> ids) {
		validateMaterialInfomationExists(ids);
		materialInfomationMapper.deleteByIds(ids);
	}

	private void validateMaterialInfomationExists(List<Long> ids) {
		List<MaterialInfomationDO> list = materialInfomationMapper.selectByIds(ids);
		if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
			throw exception(MATERIAL_INFOMATION_NOT_EXISTS);
		}
	}

	private void validateMaterialInfomationExists(Long id) {
		if (materialInfomationMapper.selectById(id) == null) {
			throw exception(MATERIAL_INFOMATION_NOT_EXISTS);
		}
	}

	private void validateMaterialClassForBinding(Long classesId) {
		if (classesId == null) {
			throw exception(MATERIAL_CLASSES_LEVEL3_REQUIRED_FOR_MATERIAL);
		}
		MaterialClassesDO materialClasses = materialClassesMapper.selectById(classesId);
		if (materialClasses == null) {
			throw exception(MATERIAL_CLASSES_NOT_EXISTS);
		}
		if (!isThirdLevelClass(materialClasses)) {
			throw exception(MATERIAL_CLASSES_LEVEL3_REQUIRED_FOR_MATERIAL);
		}
	}

	private boolean isThirdLevelClass(MaterialClassesDO materialClasses) {
		Long level = materialClasses.getLevel();
		if (Objects.equals(level, 3L)) {
			return true;
		}
		if (level != null && level > 3L) {
			return false;
		}
		Long depth = resolveClassDepth(materialClasses);
		return depth != null && depth == 3L;
	}

	private Long resolveClassDepth(MaterialClassesDO materialClasses) {
		long depth = 1L;
		Set<Long> visited = new HashSet<>();
		if (materialClasses.getId() != null) {
			visited.add(materialClasses.getId());
		}
		Long parentId = materialClasses.getParentId();
		while (parentId != null) {
			if (!visited.add(parentId)) {
				return null;
			}
			MaterialClassesDO parent = materialClassesMapper.selectById(parentId);
			if (parent == null) {
				return null;
			}
			depth++;
			if (depth > 3L) {
				return depth;
			}
			parentId = parent.getParentId();
		}
		return depth;
	}

	@Override
	public MaterialInfomationRespVO getMaterialInfomation(Long id) {
		MaterialInfomationDO info = materialInfomationMapper.selectById(id);
		if (info == null) {
			return null;
		}
		MaterialHasClassesDO relation = materialHasClassesMapper.selectFirstOne(MaterialHasClassesDO::getInfomationId, id);
		if (relation != null) {
			info.setClassesId(relation.getClassesId());
		}
		return CollUtil.getFirst(buildRespList(Collections.singletonList(info)));
	}

	@Override
	public PageResult<MaterialInfomationRespVO> getMaterialInfomationPage(MaterialInfomationPageReqVO pageReqVO) {
		List<Long> infomationIds = null;
		List<MaterialHasClassesDO> relationList = Collections.emptyList();
		if (pageReqVO.getClassesId() != null) {
			relationList = materialHasClassesMapper.selectList(MaterialHasClassesDO::getClassesId, pageReqVO.getClassesId());
			if (CollUtil.isEmpty(relationList)) {
				return PageResult.empty();
			}
			infomationIds = relationList.stream()
					.map(MaterialHasClassesDO::getInfomationId)
					.filter(Objects::nonNull)
					.distinct()
					.collect(Collectors.toList());
		}

		PageResult<MaterialInfomationDO> pageResult = materialInfomationMapper.selectPage(pageReqVO, infomationIds);
		if (CollUtil.isEmpty(pageResult.getList())) {
			return PageResult.empty(pageResult.getTotal());
		}

		List<Long> currentInfoIds = pageResult.getList().stream()
				.map(MaterialInfomationDO::getId)
				.collect(Collectors.toList());

		if (pageReqVO.getClassesId() == null) {
			relationList = materialHasClassesMapper.selectList(MaterialHasClassesDO::getInfomationId, currentInfoIds);
		}

		Map<Long, Long> infoClassMap = relationList.stream()
				.filter(item -> item.getInfomationId() != null)
				.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId, MaterialHasClassesDO::getClassesId, (existing, replacement) -> existing));

		pageResult.getList().forEach(item -> item.setClassesId(infoClassMap.get(item.getId())));
		List<MaterialInfomationRespVO> respList = buildRespList(pageResult.getList());
		return new PageResult<>(respList, pageResult.getTotal());
	}

	@Override
	public String getOneTest() {
		ErpProductiveVersionReqDTO reqDTO = new ErpProductiveVersionReqDTO();
		reqDTO.setFactoryNumber("5020");
		reqDTO.setMaterialNumber("224814");
		CommonResult<String> erpProductiveVersion = erpExternalApi.getErpProductiveVersionByFM(reqDTO);
		return erpProductiveVersion.getData();
	}

	@Override
	public PageResult<MaterialInfomationSimpleRespVO> getMaterialInfomationSimplePage(MaterialInfomationSimplePageReqVO pageReqVO) {
		LambdaQueryWrapperX<MaterialInfomationDO> wrapper = new LambdaQueryWrapperX<>();
		if (StrUtil.isNotBlank(pageReqVO.getKeyword())) {
			wrapper.and(w -> w.like(MaterialInfomationDO::getCode, pageReqVO.getKeyword())
					.or().like(MaterialInfomationDO::getName, pageReqVO.getKeyword()));
		}

		Map<Long, Long> infoClassMap = Collections.emptyMap();
		if (pageReqVO.getClassesId() != null) {
			List<MaterialHasClassesDO> relations = materialHasClassesMapper.selectList(MaterialHasClassesDO::getClassesId, pageReqVO.getClassesId());
			if (CollUtil.isEmpty(relations)) {
				return new PageResult<>(Collections.emptyList(), 0L);
			}
			List<Long> infoIds = relations.stream()
					.map(MaterialHasClassesDO::getInfomationId)
					.filter(Objects::nonNull)
					.distinct()
					.toList();
			if (CollUtil.isEmpty(infoIds)) {
				return new PageResult<>(Collections.emptyList(), 0L);
			}
			wrapper.in(MaterialInfomationDO::getId, infoIds);
			infoClassMap = relations.stream()
					.filter(item -> item.getInfomationId() != null)
					.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId,
							MaterialHasClassesDO::getClassesId, (existing, replacement) -> existing));
		}

		wrapper.orderByAsc(MaterialInfomationDO::getCode);

		PageResult<MaterialInfomationDO> pageResult = materialInfomationMapper.selectSimplePage(pageReqVO, wrapper);
		if (CollUtil.isEmpty(pageResult.getList())) {
			return new PageResult<>(Collections.emptyList(), pageResult.getTotal());
		}

		if (pageReqVO.getClassesId() == null) {
			List<Long> infoIds = pageResult.getList().stream().map(MaterialInfomationDO::getId).toList();
			List<MaterialHasClassesDO> relations = materialHasClassesMapper.selectList(MaterialHasClassesDO::getInfomationId, infoIds);
			infoClassMap = relations.stream()
					.filter(item -> item.getInfomationId() != null)
					.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId,
							MaterialHasClassesDO::getClassesId, (existing, replacement) -> existing));
		}

		Map<Long, Long> finalInfoClassMap = infoClassMap;
		List<MaterialInfomationSimpleRespVO> respList = pageResult.getList().stream().map(item -> {
			MaterialInfomationSimpleRespVO vo = new MaterialInfomationSimpleRespVO();
			vo.setId(item.getId());
			vo.setCode(item.getCode());
			vo.setName(item.getName());
			vo.setRemark(item.getRemark());
			vo.setClassesId(finalInfoClassMap.get(item.getId()));
			return vo;
		}).collect(Collectors.toList());

		return new PageResult<>(respList, pageResult.getTotal());
	}

	private List<MaterialInfomationRespVO> buildRespList(List<MaterialInfomationDO> list) {
		if (CollUtil.isEmpty(list)) {
			return Collections.emptyList();
		}
		return list.stream()
				.map(item -> BeanUtils.toBean(item, MaterialInfomationRespVO.class))
				.collect(Collectors.toList());
	}
}
