package tech.easyflow.ai.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.ai.entity.DocumentCollection;
import tech.easyflow.ai.entity.FaqCategory;
import tech.easyflow.ai.entity.FaqItem;
import tech.easyflow.ai.mapper.FaqCategoryMapper;
import tech.easyflow.ai.mapper.FaqItemMapper;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.ai.service.FaqCategoryService;
import tech.easyflow.common.tree.Tree;
import tech.easyflow.common.util.StringUtil;
import tech.easyflow.common.web.exceptions.BusinessException;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class FaqCategoryServiceImpl extends ServiceImpl<FaqCategoryMapper, FaqCategory> implements FaqCategoryService {

    private static final BigInteger ROOT_PARENT_ID = BigInteger.ZERO;
    private static final ConcurrentHashMap<BigInteger, Object> DEFAULT_CATEGORY_LOCKS = new ConcurrentHashMap<>();

    @Resource
    private DocumentCollectionService documentCollectionService;

    @Resource
    private FaqItemMapper faqItemMapper;

    @Override
    @Transactional
    public BigInteger ensureDefaultCategory(BigInteger collectionId) {
        Object lock = DEFAULT_CATEGORY_LOCKS.computeIfAbsent(collectionId, key -> new Object());
        synchronized (lock) {
            try {
                return ensureDefaultCategoryWithLock(collectionId);
            } finally {
                DEFAULT_CATEGORY_LOCKS.remove(collectionId, lock);
            }
        }
    }

    private BigInteger ensureDefaultCategoryWithLock(BigInteger collectionId) {
        DocumentCollection collection = checkFaqCollection(collectionId);

        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(FaqCategory::getCollectionId, collection.getId())
                .eq(FaqCategory::getIsDefault, true)
                .orderBy("id asc");
        List<FaqCategory> defaultCategories = list(queryWrapper);
        FaqCategory defaultCategory;
        if (defaultCategories == null || defaultCategories.isEmpty()) {
            Date now = new Date();
            BigInteger userId = getCurrentUserId();
            FaqCategory entity = new FaqCategory();
            entity.setCollectionId(collectionId);
            entity.setParentId(ROOT_PARENT_ID);
            entity.setAncestors("0");
            entity.setLevelNo(1);
            entity.setCategoryName("默认分类");
            entity.setSortNo(0);
            entity.setIsDefault(true);
            entity.setStatus(0);
            entity.setCreated(now);
            entity.setCreatedBy(userId);
            entity.setModified(now);
            entity.setModifiedBy(userId);
            save(entity);
            defaultCategory = entity;
        } else {
            defaultCategory = defaultCategories.get(0);
            if (defaultCategories.size() > 1) {
                normalizeDuplicateDefaultCategories(collectionId, defaultCategory, defaultCategories);
            }
        }

        backfillNullFaqItemCategory(collectionId, defaultCategory.getId());
        return defaultCategory.getId();
    }

    private void normalizeDuplicateDefaultCategories(BigInteger collectionId,
                                                     FaqCategory keepCategory,
                                                     List<FaqCategory> defaultCategories) {
        if (keepCategory == null || defaultCategories == null || defaultCategories.size() <= 1) {
            return;
        }
        Date now = new Date();
        BigInteger userId = getCurrentUserId();

        for (FaqCategory duplicate : defaultCategories) {
            if (duplicate == null || duplicate.getId() == null || keepCategory.getId().equals(duplicate.getId())) {
                continue;
            }

            QueryWrapper faqQuery = QueryWrapper.create()
                    .eq(FaqItem::getCollectionId, collectionId)
                    .eq(FaqItem::getCategoryId, duplicate.getId());
            FaqItem faqItem = new FaqItem();
            faqItem.setCategoryId(keepCategory.getId());
            faqItem.setModified(now);
            faqItem.setModifiedBy(userId);
            faqItemMapper.updateByQuery(faqItem, faqQuery);

            QueryWrapper childQuery = QueryWrapper.create()
                    .eq(FaqCategory::getCollectionId, collectionId)
                    .eq(FaqCategory::getParentId, duplicate.getId());
            long childCount = mapper.selectCountByQuery(childQuery);
            if (childCount <= 0) {
                removeById(duplicate.getId());
                continue;
            }

            duplicate.setIsDefault(false);
            if ("默认分类".equals(duplicate.getCategoryName())) {
                duplicate.setCategoryName("默认分类副本-" + duplicate.getId());
            }
            duplicate.setModified(now);
            duplicate.setModifiedBy(userId);
            updateById(duplicate);
        }
    }

    @Override
    @Transactional
    public BigInteger ensureFaqItemCategory(BigInteger collectionId, BigInteger categoryId) {
        checkFaqCollection(collectionId);
        BigInteger defaultCategoryId = ensureDefaultCategory(collectionId);
        if (categoryId == null) {
            return defaultCategoryId;
        }

        FaqCategory category = getById(categoryId);
        if (category == null || !collectionId.equals(category.getCollectionId())) {
            throw new BusinessException("FAQ分类不存在或不属于当前知识库");
        }
        if (category.getStatus() != null && category.getStatus() != 0) {
            throw new BusinessException("FAQ分类不可用");
        }
        return categoryId;
    }

    @Override
    public List<FaqCategory> listByCollection(BigInteger collectionId, Boolean asTree) {
        checkFaqCollection(collectionId);
        ensureDefaultCategory(collectionId);

        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(FaqCategory::getCollectionId, collectionId)
                .orderBy("is_default desc, sort_no asc, id asc");
        List<FaqCategory> categories = list(queryWrapper);
        if (asTree == null || asTree) {
            return Tree.tryToTree(categories, "id", "parentId");
        }
        return categories;
    }

    @Override
    @Transactional
    public boolean saveCategory(FaqCategory entity) {
        if (entity == null) {
            throw new BusinessException("FAQ分类不能为空");
        }
        if (entity.getCollectionId() == null) {
            throw new BusinessException("知识库ID不能为空");
        }
        if (StringUtil.noText(entity.getCategoryName())) {
            throw new BusinessException("分类名称不能为空");
        }

        checkFaqCollection(entity.getCollectionId());
        BigInteger parentId = normalizeParentId(entity.getParentId());
        validateParentNotDefault(entity.getCollectionId(), parentId);
        ParentMeta parentMeta = getParentMeta(entity.getCollectionId(), parentId, null);

        Date now = new Date();
        BigInteger userId = getCurrentUserId();
        entity.setParentId(parentId);
        entity.setAncestors(parentMeta.ancestors);
        entity.setLevelNo(parentMeta.levelNo);
        entity.setCategoryName(entity.getCategoryName().trim());
        if (entity.getSortNo() == null) {
            entity.setSortNo(nextSortNo(entity.getCollectionId(), parentId));
        }
        entity.setIsDefault(false);
        if (entity.getStatus() == null) {
            entity.setStatus(0);
        }
        entity.setCreated(now);
        entity.setCreatedBy(userId);
        entity.setModified(now);
        entity.setModifiedBy(userId);

        return save(entity);
    }

    @Override
    @Transactional
    public boolean updateCategory(FaqCategory entity) {
        if (entity == null || entity.getId() == null) {
            throw new BusinessException("FAQ分类ID不能为空");
        }
        if (StringUtil.noText(entity.getCategoryName())) {
            throw new BusinessException("分类名称不能为空");
        }

        FaqCategory old = getById(entity.getId());
        if (old == null) {
            throw new BusinessException("FAQ分类不存在");
        }

        checkFaqCollection(old.getCollectionId());

        BigInteger parentId = normalizeParentId(entity.getParentId() == null ? old.getParentId() : entity.getParentId());
        if (Boolean.TRUE.equals(old.getIsDefault()) && !ROOT_PARENT_ID.equals(parentId)) {
            throw new BusinessException("默认分类不能设置父级");
        }
        if (!parentId.equals(normalizeParentId(old.getParentId()))) {
            validateParentNotDefault(old.getCollectionId(), parentId);
        }

        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(FaqCategory::getCollectionId, old.getCollectionId())
                .orderBy("sort_no asc, id asc");
        List<FaqCategory> allCategories = list(queryWrapper);
        Map<BigInteger, FaqCategory> categoryMap = new HashMap<>();
        for (FaqCategory category : allCategories) {
            categoryMap.put(category.getId(), category);
        }

        if (!categoryMap.containsKey(old.getId())) {
            throw new BusinessException("FAQ分类不存在");
        }

        if (parentId.equals(old.getId())) {
            throw new BusinessException("父分类不能是自己");
        }

        Set<BigInteger> descendants = findDescendantSet(categoryMap, old.getId());
        if (descendants.contains(parentId)) {
            throw new BusinessException("父分类不能是当前分类的子分类");
        }

        ParentMeta parentMeta = getParentMeta(old.getCollectionId(), parentId, old.getId());

        Date now = new Date();
        BigInteger userId = getCurrentUserId();

        old.setParentId(parentId);
        old.setAncestors(parentMeta.ancestors);
        old.setLevelNo(parentMeta.levelNo);
        old.setCategoryName(entity.getCategoryName().trim());
        if (entity.getSortNo() != null) {
            old.setSortNo(entity.getSortNo());
        }
        if (entity.getStatus() != null) {
            old.setStatus(entity.getStatus());
        }
        old.setModified(now);
        old.setModifiedBy(userId);

        categoryMap.put(old.getId(), old);
        Map<BigInteger, List<FaqCategory>> childrenMap = buildChildrenMap(categoryMap.values());
        List<FaqCategory> changedChildren = new ArrayList<>();
        refreshChildrenLevelAndAncestors(old, childrenMap, changedChildren, now, userId);

        boolean updated = updateById(old);
        if (!updated) {
            return false;
        }

        for (FaqCategory child : changedChildren) {
            updateById(child);
        }

        return true;
    }

    @Override
    @Transactional
    public boolean removeCategory(BigInteger id) {
        if (id == null) {
            throw new BusinessException("FAQ分类ID不能为空");
        }

        FaqCategory old = getById(id);
        if (old == null) {
            throw new BusinessException("FAQ分类不存在");
        }

        checkFaqCollection(old.getCollectionId());

        if (Boolean.TRUE.equals(old.getIsDefault())) {
            throw new BusinessException("默认分类不允许删除");
        }

        QueryWrapper childQuery = QueryWrapper.create()
                .eq(FaqCategory::getCollectionId, old.getCollectionId())
                .eq(FaqCategory::getParentId, old.getId());
        long childrenCount = mapper.selectCountByQuery(childQuery);
        if (childrenCount > 0) {
            throw new BusinessException("该分类下存在子分类，请先处理子分类");
        }

        QueryWrapper faqQuery = QueryWrapper.create()
                .eq(FaqItem::getCollectionId, old.getCollectionId())
                .eq(FaqItem::getCategoryId, old.getId());
        long faqCount = faqItemMapper.selectCountByQuery(faqQuery);
        if (faqCount > 0) {
            throw new BusinessException("该分类下存在FAQ条目，请先处理FAQ条目");
        }

        return removeById(id);
    }

    @Override
    public List<BigInteger> findDescendantIds(BigInteger collectionId, BigInteger categoryId) {
        if (collectionId == null || categoryId == null) {
            return Collections.emptyList();
        }

        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(FaqCategory::getCollectionId, collectionId)
                .orderBy("sort_no asc, id asc");
        List<FaqCategory> categories = list(queryWrapper);
        Map<BigInteger, FaqCategory> categoryMap = new HashMap<>();
        for (FaqCategory category : categories) {
            categoryMap.put(category.getId(), category);
        }

        if (!categoryMap.containsKey(categoryId)) {
            return Collections.emptyList();
        }

        Set<BigInteger> descendantSet = findDescendantSet(categoryMap, categoryId);
        return new ArrayList<>(descendantSet);
    }

    @Override
    public Map<BigInteger, String> buildPathMap(BigInteger collectionId) {
        if (collectionId == null) {
            return Collections.emptyMap();
        }
        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(FaqCategory::getCollectionId, collectionId)
                .orderBy("sort_no asc, id asc");
        List<FaqCategory> categories = list(queryWrapper);
        Map<BigInteger, FaqCategory> categoryMap = new HashMap<>();
        for (FaqCategory category : categories) {
            categoryMap.put(category.getId(), category);
        }

        Map<BigInteger, String> pathMap = new HashMap<>();
        for (FaqCategory category : categories) {
            buildPath(category.getId(), categoryMap, pathMap, new HashSet<>());
        }
        return pathMap;
    }

    private void backfillNullFaqItemCategory(BigInteger collectionId, BigInteger defaultCategoryId) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(FaqItem::getCollectionId, collectionId)
                .isNull(FaqItem::getCategoryId);
        long emptyCategoryCount = faqItemMapper.selectCountByQuery(queryWrapper);
        if (emptyCategoryCount <= 0) {
            return;
        }
        FaqItem faqItem = new FaqItem();
        faqItem.setCategoryId(defaultCategoryId);
        faqItem.setModified(new Date());
        faqItem.setModifiedBy(getCurrentUserId());
        faqItemMapper.updateByQuery(faqItem, queryWrapper);
    }

    private String buildPath(BigInteger categoryId,
                             Map<BigInteger, FaqCategory> categoryMap,
                             Map<BigInteger, String> pathMap,
                             Set<BigInteger> visiting) {
        if (categoryId == null) {
            return "";
        }
        if (pathMap.containsKey(categoryId)) {
            return pathMap.get(categoryId);
        }

        FaqCategory category = categoryMap.get(categoryId);
        if (category == null) {
            return "";
        }

        if (!visiting.add(categoryId)) {
            throw new BusinessException("FAQ分类存在循环引用");
        }

        String path;
        BigInteger parentId = normalizeParentId(category.getParentId());
        if (ROOT_PARENT_ID.equals(parentId)) {
            path = category.getCategoryName();
        } else {
            String parentPath = buildPath(parentId, categoryMap, pathMap, visiting);
            if (StringUtil.noText(parentPath)) {
                path = category.getCategoryName();
            } else {
                path = parentPath + " / " + category.getCategoryName();
            }
        }

        visiting.remove(categoryId);
        pathMap.put(categoryId, path);
        return path;
    }

    private Set<BigInteger> findDescendantSet(Map<BigInteger, FaqCategory> categoryMap, BigInteger categoryId) {
        Map<BigInteger, List<FaqCategory>> childrenMap = buildChildrenMap(categoryMap.values());
        Set<BigInteger> result = new HashSet<>();
        ArrayDeque<BigInteger> queue = new ArrayDeque<>();
        queue.add(categoryId);

        while (!queue.isEmpty()) {
            BigInteger currentId = queue.poll();
            if (!result.add(currentId)) {
                continue;
            }
            List<FaqCategory> children = childrenMap.getOrDefault(currentId, Collections.emptyList());
            for (FaqCategory child : children) {
                queue.offer(child.getId());
            }
        }

        return result;
    }

    private Map<BigInteger, List<FaqCategory>> buildChildrenMap(Iterable<FaqCategory> categories) {
        Map<BigInteger, List<FaqCategory>> childrenMap = new HashMap<>();
        for (FaqCategory category : categories) {
            BigInteger parentId = normalizeParentId(category.getParentId());
            childrenMap.computeIfAbsent(parentId, key -> new ArrayList<>()).add(category);
        }
        return childrenMap;
    }

    private void refreshChildrenLevelAndAncestors(FaqCategory parent,
                                                  Map<BigInteger, List<FaqCategory>> childrenMap,
                                                  List<FaqCategory> changedChildren,
                                                  Date modified,
                                                  BigInteger modifiedBy) {
        List<FaqCategory> children = childrenMap.getOrDefault(parent.getId(), Collections.emptyList());
        for (FaqCategory child : children) {
            int nextLevel = parent.getLevelNo() + 1;
            if (nextLevel > 3) {
                throw new BusinessException("最多支持三级分类");
            }
            child.setLevelNo(nextLevel);
            child.setAncestors(parent.getAncestors() + "," + parent.getId());
            child.setModified(modified);
            child.setModifiedBy(modifiedBy);
            changedChildren.add(child);
            refreshChildrenLevelAndAncestors(child, childrenMap, changedChildren, modified, modifiedBy);
        }
    }

    private ParentMeta getParentMeta(BigInteger collectionId, BigInteger parentId, BigInteger currentId) {
        BigInteger normalizedParentId = normalizeParentId(parentId);
        if (ROOT_PARENT_ID.equals(normalizedParentId)) {
            return new ParentMeta("0", 1);
        }

        FaqCategory parent = getById(normalizedParentId);
        if (parent == null || !collectionId.equals(parent.getCollectionId())) {
            throw new BusinessException("父分类不存在");
        }
        if (currentId != null && currentId.equals(parent.getId())) {
            throw new BusinessException("父分类不能是自己");
        }

        int level = (parent.getLevelNo() == null ? 0 : parent.getLevelNo()) + 1;
        if (level > 3) {
            throw new BusinessException("最多支持三级分类");
        }
        return new ParentMeta(parent.getAncestors() + "," + parent.getId(), level);
    }

    private BigInteger normalizeParentId(BigInteger parentId) {
        return parentId == null ? ROOT_PARENT_ID : parentId;
    }

    private Integer nextSortNo(BigInteger collectionId, BigInteger parentId) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(FaqCategory::getCollectionId, collectionId)
                .eq(FaqCategory::getParentId, parentId)
                .orderBy("sort_no desc, id desc");
        List<FaqCategory> siblings = list(queryWrapper);
        if (siblings == null || siblings.isEmpty() || siblings.get(0).getSortNo() == null) {
            return 0;
        }
        return siblings.get(0).getSortNo() + 1;
    }

    private void validateParentNotDefault(BigInteger collectionId, BigInteger parentId) {
        if (parentId == null || ROOT_PARENT_ID.equals(parentId)) {
            return;
        }
        FaqCategory parent = getById(parentId);
        if (parent == null || !collectionId.equals(parent.getCollectionId())) {
            return;
        }
        if (Boolean.TRUE.equals(parent.getIsDefault())) {
            throw new BusinessException("默认分类不允许创建子分类");
        }
    }

    private DocumentCollection checkFaqCollection(BigInteger collectionId) {
        if (collectionId == null) {
            throw new BusinessException("知识库ID不能为空");
        }
        DocumentCollection collection = documentCollectionService.getById(collectionId);
        if (collection == null) {
            throw new BusinessException("知识库不存在");
        }
        if (!collection.isFaqCollection()) {
            throw new BusinessException("当前知识库不是FAQ类型");
        }
        return collection;
    }

    private BigInteger getCurrentUserId() {
        if (!StpUtil.isLogin()) {
            return null;
        }
        return BigInteger.valueOf(StpUtil.getLoginIdAsLong());
    }

    private static class ParentMeta {
        private final String ancestors;
        private final int levelNo;

        private ParentMeta(String ancestors, int levelNo) {
            this.ancestors = ancestors;
            this.levelNo = levelNo;
        }
    }
}
