/**
 * BladeX Commercial License Agreement
 * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
 * <p>
 * Use of this software is governed by the Commercial License Agreement
 * obtained after purchasing a license from BladeX.
 * <p>
 * 1. This software is for development use only under a valid license
 * from BladeX.
 * <p>
 * 2. Redistribution of this software's source code to any third party
 * without a commercial license is strictly prohibited.
 * <p>
 * 3. Licensees may copyright their own code but cannot use segments
 * from this software for such purposes. Copyright of this software
 * remains with BladeX.
 * <p>
 * Using this software signifies agreement to this License, and the software
 * must not be used for illegal purposes.
 * <p>
 * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
 * not liable for any claims arising from secondary or illegal development.
 * <p>
 * Author: Chill Zhuang (bladejava@qq.com)
 */
package org.springblade.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tenant.TenantGuard;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.node.ForestNodeMerger;
import org.springblade.core.tool.utils.*;
import org.springblade.system.cache.SysCache;
import org.springblade.system.mapper.DeptMapper;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.system.pojo.entity.User;
import org.springblade.system.pojo.vo.DeptVO;
import org.springblade.system.pojo.vo.UserVO;
import org.springblade.system.service.IDeptService;
import org.springblade.system.service.IUserService;
import org.springblade.system.wrapper.DeptWrapper;
import org.springblade.system.wrapper.UserWrapper;
import org.springblade.thirdparty.oa.constant.OAConvertConstant;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.springblade.core.tenant.TenantGuard.EntityType.DEPT;
import static org.springblade.core.tenant.TenantGuard.EntityType.DEPT_PARENT;

