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.BoxProductCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.BoxProductDestroyCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.BoxProductDragCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.BoxProductUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.BoxProductListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.BoxProductPageQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.BoxProductShowQry;
import com.xunhong.erp.turbo.biz.domain.entity.BoxProduct;
import com.xunhong.erp.turbo.biz.domain.gateway.BoxProductGateway;
import com.xunhong.erp.turbo.biz.infrastructure.convert.BoxProductConvert;
import com.xunhong.erp.turbo.biz.infrastructure.entity.BoxProductDO;
import com.xunhong.erp.turbo.biz.infrastructure.entity.BoxSpecDO;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.BoxProductMapper;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.BoxSpecMapper;
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 BoxProductGatewayImpl implements BoxProductGateway {
	private final BoxProductMapper boxProductMapper;
	private final BoxProductConvert boxProductConvert;
	private final BoxSpecMapper boxSpecMapper;

	@Override
	public BoxProduct save(BoxProductCreateCmd boxProductCreateCmd) {
		BoxProductDO boxProductDO = boxProductConvert.toBoxProductDO(boxProductCreateCmd);

		BoxSpecDO boxSpecDO = boxSpecMapper.selectById(boxProductDO.getSpecId());
		boxProductDO.setSpecName(boxSpecDO.getName());
		boxProductMapper.insert(boxProductDO);

		return boxProductConvert.toBoxProduct(boxProductDO);
	}

	@Override
	public IPage<BoxProduct> page(BoxProductPageQry boxProductPageQry) {
		LambdaQueryWrapper<BoxProductDO> queryWrapper = Wrappers.lambdaQuery(BoxProductDO.class);
		queryWrapper.like(StrUtil.isNotBlank(boxProductPageQry.getName()), BoxProductDO::getName, boxProductPageQry.getName());
		queryWrapper.eq(Objects.nonNull(boxProductPageQry.getSpecId()), BoxProductDO::getSpecId, boxProductPageQry.getSpecId());
		queryWrapper.eq(Objects.nonNull(boxProductPageQry.getBrandId()), BoxProductDO::getBrandId, boxProductPageQry.getBrandId());
		queryWrapper.eq(Objects.nonNull(boxProductPageQry.getStatus()), BoxProductDO::getStatus, boxProductPageQry.getStatus());
		queryWrapper.eq(Objects.nonNull(boxProductPageQry.getType()), BoxProductDO::getType, boxProductPageQry.getType());

		queryWrapper.orderByAsc(BoxProductDO::getSort);
		queryWrapper.orderByDesc(BoxProductDO::getCreatedAt);

		IPage<BoxProductDO> page = new Page<>(boxProductPageQry.getPageIndex(), boxProductPageQry.getPageSize());
		page = boxProductMapper.selectPage(page, queryWrapper);

		return page.convert(boxProductConvert::toBoxProduct);
	}

	@Override
	public List<BoxProduct> list(BoxProductListQry boxProductListQry) {
		LambdaQueryWrapper<BoxProductDO> queryWrapper = Wrappers.lambdaQuery(BoxProductDO.class);
		queryWrapper.like(StrUtil.isNotBlank(boxProductListQry.getName()), BoxProductDO::getName, boxProductListQry.getName());
		queryWrapper.eq(Objects.nonNull(boxProductListQry.getSpecId()), BoxProductDO::getSpecId, boxProductListQry.getSpecId());
		queryWrapper.eq(Objects.nonNull(boxProductListQry.getBrandId()), BoxProductDO::getBrandId, boxProductListQry.getBrandId());
		queryWrapper.eq(Objects.nonNull(boxProductListQry.getStatus()), BoxProductDO::getStatus, boxProductListQry.getStatus());
		queryWrapper.eq(Objects.nonNull(boxProductListQry.getType()), BoxProductDO::getType, boxProductListQry.getType());

		queryWrapper.orderByAsc(BoxProductDO::getSort);
		queryWrapper.orderByDesc(BoxProductDO::getCreatedAt);
		List<BoxProductDO> boxProductDOList = boxProductMapper.selectList(queryWrapper);
		return boxProductDOList.stream().map(boxProductConvert::toBoxProduct).toList();
	}

	@Override
	public BoxProduct update(BoxProductUpdateCmd boxProductUpdateCmd) {
		LambdaQueryWrapper<BoxProductDO> queryWrapper = Wrappers.lambdaQuery(BoxProductDO.class);
		queryWrapper.eq(BoxProductDO::getProductId, boxProductUpdateCmd.getProductId());
		queryWrapper.last("limit 1");

		BoxProductDO boxProductDO = boxProductMapper.selectOne(queryWrapper);

		boxProductConvert.toBoxProductDO(boxProductDO, boxProductUpdateCmd);

		if (!Objects.equals(boxProductDO.getSpecId(), boxProductUpdateCmd.getSpecId())) {
			BoxSpecDO boxSpecDO = boxSpecMapper.selectById(boxProductUpdateCmd.getSpecId());
			boxProductDO.setSpecName(boxSpecDO.getName());
		}

		boxProductMapper.updateById(boxProductDO);

		return boxProductConvert.toBoxProduct(boxProductDO);
	}

	@Override
	public BoxProduct show(BoxProductShowQry boxProductShowQry) {
		LambdaQueryWrapper<BoxProductDO> queryWrapper = Wrappers.lambdaQuery(BoxProductDO.class);
		queryWrapper.eq(BoxProductDO::getProductId, boxProductShowQry.getProductId());
		queryWrapper.last("limit 1");

		BoxProductDO boxProductDO = boxProductMapper.selectOne(queryWrapper);
		return boxProductConvert.toBoxProduct(boxProductDO);
	}

	@Override
	public void destroy(BoxProductDestroyCmd boxProductDestroyCmd) {
		LambdaQueryWrapper<BoxProductDO> queryWrapper = Wrappers.lambdaQuery(BoxProductDO.class);
		queryWrapper.eq(BoxProductDO::getProductId, boxProductDestroyCmd.getProductId());
		queryWrapper.last("limit 1");

		BoxProductDO boxProductDO = boxProductMapper.selectOne(queryWrapper);
		boxProductDO.deleteById();
	}

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

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