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

import cn.hutool.core.util.StrUtil;
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.CostItemCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.CostItemDestroyCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.CostItemDragCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.CostItemUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.CostItemListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.CostItemPageQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.CostItemShowQry;
import com.xunhong.erp.turbo.biz.domain.entity.CostItem;
import com.xunhong.erp.turbo.biz.domain.gateway.CostItemGateway;
import com.xunhong.erp.turbo.biz.infrastructure.convert.CostItemConvert;
import com.xunhong.erp.turbo.biz.infrastructure.entity.CostItemDO;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.CostItemMapper;
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;

/**
 * @author shenyifei
 */
@Repository
@RequiredArgsConstructor
public class CostItemGatewayImpl implements CostItemGateway {
	private final CostItemMapper costItemMapper;
	private final CostItemConvert costItemConvert;

	@Override
	public CostItem save(CostItemCreateCmd costItemCreateCmd) {
		CostItemDO costItemDO = costItemConvert.toCostItemDO(costItemCreateCmd);
		costItemMapper.insert(costItemDO);

		return costItemConvert.toCostItem(costItemDO);
	}

	@Override
	public IPage<CostItem> page(CostItemPageQry costItemPageQry) {
		LambdaQueryWrapper<CostItemDO> queryWrapper = Wrappers.lambdaQuery(CostItemDO.class);
		queryWrapper.like(StrUtil.isNotBlank(costItemPageQry.getName()), CostItemDO::getName, costItemPageQry.getName());
		queryWrapper.like(StrUtil.isNotBlank(costItemPageQry.getRemark()), CostItemDO::getRemark, costItemPageQry.getRemark());
		queryWrapper.eq(Objects.nonNull(costItemPageQry.getStatus()), CostItemDO::getStatus, costItemPageQry.getStatus());
		queryWrapper.eq(Objects.nonNull(costItemPageQry.getType()), CostItemDO::getType, costItemPageQry.getType());
		queryWrapper.orderByAsc(CostItemDO::getSort);
		queryWrapper.orderByDesc(CostItemDO::getCreatedAt);

		IPage<CostItemDO> page = new Page<>(costItemPageQry.getPageIndex(), costItemPageQry.getPageSize());
		page = costItemMapper.selectPage(page, queryWrapper);

		return page.convert(costItemConvert::toCostItem);
	}

	@Override
	public List<CostItem> list(CostItemListQry costItemListQry) {
		LambdaQueryWrapper<CostItemDO> queryWrapper = Wrappers.lambdaQuery(CostItemDO.class);
		queryWrapper.eq(Objects.nonNull(costItemListQry.getStatus()), CostItemDO::getStatus, costItemListQry.getStatus());
		queryWrapper.eq(Objects.nonNull(costItemListQry.getType()), CostItemDO::getType, costItemListQry.getType());

		queryWrapper.orderByAsc(CostItemDO::getType);
		queryWrapper.orderByAsc(CostItemDO::getSort);
		queryWrapper.orderByDesc(CostItemDO::getCreatedAt);
		List<CostItemDO> costItemDOList = costItemMapper.selectList(queryWrapper);
		return costItemDOList.stream().map(costItemConvert::toCostItem).toList();
	}

	@Override
	public CostItem update(CostItemUpdateCmd costItemUpdateCmd) {
		LambdaQueryWrapper<CostItemDO> queryWrapper = Wrappers.lambdaQuery(CostItemDO.class);
		queryWrapper.eq(CostItemDO::getCostItemId, costItemUpdateCmd.getCostItemId());
		queryWrapper.last("limit 1");

		CostItemDO costItemDO = costItemMapper.selectOne(queryWrapper);

		costItemConvert.toCostItemDO(costItemDO, costItemUpdateCmd);
		costItemMapper.updateById(costItemDO);

		return costItemConvert.toCostItem(costItemDO);
	}

	@Override
	public CostItem show(CostItemShowQry costItemShowQry) {
		LambdaQueryWrapper<CostItemDO> queryWrapper = Wrappers.lambdaQuery(CostItemDO.class);
		queryWrapper.eq(CostItemDO::getCostItemId, costItemShowQry.getCostItemId());
		queryWrapper.last("limit 1");

		CostItemDO costItemDO = costItemMapper.selectOne(queryWrapper);
		return costItemConvert.toCostItem(costItemDO);
	}

	@Override
	public void destroy(CostItemDestroyCmd costItemDestroyCmd) {
		LambdaQueryWrapper<CostItemDO> queryWrapper = Wrappers.lambdaQuery(CostItemDO.class);
		queryWrapper.eq(CostItemDO::getCostItemId, costItemDestroyCmd.getCostItemId());
		queryWrapper.last("limit 1");

		CostItemDO costItemDO = costItemMapper.selectOne(queryWrapper);
		costItemDO.deleteById();
	}

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

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