/**
 * 服务实现类
 *
 * @author Chill
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements IDeptService {

	private static final String TENANT_ID = "tenantId";
	private static final String PARENT_ID = "parentId";

	private final IUserService userService;

	@Override
	public Dept getDetail(Dept dept) {
		QueryWrapper<Dept> queryWrapper = Condition.getQueryWrapper(dept);
		applyTenantScope(queryWrapper);
		return getOne(queryWrapper);
	}

	@Override
	public List<DeptVO> selectList(Map<String, Object> dept) {
		QueryWrapper<Dept> queryWrapper = Condition.getQueryWrapper(dept, Dept.class);
		applyTenantScope(queryWrapper);
		return DeptWrapper.build().listNodeVO(list(queryWrapper));
	}

	@Override
	public List<DeptVO> lazyList(String tenantId, Long parentId, Map<String, Object> param) {
		// 设置租户ID
		if (AuthUtil.isAdministrator()) {
			tenantId = StringPool.EMPTY;
		}
		String paramTenantId = Func.toStr(param.get(TENANT_ID));
		if (Func.isNotEmpty(paramTenantId) && AuthUtil.isAdministrator()) {
			tenantId = paramTenantId;
		}
		// 判断点击搜索但是没有查询条件的情况
		if (Func.isEmpty(param.get(PARENT_ID)) && param.size() == 1) {
			parentId = 0L;
		}
		// 判断数据权限控制,非超管角色只可看到本级及以下数据
		if (Func.toLong(parentId) == 0L && !AuthUtil.isAdministrator()) {
			Long deptId = Func.firstLong(AuthUtil.getDeptId());
			Dept dept = SysCache.getDept(deptId);
			if (dept.getParentId() != 0) {
				parentId = dept.getParentId();
			}
		}
		// 判断点击搜索带有查询条件的情况
		if (Func.isEmpty(param.get(PARENT_ID)) && param.size() > 1 && Func.toLong(parentId) == 0L) {
			parentId = null;
		}
		return baseMapper.lazyList(tenantId, parentId, param);
	}


	@Override
	public List<DeptVO> tree(String tenantId) {
		String resolvedTenantId = AuthUtil.isAdministrator()
			? Func.toStrWithEmpty(tenantId, AuthUtil.getTenantId())
			: AuthUtil.getTenantId();
		return ForestNodeMerger.merge(baseMapper.tree(resolvedTenantId));
	}

	@Override
	public List<DeptVO> lazyTree(String tenantId, Long parentId) {
		// 仅超管可指定任意 tenantId，且空值表示查询全部；其他用户强制使用当前会话租户
		String resolvedTenantId = AuthUtil.isAdministrator()
			? Func.toStr(tenantId, StringPool.EMPTY)
			: AuthUtil.getTenantId();
		return ForestNodeMerger.merge(baseMapper.lazyTree(resolvedTenantId, parentId));
	}

	@Override
	public List<Dept> selectDept(String deptId) {
		LambdaQueryWrapper<Dept> queryWrapper = Wrappers.<Dept>lambdaQuery()
			.orderByAsc(Dept::getSort)
			.orderByAsc(Dept::getId);
		if (!AuthUtil.isAdministrator()) {
			queryWrapper.eq(Dept::getTenantId, AuthUtil.getTenantId());
		}
		List<Long> deptIds = Func.toLongList(deptId);
		if (CollectionUtil.isNotEmpty(deptIds)) {
			queryWrapper.in(Dept::getId, deptIds);
		}
		return baseMapper.selectList(queryWrapper);
	}

	@Override
	public String getDeptIds(String tenantId, String deptNames) {
		List<Dept> deptList = baseMapper.selectList(Wrappers.<Dept>query().lambda().eq(Dept::getTenantId, tenantId).in(Dept::getDeptName, Func.toStrList(deptNames)));
		if (deptList != null && deptList.size() > 0) {
			return deptList.stream().map(dept -> Func.toStr(dept.getId())).distinct().collect(Collectors.joining(","));
		}
		return null;
	}

	@Override
	public String getDeptIdsByFuzzy(String tenantId, String deptNames) {
		LambdaQueryWrapper<Dept> queryWrapper = Wrappers.<Dept>query().lambda().eq(Dept::getTenantId, tenantId);
		queryWrapper.and(wrapper -> {
			List<String> names = Func.toStrList(deptNames);
			names.forEach(name -> wrapper.like(Dept::getDeptName, name).or());
		});
		List<Dept> deptList = baseMapper.selectList(queryWrapper);
		if (deptList != null && deptList.size() > 0) {
			return deptList.stream().map(dept -> Func.toStr(dept.getId())).distinct().collect(Collectors.joining(","));
		}
		return null;
	}

	@Override
	public List<String> getDeptNames(String deptIds) {
		return baseMapper.getDeptNames(Func.toLongArray(deptIds));
	}

	@Override
	public List<Dept> getDeptChild(Long deptId) {
		return baseMapper.selectList(Wrappers.<Dept>query().lambda().like(Dept::getAncestors, deptId));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean removeDept(String ids) {
		List<Long> idList = Func.toLongList(ids);
		// 租户守卫：批量校验目标部门全部归属当前会话租户
		TenantGuard.verifyBatch(this, idList, DEPT);
		boolean containsRootDept = listByIds(idList).stream()
			.anyMatch(dept -> BladeConstant.TOP_PARENT_ID.equals(dept.getParentId()));
		if (containsRootDept) {
			throw new ServiceException("根节点不能删除!");
		}
		Long cnt = baseMapper.selectCount(Wrappers.<Dept>query().lambda().in(Dept::getParentId, idList));
		if (cnt > 0L) {
			throw new ServiceException("请先删除子节点!");
		}
		return removeByIds(idList);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean submit(Dept dept) {
		Long parentId = dept.getParentId();
		Dept parent = null;
		// 顶级语义：parentId 为 null 或 TOP_PARENT_ID (0L) 均视为顶级部门
		if (parentId == null || parentId.equals(BladeConstant.TOP_PARENT_ID)) {
			// 顶级部门：租户守卫统一处理新增 / 修改路径的租户绑定
			TenantGuard.bindTenant(this, dept, DEPT);
			dept.setParentId(BladeConstant.TOP_PARENT_ID);
			dept.setAncestors(String.valueOf(BladeConstant.TOP_PARENT_ID));
		} else {
			// 子部门：先校验自身归属（修改路径），再校验父节点归属并继承其 tenantId
			if (parentId.equals(dept.getId())) {
				throw new ServiceException("父节点不可选择自身!");
			}
			Dept self = Func.isNotEmpty(dept.getId())
				? TenantGuard.verify(this, dept.getId(), DEPT) : null;
			parent = TenantGuard.verify(this, dept.getParentId(), DEPT_PARENT);
			if (parent == null) {
				throw new ServiceException("上级部门不存在!");
			}
			// 显式禁止跨租户迁移：保护现有子树的数据一致性，self 租户为空时放行并继承父租户
			if (self != null && Func.isNotEmpty(self.getTenantId()) && !self.getTenantId().equals(parent.getTenantId())) {
				throw new ServiceException("不允许跨租户迁移部门，请联系运维处理");
			}
			dept.setTenantId(parent.getTenantId());
			dept.setAncestors(parent.getAncestors() + StringPool.COMMA + dept.getParentId());
		}
		dept.setIsDeleted(BladeConstant.DB_NOT_DELETED);
		if (Func.isEmpty(dept.getTenantId())) {
			throw new ServiceException("租户ID不能为空");
		}
		validateDeptCategory(dept, parent);
		validateDeptCode(dept, parent);
		return saveOrUpdate(dept);
	}

	private void validateDeptCategory(Dept dept, Dept parent) {
		if (parent == null) {
			throw new ServiceException("请选择上级组织");
		}
		List<Integer> allowedCategories;
		if (BladeConstant.TOP_PARENT_ID.equals(parent.getParentId())) {
			allowedCategories = "外部组织".equals(parent.getDeptName()) ? List.of(6) : List.of(1);
		} else if (Integer.valueOf(1).equals(parent.getDeptCategory())) {
			allowedCategories = List.of(2, 3);
		} else if (List.of(2, 3).contains(parent.getDeptCategory())) {
			allowedCategories = List.of(4, 5, 6);
		} else {
			throw new ServiceException("上级组织层级不支持新增下级组织");
		}
		if (!allowedCategories.contains(dept.getDeptCategory())) {
			throw new ServiceException("组织类型不符合上级组织层级规则");
		}
		if (Integer.valueOf(6).equals(dept.getDeptCategory()) && Func.isEmpty(dept.getCarrierCustomerId())) {
			throw new ServiceException("请选择承运商");
		}
	}

	private void validateDeptCode(Dept dept, Dept parent) {
		String deptCode = dept.getDeptCode();
		if (StringUtil.isBlank(deptCode)) {
			throw new ServiceException("组织编码不能为空");
		}
		deptCode = deptCode.trim();
		if (deptCode.length() > 30) {
			throw new ServiceException("组织编码不能超过30个字符");
		}
		if (parent == null || StringUtil.isBlank(parent.getDeptCode())) {
			throw new ServiceException("请选择已配置组织编码的上级组织");
		}
		String pattern = Pattern.quote(parent.getDeptCode()) + "-\\d+";
		if (!deptCode.matches(pattern)) {
			throw new ServiceException("组织编码格式应为：上级编码-分段数字");
		}
		LambdaQueryWrapper<Dept> queryWrapper = Wrappers.<Dept>lambdaQuery().eq(Dept::getDeptCode, deptCode);
		if (Func.isNotEmpty(dept.getId())) {
			queryWrapper.ne(Dept::getId, dept.getId());
		}
		if (baseMapper.selectCount(queryWrapper) > 0) {
			throw new ServiceException("组织编码已存在");
		}
		dept.setDeptCode(deptCode);
	}

	@Override
	public List<DeptVO> search(String deptName, Long parentId) {
		String tenantId = AuthUtil.getTenantId();
		LambdaQueryWrapper<Dept> queryWrapper = Wrappers.<Dept>query().lambda();
		if (Func.isNotEmpty(tenantId)) {
			queryWrapper.eq(Dept::getTenantId, tenantId);
		}
		if (Func.isNotEmpty(deptName)) {
			queryWrapper.like(Dept::getDeptName, deptName);
		}
		if (Func.isNotEmpty(parentId) && parentId > 0L) {
			queryWrapper.eq(Dept::getParentId, parentId);
		}
		List<Dept> deptList = baseMapper.selectList(queryWrapper);
		return DeptWrapper.build().listNodeVO(deptList);
	}

	@Override
	public List<UserVO> deptLeaderInfo(Long deptId) {
		// 租户守卫：校验目标部门归属当前会话租户，禁止跨租户读取部门主管信息
		Dept dept = TenantGuard.verify(this, deptId, DEPT);
		if (Func.isEmpty(dept)) {
			throw new ServiceException("部门不存在!");
		}
		if (StringUtil.isBlank(dept.getLeaderId())) {
			return new ArrayList<>();
		}
		List<Long> leaderIds = Func.toLongList(dept.getLeaderId());
		LambdaQueryWrapper<User> queryWrapper = Wrappers.<User>query().lambda()
			.in(User::getId, leaderIds)
			.eq(User::getStatus, BladeConstant.DB_STATUS_NORMAL)
			.eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED);
		// 主管列表同样限定本租户，避免污染的 leaderId 跨租户回显
		if (!AuthUtil.isAdministrator()) {
			queryWrapper.eq(User::getTenantId, AuthUtil.getTenantId());
		}
		List<User> userList = userService.list(queryWrapper);
		return UserWrapper.build().listVO(userList);
	}

	/**
	 * 统一的租户范围过滤：超管放行，其他用户强制限定为当前会话租户。
	 */
	private void applyTenantScope(QueryWrapper<Dept> queryWrapper) {
		if (!AuthUtil.isAdministrator()) {
			queryWrapper.lambda().eq(Dept::getTenantId, AuthUtil.getTenantId());
		}
	}


	@Transactional(rollbackFor = Exception.class)
	@Override
	public void updateAncestors(Date startTime) {
		// 1. 查询需要更新祖级列表的部门
		List<Dept> list = this.list(Wrappers.<Dept>lambdaQuery()
			// 祖级列表为空
			.isNull(Dept::getAncestors)
			// 开始时间不为空，查询同步时间大于开始时间或修改时间大于开始时间的数据
			.or(startTime != null, wrapper -> wrapper.ge(Dept::getSyncTime, startTime))
			.or(startTime != null, wrapper -> wrapper.ge(Dept::getUpdateTime, startTime))
		);
		if (CollectionUtil.isEmpty(list)) {
			log.info("需要更新祖级列表的部门为空");
			return;
		}
		// 所有公司部门列表
		List<DeptVO> allList = this.list().stream()
			.map(dept -> BeanUtil.copyProperties(dept, DeptVO.class))
			.toList();
		// id对应的部门map
		Map<Long, DeptVO> idDeptMap = allList.stream()
			.collect(Collectors.toMap(DeptVO::getId, Function.identity(), (a, b) -> b));
		// 获取根节点列表
		List<DeptVO> rootList = getRootList(allList, idDeptMap);
		// 计算祖级列表
		setAncestors(rootList);

		// 3. 更新祖级列表
		List<Dept> updateParams = list.stream()
			.filter(dept -> {
				DeptVO vo = idDeptMap.get(dept.getId());
				if (vo == null) {
					return false;
				}
				if (vo.getAncestors() == null) {
					return false;
				}
				// 祖级列表和数据库的不一样的才需要更新
				return !StringUtil.equals(vo.getAncestors(), dept.getAncestors());
			})
			.map(dept -> {
				Dept updateParam = new Dept();
				updateParam.setId(dept.getId());
				DeptVO vo = idDeptMap.get(dept.getId());
				updateParam.setAncestors(vo.getAncestors());
				return updateParam;
			}).toList();
		if (CollectionUtil.isNotEmpty(updateParams)) {
			this.updateBatchById(updateParams);
		}
	}

	/**
	 *  获取根节点列表
	 * @param allList
	 * @param idDeptMap
	 * @return
	 */
	private List<DeptVO> getRootList(List<DeptVO> allList, Map<Long, DeptVO> idDeptMap) {
		if (CollectionUtil.isEmpty(allList)) {
			return Collections.emptyList();
		}
		// 根节点列表
		List<DeptVO> rootList = new ArrayList<>();
		for (DeptVO vo : allList) {
			Long parentId = vo.getParentId();
			if (OAConvertConstant.ROOT_PARENT_ID.equals(parentId)) {
				// 父节点是根节点，添加到根节点列表
				rootList.add(vo);
				continue;
			}
			DeptVO parent = idDeptMap.get(parentId);
			if (parent != null) {
				// 父节点存在，放入父节点的子节点列表中
				if (parent.getChildren() == null) {
					parent.setChildren(new ArrayList<>());
				}
				parent.getChildren().add(vo);
			}
		}
		return rootList;
	}

	/**
	 *  计算祖级列表
	 * @param rootList 根节点列表
	 */
	private void setAncestors(List<DeptVO> rootList) {
		if (CollectionUtil.isEmpty(rootList)) {
			return;
		}
		// 根节点的祖级列表是 0, 最后拼接一个逗号，防止查询的时候有包含的
		String ancestors =  OAConvertConstant.ROOT_PARENT_ID + ",";
		setAncestors(rootList, ancestors);
	}

	/**
	 *  设置祖级列表
	 * @param list 节点列表
	 * @param ancestors 祖级列表
	 */
	private void setAncestors(List<DeptVO> list, String ancestors) {
		if (CollectionUtil.isEmpty(list)) {
			return;
		}
		for (DeptVO vo : list) {
			vo.setAncestors(ancestors);
			if (CollectionUtil.isNotEmpty(vo.getChildren())) {
				// 子节点列表不为空，设置子节点的祖级列表
				String newAncestors = ancestors + vo.getId() + ",";
				setAncestors(vo.getChildren(), newAncestors);
			}
		}
	}


}
