package com.aegis.resource.material.service.impl;

import com.aegis.common.constant.UserConstants;
import com.aegis.common.core.domain.TreeSelect;
import com.aegis.common.exception.ServiceException;
import com.aegis.common.utils.SecurityUtils;
import com.aegis.common.utils.StringUtils;
import com.aegis.resource.material.domain.Material;
import com.aegis.resource.material.domain.MaterialCategory;
import com.aegis.resource.material.mapper.ResourceMaterialCategoryMapper;
import com.aegis.resource.material.service.IResourceMaterialCategoryService;
import com.aegis.resource.material.service.IResourceMaterialService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 物资分类Service业务层处理（MyBatis-Plus风格）
 *
 * @author aegis
 */
@Service
public class ResourceMaterialCategoryServiceImpl extends ServiceImpl<ResourceMaterialCategoryMapper, MaterialCategory>
    implements IResourceMaterialCategoryService {

    private static final String STATUS_NORMAL = "0";
    private static final String STATUS_DISABLED = "1";
    private static final String ROOT_ANCESTOR = "0";

    @Autowired
    private IResourceMaterialService materialService;

    @Override
    public List<MaterialCategory> listByCondition(MaterialCategory condition) {
        LambdaQueryWrapper<MaterialCategory> wrapper = new LambdaQueryWrapper<>();
        if (condition != null) {
            wrapper.like(StringUtils.isNotBlank(condition.getCategoryName()), MaterialCategory::getCategoryName, condition.getCategoryName());
            wrapper.like(StringUtils.isNotBlank(condition.getCategoryCode()), MaterialCategory::getCategoryCode, condition.getCategoryCode());
            wrapper.eq(condition.getParentId() != null, MaterialCategory::getParentId, condition.getParentId());
            wrapper.eq(StringUtils.isNotBlank(condition.getStatus()), MaterialCategory::getStatus, condition.getStatus());
        }
        wrapper.orderByAsc(MaterialCategory::getParentId)
            .orderByAsc(MaterialCategory::getOrderNum)
            .orderByAsc(MaterialCategory::getId);
        return list(wrapper);
    }

    @Override
    public List<MaterialCategory> buildCategoryTree(List<MaterialCategory> categories) {
        List<MaterialCategory> returnList = new ArrayList<>();
        List<Long> tempList = categories.stream().map(MaterialCategory::getId).collect(Collectors.toList());
        for (MaterialCategory category : categories) {
            if (!tempList.contains(category.getParentId())) {
                recursionFn(categories, category);
                returnList.add(category);
            }
        }
        if (returnList.isEmpty()) {
            returnList = categories;
        }
        return returnList;
    }

    @Override
    public List<TreeSelect> buildCategoryTreeSelect(List<MaterialCategory> categories) {
        List<MaterialCategory> categoryTrees = buildCategoryTree(categories);
        return categoryTrees.stream().map(this::convertToTreeSelect).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(MaterialCategory category) {
        if (category == null) {
            throw new ServiceException("分类信息不能为空");
        }
        if (!isCategoryCodeUnique(category)) {
            throw new ServiceException("新增分类'" + category.getCategoryName() + "'失败，分类编码已存在");
        }
        MaterialCategory parentCategory = resolveParent(category.getParentId());
        ensureParentAvailable(parentCategory);
        String ancestors = buildAncestors(parentCategory);
        String operator = SecurityUtils.getUsername();
        Date now = new Date();

        MaterialCategory target = buildCategoryPayload(category);
        target.setAncestors(ancestors);

        applyCreateAudit(target, operator, now);
        return super.save(target);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(MaterialCategory category) {
        if (category == null || category.getId() == null) {
            throw new ServiceException("分类ID不能为空");
        }
        MaterialCategory existing = super.getById(category.getId());
        if (existing == null) {
            throw new ServiceException("分类不存在");
        }

        if (StringUtils.isNotBlank(category.getCategoryCode())
            && !StringUtils.equals(existing.getCategoryCode(), category.getCategoryCode())) {
            ensureCategoryCodeAvailable(category.getCategoryCode(), category.getId(), category.getCategoryName());
            existing.setCategoryCode(category.getCategoryCode());
        }

        if (StringUtils.isNotBlank(category.getCategoryName())) {
            existing.setCategoryName(category.getCategoryName());
        }
        if (category.getOrderNum() != null) {
            existing.setOrderNum(category.getOrderNum());
        }
        if (category.getRemark() != null) {
            existing.setRemark(category.getRemark());
        }
        if (StringUtils.isNotBlank(category.getStatus())
            && !StringUtils.equals(existing.getStatus(), category.getStatus())) {
            validateStatus(category.getStatus());
            existing.setStatus(category.getStatus());
        }

        if (category.getParentId() != null && !category.getParentId().equals(existing.getParentId())) {
            MaterialCategory parentCategory = resolveParent(category.getParentId());
            ensureParentAvailable(parentCategory);
            String newAncestors = buildAncestors(parentCategory);
            String oldAncestors = existing.getAncestors();
            existing.setParentId(category.getParentId());
            existing.setAncestors(newAncestors);
            String operator = SecurityUtils.getUsername();
            Date now = new Date();
            updateCategoryChildren(existing.getId(), newAncestors, oldAncestors, operator, now);
        }

        String operator = SecurityUtils.getUsername();
        Date now = new Date();
        applyUpdateAudit(existing, operator, now);
        return super.updateById(existing);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeById(java.io.Serializable id) {
        if (id == null) {
            throw new ServiceException("分类ID不能为空");
        }
        Long categoryId = (Long) id;
        if (hasChildren(categoryId)) {
            throw new ServiceException("存在下级分类，不允许删除");
        }
        if (hasMaterials(categoryId)) {
            throw new ServiceException("分类存在物资，不允许删除");
        }
        MaterialCategory existing = super.getById(id);
        if (existing == null) {
            throw new ServiceException("分类不存在");
        }
        // 物理删除：直接从数据库删除记录
        return super.removeById(id);
    }

    /**
     * 递归列表
     */
    private void recursionFn(List<MaterialCategory> list, MaterialCategory t) {
        // 得到子节点列表
        List<MaterialCategory> childList = getChildList(list, t);
        t.setChildren(childList);
        for (MaterialCategory tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<MaterialCategory> getChildList(List<MaterialCategory> list, MaterialCategory t) {
        List<MaterialCategory> tlist = new ArrayList<MaterialCategory>();
        Iterator<MaterialCategory> it = list.iterator();
        while (it.hasNext()) {
            MaterialCategory n = (MaterialCategory) it.next();
            if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getId().longValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<MaterialCategory> list, MaterialCategory t) {
        return getChildList(list, t).size() > 0;
    }

    private MaterialCategory getActiveCategory(Long id) {
        if (id == null) {
            return null;
        }
        return super.getById(id);
    }

    private MaterialCategory resolveParent(Long parentId) {
        if (parentId == null || parentId == 0L) {
            return null;
        }
        MaterialCategory parent = getActiveCategory(parentId);
        if (parent == null) {
            throw new ServiceException("父级分类不存在");
        }
        return parent;
    }

    private String buildAncestors(MaterialCategory parentCategory) {
        if (parentCategory == null) {
            return ROOT_ANCESTOR;
        }
        if (StringUtils.isNotBlank(parentCategory.getAncestors())) {
            return parentCategory.getAncestors() + "," + parentCategory.getId();
        }
        return ROOT_ANCESTOR + "," + parentCategory.getId();
    }

    private void ensureCategoryCodeAvailable(String categoryCode, Long currentId, String categoryName) {
        if (StringUtils.isEmpty(categoryCode)) {
            throw new ServiceException("分类编码不能为空");
        }
        LambdaQueryWrapper<MaterialCategory> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MaterialCategory::getCategoryCode, categoryCode);
        if (currentId != null) {
            wrapper.ne(MaterialCategory::getId, currentId);
        }
        MaterialCategory exists = getOne(wrapper);
        if (exists != null) {
            String name = StringUtils.defaultIfBlank(categoryName, categoryCode);
            throw new ServiceException("分类编码已存在：" + name);
        }
    }

    private void ensureParentAvailable(MaterialCategory parentCategory) {
        if (parentCategory != null && !STATUS_NORMAL.equals(parentCategory.getStatus())) {
            throw new ServiceException("分类停用，不允许新增");
        }
    }

    private void applyCreateAudit(MaterialCategory category, String operator, Date now) {
        category.setCreateBy(operator);
        category.setCreateTime(now);
        category.setUpdateBy(operator);
        category.setUpdateTime(now);
    }

    private void applyUpdateAudit(MaterialCategory category, String operator, Date now) {
        category.setUpdateBy(operator);
        category.setUpdateTime(now);
    }

    private MaterialCategory buildCategoryPayload(MaterialCategory source) {
        MaterialCategory target = new MaterialCategory();
        target.setCategoryCode(source.getCategoryCode());
        target.setCategoryName(source.getCategoryName());
        target.setParentId(source.getParentId() == null ? 0L : source.getParentId());
        target.setOrderNum(source.getOrderNum());
        target.setStatus(StringUtils.defaultIfBlank(source.getStatus(), STATUS_NORMAL));
        target.setRemark(source.getRemark());
        return target;
    }

    private void validateStatus(String status) {
        if (!STATUS_NORMAL.equals(status) && !STATUS_DISABLED.equals(status)) {
            throw new ServiceException("分类状态不合法");
        }
    }

    private List<MaterialCategory> listDescendants(Long categoryId) {
        LambdaQueryWrapper<MaterialCategory> wrapper = new LambdaQueryWrapper<>();
        wrapper.apply("find_in_set({0}, ancestors)", categoryId);
        return list(wrapper);
    }

    private TreeSelect convertToTreeSelect(MaterialCategory category) {
        TreeSelect treeSelect = new TreeSelect();
        treeSelect.setId(category.getId());
        treeSelect.setLabel(category.getCategoryName());
        treeSelect.setDisabled(STATUS_DISABLED.equals(category.getStatus()));
        @SuppressWarnings("unchecked")
        List<MaterialCategory> children = category.getChildren() == null
            ? Collections.emptyList()
            : (List<MaterialCategory>) category.getChildren();
        if (!CollectionUtils.isEmpty(children)) {
            treeSelect.setChildren(children.stream()
                .map(this::convertToTreeSelect)
                .collect(Collectors.toList()));
        }
        return treeSelect;
    }

    private boolean hasChildren(Long id) {
        LambdaQueryWrapper<MaterialCategory> wrapper = new LambdaQueryWrapper<>();
        wrapper.apply("find_in_set({0}, ancestors)", id);
        return count(wrapper) > 0;
    }

    private boolean hasMaterials(Long id) {
        LambdaQueryWrapper<Material> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Material::getCategoryId, id);
        return materialService.count(wrapper) > 0;
    }

    private void updateCategoryChildren(Long categoryId, String newAncestors, String oldAncestors, String operator, Date now) {
        List<MaterialCategory> children = listDescendants(categoryId);
        if (CollectionUtils.isEmpty(children)) {
            return;
        }
        for (MaterialCategory child : children) {
            if (StringUtils.isNotBlank(child.getAncestors()) && StringUtils.isNotBlank(oldAncestors)) {
                child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
            } else {
                child.setAncestors(newAncestors);
            }
            child.setUpdateBy(operator);
            child.setUpdateTime(now);
            super.updateById(child);
        }
    }

    private boolean isCategoryCodeUnique(MaterialCategory category) {
        if (StringUtils.isEmpty(category.getCategoryCode())) {
            return UserConstants.UNIQUE;
        }
        LambdaQueryWrapper<MaterialCategory> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MaterialCategory::getCategoryCode, category.getCategoryCode());
        if (category.getId() != null) {
            wrapper.ne(MaterialCategory::getId, category.getId());
        }
        return count(wrapper) == 0 ? UserConstants.UNIQUE : UserConstants.NOT_UNIQUE;
    }
}