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

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
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.biz.dto.cmd.CostCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.CostDestroyCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.CostDragCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.CostUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.CostListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.CostPageQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.CostShowQry;
import com.xunhong.erp.turbo.biz.domain.entity.Cost;
import com.xunhong.erp.turbo.biz.domain.gateway.CostGateway;
import com.xunhong.erp.turbo.biz.infrastructure.convert.CostConvert;
import com.xunhong.erp.turbo.biz.infrastructure.entity.CostDO;
import com.xunhong.erp.turbo.biz.infrastructure.entity.CostItemDO;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.CostItemMapper;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.CostMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author shenyifei
 */
@Repository
@RequiredArgsConstructor
public class CostGatewayImpl implements CostGateway {
	private final CostMapper costMapper;
	private final CostConvert costConvert;

	private final CostItemMapper costItemMapper;

	@Override
	public Cost save(CostCreateCmd costCreateCmd) {
		CostDO costDO = costConvert.toCostDO(costCreateCmd);
		costMapper.insert(costDO);

		return costConvert.toCost(costDO);
	}

	@Override
	public IPage<Cost> page(CostPageQry costPageQry) {
		LambdaQueryWrapper<CostDO> queryWrapper = Wrappers.lambdaQuery(CostDO.class);
		queryWrapper.eq(Objects.nonNull(costPageQry.getType()), CostDO::getType, costPageQry.getType());
		queryWrapper.eq(Objects.nonNull(costPageQry.getBelong()), CostDO::getBelong, costPageQry.getBelong());
		queryWrapper.eq(Objects.nonNull(costPageQry.getCostId()), CostDO::getCostId, costPageQry.getCostId());

		queryWrapper.orderByAsc(CostDO::getType);
		queryWrapper.orderByAsc(CostDO::getSort);
		queryWrapper.orderByDesc(CostDO::getCreatedAt);

		IPage<CostDO> page = new Page<>(costPageQry.getPageIndex(), costPageQry.getPageSize());
		page = costMapper.selectPage(page, queryWrapper);


		Set<Long> costItemIdList = page.getRecords().stream().map(CostDO::getCostItemIds).filter(CollUtil::isNotEmpty).flatMap(List::stream).collect(Collectors.toSet());
		if (CollUtil.isNotEmpty(costItemIdList)) {
			LambdaQueryWrapper<CostItemDO> queryWrapper1 = Wrappers.lambdaQuery(CostItemDO.class);
			queryWrapper1.in(CostItemDO::getCostItemId, costItemIdList);
			List<CostItemDO> costItemDOList = costItemMapper.selectList(queryWrapper1);

			page.getRecords().forEach(productDO -> {
				if (CollUtil.isNotEmpty(productDO.getCostItemIds())) {
					productDO.setCostItemDOList(costItemDOList.stream().filter(costItemDO -> productDO.getCostItemIds().contains(costItemDO.getCostItemId())).toList());
				}
			});
		}

		return page.convert(costConvert::toCost);
	}

	@Override
	public List<Cost> list(CostListQry costListQry) {
		LambdaQueryWrapper<CostDO> queryWrapper = Wrappers.lambdaQuery(CostDO.class);
		queryWrapper.eq(Objects.nonNull(costListQry.getType()), CostDO::getType, costListQry.getType());
		queryWrapper.eq(Objects.nonNull(costListQry.getBelong()), CostDO::getBelong, costListQry.getBelong());
		queryWrapper.eq(Objects.nonNull(costListQry.getCostId()), CostDO::getCostId, costListQry.getCostId());
		queryWrapper.eq(Objects.nonNull(costListQry.getStatus()), CostDO::getStatus, costListQry.getStatus());
		queryWrapper.like(Objects.nonNull(costListQry.getName()), CostDO::getName, costListQry.getName());

		queryWrapper.orderByAsc(CostDO::getType);
		queryWrapper.orderByAsc(CostDO::getSort);
		queryWrapper.orderByDesc(CostDO::getCreatedAt);
		List<CostDO> costDOList = costMapper.selectList(queryWrapper);

		Set<Long> costItemIdList = costDOList.stream().map(CostDO::getCostItemIds).filter(CollUtil::isNotEmpty).flatMap(List::stream).collect(Collectors.toSet());
		if (CollUtil.isNotEmpty(costItemIdList)) {
			LambdaQueryWrapper<CostItemDO> queryWrapper1 = Wrappers.lambdaQuery(CostItemDO.class);
			queryWrapper1.in(CostItemDO::getCostItemId, costItemIdList);
			List<CostItemDO> costItemDOList = costItemMapper.selectList(queryWrapper1);
			costDOList.forEach(productDO -> {
				if (CollUtil.isNotEmpty(productDO.getCostItemIds())) {
					productDO.setCostItemDOList(costItemDOList.stream().filter(costItemDO -> productDO.getCostItemIds().contains(costItemDO.getCostItemId())).toList());
				}
			});
		}

		return costDOList.stream().map(costConvert::toCost).toList();
	}

