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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xunhong.erp.turbo.api.rbac.dto.cmd.MenuCreateCmd;
import com.xunhong.erp.turbo.api.rbac.dto.cmd.MenuDestroyCmd;
import com.xunhong.erp.turbo.api.rbac.dto.cmd.MenuDragCmd;
import com.xunhong.erp.turbo.api.rbac.dto.cmd.MenuUpdateCmd;
import com.xunhong.erp.turbo.api.rbac.dto.qry.MenuListQry;
import com.xunhong.erp.turbo.api.rbac.dto.qry.MenuShowQry;
import com.xunhong.erp.turbo.api.rbac.dto.qry.MenuTreeQry;
import com.xunhong.erp.turbo.api.rbac.exception.RbacErrorCode;
import com.xunhong.erp.turbo.api.rbac.exception.RbacException;
import com.xunhong.erp.turbo.rbac.domain.entity.Menu;
import com.xunhong.erp.turbo.rbac.domain.gateway.MenuGateway;
import com.xunhong.erp.turbo.rbac.infrastructure.convert.MenuConvert;
import com.xunhong.erp.turbo.rbac.infrastructure.entity.MenuDO;
import com.xunhong.erp.turbo.rbac.infrastructure.entity.PermissionDO;
import com.xunhong.erp.turbo.rbac.infrastructure.entity.RoleMenuDO;
import com.xunhong.erp.turbo.rbac.infrastructure.mapper.MenuMapper;
import com.xunhong.erp.turbo.rbac.infrastructure.mapper.PermissionMapper;
import com.xunhong.erp.turbo.rbac.infrastructure.mapper.RoleMenuMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author shenyifei
 */
@Repository
@RequiredArgsConstructor
public class MenuGatewayImpl implements MenuGateway {
	private final MenuMapper menuMapper;
	private final MenuConvert menuConvert;
	private final PermissionMapper permissionMapper;
	private final RoleMenuMapper roleMenuMapper;

	@Override
	public List<Menu> tree(MenuTreeQry menuTreeQry) {
		LambdaQueryWrapper<MenuDO> queryWrapper = Wrappers.lambdaQuery(MenuDO.class);
		queryWrapper.eq(Objects.nonNull(menuTreeQry.getPlatformId()), MenuDO::getPlatformId, menuTreeQry.getPlatformId());
		queryWrapper.eq(Objects.nonNull(menuTreeQry.getPid()), MenuDO::getPid, menuTreeQry.getPid());
		queryWrapper.orderByAsc(MenuDO::getSort);
		List<MenuDO> menuDOList = menuMapper.selectList(queryWrapper);
		return menuDOList.stream().map(menuConvert::toMenu).toList();
	}

	@Override
	public List<Menu> list(MenuListQry menuListQry) {
		LambdaQueryWrapper<MenuDO> queryWrapper = Wrappers.lambdaQuery(MenuDO.class);
		queryWrapper.eq(Objects.nonNull(menuListQry.getPlatformId()), MenuDO::getPlatformId, menuListQry.getPlatformId());
		queryWrapper.eq(Objects.nonNull(menuListQry.getPid()), MenuDO::getPid, menuListQry.getPid());
		queryWrapper.orderByAsc(MenuDO::getSort);
		List<MenuDO> menuDOList = menuMapper.selectList(queryWrapper);
		return menuDOList.stream().map(menuConvert::toMenu).toList();
	}

	@Override
	@Transactional
	public Menu create(MenuCreateCmd menuCreateCmd) {
		// 创建菜单
		MenuDO menuDO = menuConvert.toMenuDO(menuCreateCmd);

		MenuDO menuDO1 = menuMapper.selectOne(new LambdaQueryWrapper<MenuDO>()
			.select(MenuDO::getSort)
			.eq(MenuDO::getMenuId, menuDO.getMenuId())
			.eq(Objects.nonNull(menuCreateCmd.getPid()), MenuDO::getPid, menuCreateCmd.getPid())
			.eq(Objects.nonNull(menuCreateCmd.getPlatformId()), MenuDO::getPlatformId, menuCreateCmd.getPlatformId())
			.orderByDesc(MenuDO::getSort)
			.last("LIMIT 1")
		);

		BigDecimal maxSort = Optional.ofNullable(menuDO1).map(MenuDO::getSort).orElseGet(() -> {
			MenuDO customFieldDO2 = new MenuDO();
			customFieldDO2.setSort(BigDecimal.ZERO);
			return customFieldDO2.getSort();
		});

		menuDO.setSort(BigDecimal.valueOf(maxSort.doubleValue() + 1000));

		menuMapper.insert(menuDO);

		return menuConvert.toMenu(menuDO);
	}

	@Override
	@Transactional
	public Menu update(MenuUpdateCmd menuUpdateCmd) {
		MenuDO menuDO = menuMapper.selectById(menuUpdateCmd.getMenuId());
		menuConvert.toMenuDO(menuDO, menuUpdateCmd);
		menuMapper.updateById(menuDO);
		return menuConvert.toMenu(menuDO);
	}

	@Override
	public Menu show(MenuShowQry menuShowQry) {
		MenuDO menuDO = menuMapper.selectById(menuShowQry.getMenuId());
		PermissionDO permissionDO = permissionMapper.selectById(menuDO.getPermissionId());
		menuDO.setPermissionDO(permissionDO);
		return menuConvert.toMenu(menuDO);
	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public void destroy(MenuDestroyCmd menuDestroyCmd) {
		MenuDO menuDO = menuMapper.selectById(menuDestroyCmd.getMenuId());
		// 判断菜单是否存在
		if (Objects.isNull(menuDO)) {
			throw new RbacException(RbacErrorCode.B_RBAC_MENU_NOT_EXIST);
		}

		// 判断菜单是否被应用引用
		LambdaQueryWrapper<RoleMenuDO> queryWrapper = Wrappers.lambdaQuery(RoleMenuDO.class);
		queryWrapper.eq(RoleMenuDO::getMenuId, menuDestroyCmd.getMenuId());
		if (roleMenuMapper.selectCount(queryWrapper) > 0) {
			throw new RbacException(RbacErrorCode.B_RBAC_MENU_IN_ROLE_USE);
		}

		// 有下级菜单不能删除
		if (menuMapper.selectCount(Wrappers.lambdaQuery(MenuDO.class).eq(MenuDO::getPid, menuDestroyCmd.getMenuId())) > 0) {
			throw new RbacException(RbacErrorCode.B_RBAC_MENU_HAS_CHILDREN);
		}

		// 删除菜单
		menuMapper.deleteById(menuDO.getMenuId());
	}


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

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