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.ProductCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ProductDestroyCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ProductDragCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ProductUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.ProductListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.ProductPageQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.ProductShowQry;
import com.xunhong.erp.turbo.biz.domain.entity.Product;
import com.xunhong.erp.turbo.biz.domain.gateway.ProductGateway;
import com.xunhong.erp.turbo.biz.infrastructure.convert.ProductConvert;
import com.xunhong.erp.turbo.biz.infrastructure.entity.CostDO;
import com.xunhong.erp.turbo.biz.infrastructure.entity.ProductDO;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.CostMapper;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.ProductMapper;
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 ProductGatewayImpl implements ProductGateway {
	private final ProductMapper productMapper;
	private final ProductConvert productConvert;

	private final CostMapper costMapper;

	@Override
	public Product save(ProductCreateCmd productCreateCmd) {
		ProductDO productDO = productConvert.toProductDO(productCreateCmd);
		productMapper.insert(productDO);

		return productConvert.toProduct(productDO);
	}

	@Override
	public IPage<Product> page(ProductPageQry productPageQry) {
		LambdaQueryWrapper<ProductDO> queryWrapper = Wrappers.lambdaQuery(ProductDO.class);
		queryWrapper.eq(Objects.nonNull(productPageQry.getStatus()), ProductDO::getStatus, productPageQry.getStatus());

		queryWrapper.orderByAsc(ProductDO::getSort);
		queryWrapper.orderByDesc(ProductDO::getCreatedAt);
		IPage<ProductDO> page = new Page<>(productPageQry.getPageIndex(), productPageQry.getPageSize());
		page = productMapper.selectPage(page, queryWrapper);

		return page.convert(productConvert::toProduct);
	}

	@Override
	public List<Product> list(ProductListQry productListQry) {
		LambdaQueryWrapper<ProductDO> queryWrapper = Wrappers.lambdaQuery(ProductDO.class);
		queryWrapper.eq(Objects.nonNull(productListQry.getStatus()), ProductDO::getStatus, productListQry.getStatus());

		queryWrapper.orderByAsc(ProductDO::getSort);
		queryWrapper.orderByDesc(ProductDO::getCreatedAt);
		List<ProductDO> productDOList = productMapper.selectList(queryWrapper);

		Set<Long> costIdList = productDOList.stream().map(ProductDO::getCostIds).filter(Objects::nonNull).flatMap(List::stream).collect(Collectors.toSet());
		if (CollUtil.isNotEmpty(costIdList)) {
			LambdaQueryWrapper<CostDO> queryWrapper1 = Wrappers.lambdaQuery(CostDO.class);
			queryWrapper1.in(CostDO::getCostId, costIdList);
			List<CostDO> costDOList = costMapper.selectList(queryWrapper1);

			productDOList.forEach(productDO -> {
				productDO.setCostDOList(costDOList.stream().filter(costItemDO -> productDO.getCostIds().contains(costItemDO.getCostId())).toList());
			});
		}

		return productDOList.stream().map(productConvert::toProduct).toList();
	}

	@Override
	public Product update(ProductUpdateCmd productUpdateCmd) {
		LambdaQueryWrapper<ProductDO> queryWrapper = Wrappers.lambdaQuery(ProductDO.class);
		queryWrapper.eq(ProductDO::getProductId, productUpdateCmd.getProductId());
		queryWrapper.last("limit 1");

		ProductDO productDO = productMapper.selectOne(queryWrapper);

		productConvert.toProductDO(productDO, productUpdateCmd);
		productMapper.updateById(productDO);

		return productConvert.toProduct(productDO);
	}

	@Override
	public Product show(ProductShowQry productShowQry) {
		LambdaQueryWrapper<ProductDO> queryWrapper = Wrappers.lambdaQuery(ProductDO.class);
		queryWrapper.eq(ProductDO::getProductId, productShowQry.getProductId());
		queryWrapper.last("limit 1");

		ProductDO productDO = productMapper.selectOne(queryWrapper);

		if (Objects.nonNull(productDO)) {
			List<Long> costIds = productDO.getCostIds();
			if (CollUtil.isNotEmpty(costIds)) {
				LambdaQueryWrapper<CostDO> queryWrapper1 = Wrappers.lambdaQuery(CostDO.class);
				queryWrapper1.in(CostDO::getCostId, costIds);
				productDO.setCostDOList(costMapper.selectList(queryWrapper1));
			}
		}

		return productConvert.toProduct(productDO);
	}

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

		ProductDO productDO = productMapper.selectOne(queryWrapper);
		productDO.deleteById();
	}

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

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