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.BoxBrandCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.BoxBrandDestroyCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.BoxBrandDragCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.BoxBrandUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.BoxBrandListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.BoxBrandPageQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.BoxBrandShowQry;
import com.xunhong.erp.turbo.biz.domain.entity.BoxBrand;
import com.xunhong.erp.turbo.biz.domain.gateway.BoxBrandGateway;
import com.xunhong.erp.turbo.biz.infrastructure.convert.BoxBrandConvert;
import com.xunhong.erp.turbo.biz.infrastructure.entity.BoxBrandDO;
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.BoxBrandMapper;
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.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author shenyifei
 */
@Repository
@RequiredArgsConstructor
public class BoxBrandGatewayImpl implements BoxBrandGateway {
	private final BoxBrandMapper boxBrandMapper;
	private final BoxBrandConvert boxBrandConvert;
	private final BoxProductMapper boxProductMapper;
	private final BoxSpecMapper boxSpecMapper;

	@Override
	public BoxBrand save(BoxBrandCreateCmd boxBrandCreateCmd) {
		BoxBrandDO boxBrandDO = boxBrandConvert.toBoxBrandDO(boxBrandCreateCmd);
		boxBrandMapper.insert(boxBrandDO);

		return boxBrandConvert.toBoxBrand(boxBrandDO);
	}

	@Override
	public IPage<BoxBrand> page(BoxBrandPageQry boxBrandPageQry) {
		LambdaQueryWrapper<BoxBrandDO> queryWrapper = Wrappers.lambdaQuery(BoxBrandDO.class);
		queryWrapper.eq(Objects.nonNull(boxBrandPageQry.getStatus()), BoxBrandDO::getStatus, boxBrandPageQry.getStatus());
		queryWrapper.eq(Objects.nonNull(boxBrandPageQry.getBrandId()), BoxBrandDO::getBrandId, boxBrandPageQry.getBrandId());
		queryWrapper.eq(Objects.nonNull(boxBrandPageQry.getType()), BoxBrandDO::getType, boxBrandPageQry.getType());
		queryWrapper.eq(Objects.nonNull(boxBrandPageQry.getStatus()), BoxBrandDO::getStatus, boxBrandPageQry.getStatus());

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

		IPage<BoxBrandDO> page = new Page<>(boxBrandPageQry.getPageIndex(), boxBrandPageQry.getPageSize());
		page = boxBrandMapper.selectPage(page, queryWrapper);

		List<Long> brandIdList = page.getRecords().stream().map(BoxBrandDO::getBrandId).toList();
		if (CollUtil.isNotEmpty(brandIdList)) {
			LambdaQueryWrapper<BoxProductDO> queryWrapper1 = Wrappers.lambdaQuery(BoxProductDO.class);
			queryWrapper1.in(BoxProductDO::getBrandId, brandIdList);
			List<BoxProductDO> boxProductDOList = boxProductMapper.selectList(queryWrapper1);

			page.getRecords().forEach(boxBrandDO -> {
				boxBrandDO.setBoxProductDOList(boxProductDOList.stream()
					.filter(boxProductDO -> boxProductDO.getBrandId().equals(boxBrandDO.getBrandId()))
					.toList());
			});
		}

		return page.convert(boxBrandConvert::toBoxBrand);
	}

