package com.xunhong.erp.turbo.infra.infrastructure.gateway;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xunhong.erp.turbo.api.infra.dto.cmd.MaterialBatchAddCmd;
import com.xunhong.erp.turbo.api.infra.dto.cmd.MaterialCreateCmd;
import com.xunhong.erp.turbo.api.infra.dto.cmd.MaterialDestroyCmd;
import com.xunhong.erp.turbo.api.infra.dto.cmd.MaterialUpdateCmd;
import com.xunhong.erp.turbo.api.infra.dto.qry.MaterialPageQry;
import com.xunhong.erp.turbo.api.infra.dto.qry.MaterialShowQry;
import com.xunhong.erp.turbo.infra.domain.entity.Material;
import com.xunhong.erp.turbo.infra.domain.gateway.MaterialGateway;
import com.xunhong.erp.turbo.infra.infrastructure.convert.MaterialConvert;
import com.xunhong.erp.turbo.infra.infrastructure.entity.CategoryDO;
import com.xunhong.erp.turbo.infra.infrastructure.entity.MaterialDO;
import com.xunhong.erp.turbo.infra.infrastructure.mapper.CategoryMapper;
import com.xunhong.erp.turbo.infra.infrastructure.mapper.MaterialMapper;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Repository;

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


/**
 * @author shenyifei
 */
@Repository
@RequiredArgsConstructor
public class MaterialGatewayImpl implements MaterialGateway {
	private final MaterialMapper materialMapper;
	private final MaterialConvert materialConvert;
	private final SqlSessionFactory sqlSessionFactory;

	private final CategoryMapper categoryMapper;

	@Override
	public List<Material> save(MaterialBatchAddCmd materialBatchAddCmd) {
		List<MaterialCreateCmd> materialCreateCmdList = materialBatchAddCmd.getMaterialCreateCmdList();
		SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
		MaterialMapper materialMapper = sqlSession.getMapper(MaterialMapper.class);

		List<MaterialDO> materialDOList = materialCreateCmdList.stream()
			.map(materialCreateCmd -> {
				MaterialDO materialDO = materialConvert.toMaterialDO(materialCreateCmd);

				materialMapper.insert(materialDO);

				return materialDO;
			})
			.toList();

		sqlSession.commit();
		sqlSession.close();

		return materialDOList.stream().map(materialConvert::toMaterial).toList();
	}

	@Override
	public IPage<Material> page(MaterialPageQry materialPageQry) {
		LambdaQueryWrapper<MaterialDO> queryWrapper = Wrappers.lambdaQuery(MaterialDO.class);
		queryWrapper.like(StrUtil.isNotEmpty(materialPageQry.getTitle()), MaterialDO::getName, materialPageQry.getTitle());
		queryWrapper.eq(Objects.nonNull(materialPageQry.getCategoryId()), MaterialDO::getCategoryId, materialPageQry.getCategoryId());
		queryWrapper.eq(Objects.nonNull(materialPageQry.getType()), MaterialDO::getType, materialPageQry.getType());

		queryWrapper.orderByDesc(MaterialDO::getMaterialId);
		IPage<MaterialDO> page = new Page<>(materialPageQry.getPageIndex(), materialPageQry.getPageSize());
		page = materialMapper.selectPage(page, queryWrapper);

		List<Long> categoryIdList = page.getRecords().stream().map(MaterialDO::getCategoryId).toList();
		if (CollUtil.isNotEmpty(categoryIdList)) {
			LambdaQueryWrapper<CategoryDO> queryWrapper1 = Wrappers.lambdaQuery(CategoryDO.class);
			queryWrapper1.in(CategoryDO::getCategoryId, categoryIdList);
			List<CategoryDO> categoryDOList = categoryMapper.selectList(queryWrapper1);
			Map<Long, CategoryDO> categoryDOMap = categoryDOList.stream().collect(Collectors.toMap(CategoryDO::getCategoryId, Function.identity()));
			page.getRecords().forEach(materialDO -> materialDO.setCategoryDO(categoryDOMap.get(materialDO.getCategoryId())));
		}

		return page.convert(materialConvert::toMaterial);
	}

	@Override
	public Material show(MaterialShowQry materialShowQry) {
		MaterialDO materialDO = materialMapper.selectById(materialShowQry.getMaterialId());
		return materialConvert.toMaterial(materialDO);
	}

	@Override
	public Material update(MaterialUpdateCmd materialUpdateCmd) {
		MaterialDO materialDO = materialMapper.selectById(materialUpdateCmd.getMaterialId());
		materialConvert.toMaterialDO(materialDO, materialUpdateCmd);
		materialMapper.updateById(materialDO);
		return materialConvert.toMaterial(materialDO);
	}

	@Override
	public void destroy(MaterialDestroyCmd materialDestroyCmd) {
		materialMapper.deleteById(materialDestroyCmd.getMaterialId());
	}

	@Override
	public Material save(MaterialCreateCmd materialCreateCmd) {
		MaterialDO materialDO = materialConvert.toMaterialDO(materialCreateCmd);

		materialMapper.insert(materialDO);
		return materialConvert.toMaterial(materialDO);
	}
}