	@Override
	public Cost update(CostUpdateCmd costUpdateCmd) {
		LambdaQueryWrapper<CostDO> queryWrapper = Wrappers.lambdaQuery(CostDO.class);
		queryWrapper.eq(CostDO::getCostId, costUpdateCmd.getCostId());
		queryWrapper.last("limit 1");

		CostDO costDO = costMapper.selectOne(queryWrapper);

		costConvert.toCostDO(costDO, costUpdateCmd);
		costMapper.updateById(costDO);

		return costConvert.toCost(costDO);
	}

	@Override
	public Cost show(CostShowQry costShowQry) {
		LambdaQueryWrapper<CostDO> queryWrapper = Wrappers.lambdaQuery(CostDO.class);
		queryWrapper.eq(CostDO::getCostId, costShowQry.getCostId());
		queryWrapper.last("limit 1");

		CostDO costDO = costMapper.selectOne(queryWrapper);

		if (Objects.nonNull(costDO)) {
			List<Long> costItemIds = costDO.getCostItemIds();
			if (CollUtil.isNotEmpty(costItemIds)) {
				LambdaQueryWrapper<CostItemDO> queryWrapper1 = Wrappers.lambdaQuery(CostItemDO.class);
				queryWrapper1.in(CostItemDO::getCostItemId, costItemIds);
				costDO.setCostItemDOList(costItemMapper.selectList(queryWrapper1));
			}
		}
		return costConvert.toCost(costDO);
	}

	@Override
	public void destroy(CostDestroyCmd costDestroyCmd) {
		LambdaQueryWrapper<CostDO> queryWrapper = Wrappers.lambdaQuery(CostDO.class);
		queryWrapper.eq(CostDO::getCostId, costDestroyCmd.getCostId());
		queryWrapper.last("limit 1");

		CostDO costDO = costMapper.selectOne(queryWrapper);
		costDO.deleteById();
	}

	@Transactional
	@Override
	public void drag(CostDragCmd costDragCmd) {
		reorderTask(costDragCmd.getCurrentId(), costDragCmd.getPrevId(), costDragCmd.getNextId());
	}

	// 拖拽排序核心方法
	public void reorderTask(Long currentId, Long prevId, Long nextId) {
		// 获取相邻元素的sort值
		Double prevSort = Optional.ofNullable(prevId)
			.map(id -> costMapper.selectById(id).getSort().doubleValue())
			.orElse(null);
		Double nextSort = Optional.ofNullable(nextId)
			.map(id -> costMapper.selectById(id).getSort().doubleValue())
			.orElse(null);
		// 计算新sort值
		Double newSort = calculateNewSort(prevSort, nextSort);
		// 检查是否需要重整排序
		if (needResetSort(prevSort, nextSort)) {
			costMapper.batchResetSort(costMapper.selectById(currentId).getCostId());
		} else {
			// 更新当前任务sort值
			costMapper.update(null, new LambdaUpdateWrapper<CostDO>()
				.eq(CostDO::getCostId, currentId)
				.set(CostDO::getSort, newSort)
			);
		}
	}

	private Double calculateNewSort(Double prevSort, Double nextSort) {
		if (prevSort == null && nextSort != null) {
			return nextSort - 1000; // 插入到开头
		} else if (nextSort == null && prevSort != null) {
			return prevSort + 1000; // 插入到末尾
		} else if (prevSort == null && nextSort == null) {
			return 0.0;
		} else {
			return (prevSort + nextSort) / 2; // 插入中间
		}
	}

	private boolean needResetSort(Double prevSort, Double nextSort) {
		return prevSort != null && nextSort != null
			&& (nextSort - prevSort) < 1.0; // 判断间隙是否耗尽
	}
}