	@Override
	public List<BoxBrand> list(BoxBrandListQry boxBrandListQry) {
		LambdaQueryWrapper<BoxBrandDO> queryWrapper = Wrappers.lambdaQuery(BoxBrandDO.class);
		queryWrapper.eq(Objects.nonNull(boxBrandListQry.getStatus()), BoxBrandDO::getStatus, boxBrandListQry.getStatus());
		queryWrapper.eq(Objects.nonNull(boxBrandListQry.getBrandId()), BoxBrandDO::getBrandId, boxBrandListQry.getBrandId());
		queryWrapper.eq(Objects.nonNull(boxBrandListQry.getType()), BoxBrandDO::getType, boxBrandListQry.getType());
		queryWrapper.eq(Objects.nonNull(boxBrandListQry.getStatus()), BoxBrandDO::getStatus, boxBrandListQry.getStatus());

		queryWrapper.orderByAsc(BoxBrandDO::getSort);
		queryWrapper.orderByDesc(BoxBrandDO::getCreatedAt);
		List<BoxBrandDO> boxBrandDOList = boxBrandMapper.selectList(queryWrapper);

		if (Objects.nonNull(boxBrandListQry.getWithProduct()) && boxBrandListQry.getWithProduct()) {
			List<Long> brandIdList = boxBrandDOList.stream().map(BoxBrandDO::getBrandId).toList();
			if (CollUtil.isNotEmpty(brandIdList)) {
				LambdaQueryWrapper<BoxProductDO> queryWrapper1 = Wrappers.lambdaQuery(BoxProductDO.class);
				queryWrapper1.in(BoxProductDO::getBrandId, brandIdList);
				queryWrapper1.orderByDesc(BoxProductDO::getCreatedAt);
				List<BoxProductDO> boxProductDOList = boxProductMapper.selectList(queryWrapper1);
				boxBrandDOList.forEach(boxBrandDO -> {
					boxBrandDO.setBoxProductDOList(boxProductDOList.stream().filter(boxProductDO -> boxProductDO.getBrandId().equals(boxBrandDO.getBrandId())).toList());
				});
			}

			List<Long> specIdList = boxBrandDOList.stream().map(BoxBrandDO::getSpecIds).flatMap(Collection::stream).toList();
			if (CollUtil.isNotEmpty(specIdList)) {
				LambdaQueryWrapper<BoxSpecDO> queryWrapper2 = Wrappers.lambdaQuery(BoxSpecDO.class);
				queryWrapper2.in(BoxSpecDO::getSpecId, specIdList);
				List<BoxSpecDO> boxSpecDOList = boxSpecMapper.selectList(queryWrapper2);
				boxBrandDOList.forEach(boxBrandDO -> {
					boxBrandDO.setBoxSpecDOList(boxSpecDOList.stream().filter(boxSpecDO -> boxBrandDO.getSpecIds().contains(boxSpecDO.getSpecId())).toList());
				});
			}
		}

		return boxBrandDOList.stream().map(boxBrandConvert::toBoxBrand).toList();
	}

	@Override
	public BoxBrand update(BoxBrandUpdateCmd boxBrandUpdateCmd) {
		LambdaQueryWrapper<BoxBrandDO> queryWrapper = Wrappers.lambdaQuery(BoxBrandDO.class);
		queryWrapper.eq(BoxBrandDO::getBrandId, boxBrandUpdateCmd.getBrandId());
		queryWrapper.last("limit 1");

		BoxBrandDO boxBrandDO = boxBrandMapper.selectOne(queryWrapper);

		boxBrandConvert.toBoxBrandDO(boxBrandDO, boxBrandUpdateCmd);
		boxBrandMapper.updateById(boxBrandDO);

		return boxBrandConvert.toBoxBrand(boxBrandDO);
	}

	@Override
	public BoxBrand show(BoxBrandShowQry boxBrandShowQry) {
		LambdaQueryWrapper<BoxBrandDO> queryWrapper = Wrappers.lambdaQuery(BoxBrandDO.class);
		queryWrapper.eq(BoxBrandDO::getBrandId, boxBrandShowQry.getBrandId());
		queryWrapper.last("limit 1");

		BoxBrandDO boxBrandDO = boxBrandMapper.selectOne(queryWrapper);

		if (Objects.nonNull(boxBrandDO)) {
			boxBrandDO.setBoxSpecDOList(boxSpecMapper.selectList(Wrappers.lambdaQuery(BoxSpecDO.class).in(BoxSpecDO::getSpecId, boxBrandDO.getSpecIds())));
		}

		return boxBrandConvert.toBoxBrand(boxBrandDO);
	}

	@Override
	public void destroy(BoxBrandDestroyCmd boxBrandDestroyCmd) {
		LambdaQueryWrapper<BoxBrandDO> queryWrapper = Wrappers.lambdaQuery(BoxBrandDO.class);
		queryWrapper.eq(BoxBrandDO::getBrandId, boxBrandDestroyCmd.getBrandId());
		queryWrapper.last("limit 1");

		BoxBrandDO boxBrandDO = boxBrandMapper.selectOne(queryWrapper);
		boxBrandDO.deleteById();
	}

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

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