package tech.easyflow.skill.service.impl;

import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.skill.entity.SkillCategory;
import tech.easyflow.skill.mapper.SkillCategoryMapper;
import tech.easyflow.skill.mapper.SkillMapper;
import tech.easyflow.skill.service.SkillCategoryService;

import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * Skill 分类服务实现。
 */
@Service
public class SkillCategoryServiceImpl extends ServiceImpl<SkillCategoryMapper, SkillCategory> implements SkillCategoryService {

    private static final int MAX_LEVEL = 3;

    @Resource
    private SkillMapper skillMapper;

    /**
     * {@inheritDoc}
     */
    @Override
    public void validateUsableCategory(BigInteger categoryId) {
        if (categoryId == null) {
            return;
        }
        validateUsableCategory(requireTenantCategory(categoryId));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
    public void lockAndValidateUsableCategory(BigInteger categoryId) {
        LoginAccount account = requireAccount();
        List<SkillCategory> lockedCategories = lockTenantTree(account.getTenantId());
        if (categoryId == null) {
            return;
        }
        SkillCategory category = lockedCategories == null
                ? requireTenantCategory(categoryId)
                : requireLockedCategory(lockedCategories, categoryId, account.getTenantId());
        validateUsableCategory(category);
    }

    /**
     * 校验已按租户边界读取的分类状态。
     *
     * @param category Skill 分类
     * @throws BusinessException 分类层级超限或已停用
     */
    private void validateUsableCategory(SkillCategory category) {
        if (category.getLevelNo() != null && category.getLevelNo() > MAX_LEVEL) {
            throw new BusinessException("Skill 分类最多支持三级");
        }
        if (category.getStatus() != null && category.getStatus() != 1) {
            throw new BusinessException("Skill 分类不可用");
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(SkillCategory entity) {
        LoginAccount account = requireAccount();
        List<SkillCategory> lockedCategories = lockTenantTree(account.getTenantId());
        applyCategoryFields(entity, lockedCategories);
        assertUniqueCategoryName(entity);
        try {
            return super.save(entity);
        } catch (DuplicateKeyException exception) {
            throw new BusinessException(409, 4094, "同级分类下已存在同名 Skill 分类");
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(SkillCategory entity) {
        LoginAccount account = requireAccount();
        List<SkillCategory> lockedCategories = lockTenantTree(account.getTenantId());
        SkillCategory before = entity == null || entity.getId() == null ? null
                : (lockedCategories == null
                    ? copyCategoryState(entity)
                    : requireLockedCategory(lockedCategories, entity.getId(), account.getTenantId()));
        if (before == null) {
            throw new BusinessException("Skill 分类不存在");
        }
        if (getMapper() != null) {
            entity.setTenantId(before.getTenantId());
            entity.setCreated(before.getCreated());
            entity.setCreatedBy(before.getCreatedBy());
        }
        applyCategoryFields(entity, lockedCategories);
        assertUniqueCategoryName(entity);
        List<SkillCategory> descendants = entity.getId() == null ? List.of()
                : lockedCategories == null
                    ? listDescendants(entity.getId())
                    : listDescendants(lockedCategories, entity.getId());
        int previousLevel = before == null || before.getLevelNo() == null ? entity.getLevelNo() : before.getLevelNo();
        int levelDelta = entity.getLevelNo() - previousLevel;
        int deepestLevel = descendants.stream()
                .map(SkillCategory::getLevelNo)
                .filter(java.util.Objects::nonNull)
                .mapToInt(Integer::intValue)
                .max().orElse(previousLevel) + levelDelta;
        if (deepestLevel > MAX_LEVEL) {
            throw new BusinessException("移动后子分类将超过三级限制");
        }
        boolean updated;
        try {
            updated = getMapper() == null || getMapper().updateByQuery(entity, QueryWrapper.create()
                    .eq(SkillCategory::getId, entity.getId())
                    .eq(SkillCategory::getTenantId, account.getTenantId())) == 1;
        } catch (DuplicateKeyException exception) {
            throw new BusinessException(409, 4094, "同级分类下已存在同名 Skill 分类");
        }
        if (!updated) {
            return false;
        }
        updateDescendantPaths(entity, before, descendants, levelDelta);
        return true;
    }

    /**
     * 判断分类下是否存在子分类。
     *
     * @param categoryId 分类 ID
     * @return 存在子分类时返回 true
     */
    public boolean hasChildren(BigInteger categoryId) {
        if (categoryId == null) {
            return false;
        }
        return count(QueryWrapper.create()
                .eq(SkillCategory::getTenantId, requireAccount().getTenantId())
                .eq(SkillCategory::getParentId, categoryId)) > 0;
    }

    /**
     * 删除分类前在服务层校验子分类和 Skill 占用，避免绕过控制器。
     *
     * @param id 分类 ID
     * @return 删除结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeById(Serializable id) {
        BigInteger categoryId;
        try {
            categoryId = id instanceof BigInteger value ? value : new BigInteger(String.valueOf(id));
        } catch (RuntimeException exception) {
            throw new BusinessException("Skill 分类 ID 格式不正确");
        }
        LoginAccount account = requireAccount();
        List<SkillCategory> lockedCategories = lockTenantTree(account.getTenantId());
        if (lockedCategories == null) {
            requireTenantCategory(categoryId);
        } else {
            requireLockedCategory(lockedCategories, categoryId, account.getTenantId());
        }
        boolean occupiedByChildren = lockedCategories == null
                ? hasChildren(categoryId)
                : lockedCategories.stream().anyMatch(category -> categoryId.equals(category.getParentId()));
        if (occupiedByChildren) {
            throw new BusinessException("请先删除子分类");
        }
        if (skillMapper != null && skillMapper.selectCountByQuery(
                QueryWrapper.create().eq("tenant_id", account.getTenantId())
                        .eq("category_id", categoryId)) > 0) {
            throw new BusinessException("请先迁移或删除该分类下的 Skill");
        }
        return getMapper() == null || getMapper().deleteByQuery(QueryWrapper.create()
                .eq(SkillCategory::getId, categoryId)
                .eq(SkillCategory::getTenantId, account.getTenantId())) == 1;
    }

    /**
     * 批量删除时逐项执行分类占用约束。
     *
     * @param ids 分类 ID 集合
     * @return 全部删除成功时为 true
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByIds(Collection<? extends Serializable> ids) {
        if (ids == null || ids.isEmpty()) {
            return false;
        }
        for (Serializable id : ids) {
            removeById(id);
        }
        return true;
    }

    /**
     * 应用分类字段，并优先从当前事务已锁定的分类树解析父级。
     *
     * @param category 待保存分类
     * @param lockedCategories 已锁定分类树；无数据库 Mapper 的单元场景可为 null
     */
    private void applyCategoryFields(SkillCategory category, List<SkillCategory> lockedCategories) {
        if (category == null) {
            throw new BusinessException("Skill 分类不能为空");
        }
        if (category.getCategoryName() == null || category.getCategoryName().isBlank()) {
            throw new BusinessException("Skill 分类名称不能为空");
        }
        category.setCategoryName(category.getCategoryName().trim());
        if (category.getCategoryName().length() > 128) {
            throw new BusinessException("Skill 分类名称不能超过 128 个字符");
        }
        SkillCategory parent = null;
        if (category.getParentId() != null) {
            LoginAccount account = requireAccount();
            parent = lockedCategories == null
                    ? requireTenantCategory(category.getParentId())
                    : requireLockedCategory(lockedCategories, category.getParentId(), account.getTenantId());
            if (category.getId() != null && category.getId().equals(category.getParentId())) {
                throw new BusinessException("父级分类不能是自身");
            }
            if (category.getId() != null && containsAncestor(parent.getAncestors(), category.getId())) {
                throw new BusinessException("父级分类不能是当前分类的后代");
            }
        }
        int level = parent == null ? 1 : (parent.getLevelNo() == null ? 1 : parent.getLevelNo()) + 1;
        if (level > MAX_LEVEL) {
            throw new BusinessException("Skill 分类最多支持三级");
        }
        LoginAccount account = requireAccount();
        Date now = new Date();
        category.setLevelNo(level);
        category.setAncestors(parent == null ? "" : appendAncestor(parent));
        category.setStatus(category.getStatus() == null ? 1 : category.getStatus());
        if (category.getStatus() != 0 && category.getStatus() != 1) {
            throw new BusinessException("Skill 分类状态只支持 0 或 1");
        }
        category.setSortNo(category.getSortNo() == null ? 0 : category.getSortNo());
        if (category.getSortNo() < -999_999 || category.getSortNo() > 999_999) {
            throw new BusinessException("Skill 分类排序值超出允许范围");
        }
        if (category.getId() == null) {
            category.setTenantId(account.getTenantId());
            category.setCreated(now);
            category.setCreatedBy(account.getId());
        }
        category.setModified(now);
        category.setModifiedBy(account.getId());
    }

    private String appendAncestor(SkillCategory parent) {
        String ancestors = parent.getAncestors() == null || parent.getAncestors().isBlank()
                ? String.valueOf(parent.getId())
                : parent.getAncestors() + "," + parent.getId();
        if (ancestors.length() > 512) {
            throw new BusinessException("Skill 分类层级路径过长");
        }
        return ancestors;
    }

    private List<SkillCategory> listDescendants(BigInteger categoryId) {
        return list(QueryWrapper.create()
                .eq(SkillCategory::getTenantId, requireAccount().getTenantId())
                .and("FIND_IN_SET(?, ancestors)", categoryId));
    }

    /**
     * 从已锁定分类树中提取当前分类的全部后代。
     *
     * @param lockedCategories 已锁定分类树
     * @param categoryId 当前分类 ID
     * @return 后代分类列表
     */
    private List<SkillCategory> listDescendants(List<SkillCategory> lockedCategories, BigInteger categoryId) {
        return lockedCategories.stream()
                .filter(category -> containsAncestor(category.getAncestors(), categoryId))
                .toList();
    }

    private void updateDescendantPaths(SkillCategory category,
                                       SkillCategory before,
                                       List<SkillCategory> descendants,
                                       int levelDelta) {
        if (descendants.isEmpty()) {
            return;
        }
        String oldPrefix = before == null || before.getAncestors() == null || before.getAncestors().isBlank()
                ? String.valueOf(category.getId()) : before.getAncestors() + "," + category.getId();
        String newPrefix = category.getAncestors() == null || category.getAncestors().isBlank()
                ? String.valueOf(category.getId()) : category.getAncestors() + "," + category.getId();
        for (SkillCategory descendant : descendants) {
            String ancestors = descendant.getAncestors();
            if (ancestors == null || (!ancestors.equals(oldPrefix) && !ancestors.startsWith(oldPrefix + ","))) {
                throw new BusinessException(500, 500, "Skill 分类层级数据异常，请联系管理员处理");
            }
            descendant.setAncestors(newPrefix + ancestors.substring(oldPrefix.length()));
            descendant.setLevelNo(descendant.getLevelNo() + levelDelta);
            descendant.setModified(category.getModified());
            descendant.setModifiedBy(category.getModifiedBy());
            if (getMapper().updateByQuery(descendant, QueryWrapper.create()
                    .eq(SkillCategory::getId, descendant.getId())
                    .eq(SkillCategory::getTenantId, category.getTenantId())) != 1) {
                throw new BusinessException(500, 500, "更新 Skill 子分类层级失败，请稍后重试");
            }
        }
    }

    private boolean containsAncestor(String ancestors, BigInteger categoryId) {
        if (ancestors == null || ancestors.isBlank()) {
            return false;
        }
        String expected = String.valueOf(categoryId);
        for (String ancestor : ancestors.split(",")) {
            if (expected.equals(ancestor.trim())) {
                return true;
            }
        }
        return false;
    }

    private SkillCategory copyCategoryState(SkillCategory source) {
        SkillCategory copy = new SkillCategory();
        copy.setId(source.getId());
        copy.setParentId(source.getParentId());
        copy.setLevelNo(source.getLevelNo());
        copy.setAncestors(source.getAncestors());
        copy.setTenantId(source.getTenantId());
        return copy;
    }

    private SkillCategory requireTenantCategory(BigInteger categoryId) {
        if (categoryId == null) {
            throw new BusinessException("Skill 分类 ID 不能为空");
        }
        LoginAccount account = requireAccount();
        SkillCategory category = getMapper() == null ? getById(categoryId) : getOne(QueryWrapper.create()
                .eq(SkillCategory::getId, categoryId)
                .eq(SkillCategory::getTenantId, account.getTenantId()));
        if (category == null || !account.getTenantId().equals(category.getTenantId())) {
            throw new BusinessException(404, 404, "Skill 分类不存在");
        }
        return category;
    }

    /**
     * 锁定租户完整分类树。所有结构写操作都使用同一锁顺序，防止并发移动形成循环或孤儿节点。
     *
     * @param tenantId 租户 ID
     * @return 已锁定分类树；无 Mapper 的隔离单元场景返回 null
     */
    private List<SkillCategory> lockTenantTree(BigInteger tenantId) {
        if (getMapper() == null) {
            return null;
        }
        List<SkillCategory> categories = getMapper().selectTenantTreeForUpdate(tenantId);
        return categories == null ? List.of() : categories;
    }

    /**
     * 从已锁定分类树中读取同租户分类。
     *
     * @param lockedCategories 已锁定分类树
     * @param categoryId 分类 ID
     * @param tenantId 租户 ID
     * @return 分类实体
     */
    private SkillCategory requireLockedCategory(List<SkillCategory> lockedCategories,
                                                BigInteger categoryId,
                                                BigInteger tenantId) {
        if (categoryId == null) {
            throw new BusinessException("Skill 分类 ID 不能为空");
        }
        return lockedCategories.stream()
                .filter(category -> categoryId.equals(category.getId()) && tenantId.equals(category.getTenantId()))
                .findFirst()
                .orElseThrow(() -> new BusinessException(404, 404, "Skill 分类不存在"));
    }

    private void assertUniqueCategoryName(SkillCategory category) {
        QueryWrapper query = QueryWrapper.create()
                .eq(SkillCategory::getTenantId, category.getTenantId())
                .eq(SkillCategory::getCategoryName, category.getCategoryName());
        if (category.getParentId() == null) {
            query.isNull(SkillCategory::getParentId);
        } else {
            query.eq(SkillCategory::getParentId, category.getParentId());
        }
        if (category.getId() != null) {
            query.ne(SkillCategory::getId, category.getId());
        }
        if (getMapper() != null && count(query) > 0) {
            throw new BusinessException(409, 4094, "同级分类下已存在同名 Skill 分类");
        }
    }

    private LoginAccount requireAccount() {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        if (account == null || account.getId() == null || account.getTenantId() == null) {
            throw new BusinessException(401, 401, "未登录或登录态无效");
        }
        return account;
    }
}
