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

import cn.hutool.core.collection.CollUtil;
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.DealerCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.DealerDestroyCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.DealerDragCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.DealerUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.DealerListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.DealerPageQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.DealerShowQry;
import com.xunhong.erp.turbo.biz.domain.entity.Dealer;
import com.xunhong.erp.turbo.biz.domain.gateway.DealerGateway;
import com.xunhong.erp.turbo.biz.infrastructure.convert.DealerConvert;
import com.xunhong.erp.turbo.biz.infrastructure.entity.DealerDO;
import com.xunhong.erp.turbo.biz.infrastructure.entity.DealerPaymentAccountDO;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.DealerMapper;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.DealerPaymentAccountMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author shenyifei
 */
@Repository
@RequiredArgsConstructor
public class DealerGatewayImpl implements DealerGateway {
	private final DealerMapper dealerMapper;
	private final DealerConvert dealerConvert;

	private final DealerPaymentAccountMapper dealerPaymentAccountMapper;

	@Override
	public Dealer save(DealerCreateCmd dealerCreateCmd) {
		DealerDO dealerDO = dealerConvert.toDealerDO(dealerCreateCmd);
		dealerMapper.insert(dealerDO);

		return dealerConvert.toDealer(dealerDO);
	}

	@Override
	public IPage<Dealer> page(DealerPageQry dealerPageQry) {
		LambdaQueryWrapper<DealerDO> queryWrapper = Wrappers.lambdaQuery(DealerDO.class);
		queryWrapper.like(StrUtil.isNotBlank(dealerPageQry.getShortName()), DealerDO::getShortName, dealerPageQry.getShortName());
		queryWrapper.like(StrUtil.isNotBlank(dealerPageQry.getFullName()), DealerDO::getFullName, dealerPageQry.getFullName());
		queryWrapper.like(StrUtil.isNotBlank(dealerPageQry.getRemark()), DealerDO::getRemark, dealerPageQry.getRemark());
		queryWrapper.eq(Objects.nonNull(dealerPageQry.getDealerType()), DealerDO::getDealerType, dealerPageQry.getDealerType());
		queryWrapper.eq(Objects.nonNull(dealerPageQry.getStatus()), DealerDO::getStatus, dealerPageQry.getStatus());

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

		IPage<DealerDO> page = new Page<>(dealerPageQry.getPageIndex(), dealerPageQry.getPageSize());
		page = dealerMapper.selectPage(page, queryWrapper);

		List<Long> dealerIdList = page.getRecords().stream().map(DealerDO::getDealerId).toList();
		if (CollUtil.isNotEmpty(dealerIdList)) {
			LambdaQueryWrapper<DealerPaymentAccountDO> queryWrapper1 = Wrappers.lambdaQuery(DealerPaymentAccountDO.class);
			queryWrapper1.in(DealerPaymentAccountDO::getDealerId, dealerIdList);
			List<DealerPaymentAccountDO> dealerPaymentAccountDOList = dealerPaymentAccountMapper.selectList(queryWrapper1);
			Map<Long, List<DealerPaymentAccountDO>> dealerPaymentAccountDOMap = dealerPaymentAccountDOList.stream().collect(Collectors.groupingBy(DealerPaymentAccountDO::getDealerId));

			page.getRecords().forEach(dealerDO -> {
				dealerDO.setDealerPaymentAccountDOList(dealerPaymentAccountDOMap.get(dealerDO.getDealerId()));
			});
		}

		return page.convert(dealerConvert::toDealer);
	}

	@Override
	public List<Dealer> list(DealerListQry dealerListQry) {
		LambdaQueryWrapper<DealerDO> queryWrapper = Wrappers.lambdaQuery(DealerDO.class);
		queryWrapper.like(StrUtil.isNotBlank(dealerListQry.getShortName()), DealerDO::getShortName, dealerListQry.getShortName());
		queryWrapper.eq(Objects.nonNull(dealerListQry.getStatus()), DealerDO::getStatus, dealerListQry.getStatus());
		queryWrapper.eq(Objects.nonNull(dealerListQry.getDealerType()), DealerDO::getDealerType, dealerListQry.getDealerType());

		queryWrapper.orderByAsc(DealerDO::getSort);
		queryWrapper.orderByDesc(DealerDO::getCreatedAt);
		List<DealerDO> dealerDOList = dealerMapper.selectList(queryWrapper);
		return dealerDOList.stream().map(dealerConvert::toDealer).toList();
	}

	@Override
	public Dealer update(DealerUpdateCmd dealerUpdateCmd) {
		LambdaQueryWrapper<DealerDO> queryWrapper = Wrappers.lambdaQuery(DealerDO.class);
		queryWrapper.eq(DealerDO::getDealerId, dealerUpdateCmd.getDealerId());
		queryWrapper.last("limit 1");

		DealerDO dealerDO = dealerMapper.selectOne(queryWrapper);

		dealerConvert.toDealerDO(dealerDO, dealerUpdateCmd);
		dealerMapper.updateById(dealerDO);

		return dealerConvert.toDealer(dealerDO);
	}

	@Override
	public Dealer show(DealerShowQry dealerShowQry) {
		LambdaQueryWrapper<DealerDO> queryWrapper = Wrappers.lambdaQuery(DealerDO.class);
		queryWrapper.eq(DealerDO::getDealerId, dealerShowQry.getDealerId());
		queryWrapper.last("limit 1");

		DealerDO dealerDO = dealerMapper.selectOne(queryWrapper);
		return dealerConvert.toDealer(dealerDO);
	}

	@Override
	public void destroy(DealerDestroyCmd dealerDestroyCmd) {
		LambdaQueryWrapper<DealerDO> queryWrapper = Wrappers.lambdaQuery(DealerDO.class);
		queryWrapper.eq(DealerDO::getDealerId, dealerDestroyCmd.getDealerId());
		queryWrapper.last("limit 1");

		DealerDO dealerDO = dealerMapper.selectOne(queryWrapper);
		dealerDO.deleteById();
	}

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

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