package com.seezoon.admin.modules.sys.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import com.seezoon.admin.framework.service.AbstractCrudService;
import com.seezoon.dao.framework.dto.Tree;
import com.seezoon.dao.modules.sys.SysDeptDao;
import com.seezoon.dao.modules.sys.entity.SysDept;
import com.seezoon.dao.modules.sys.entity.SysDeptCondition;
import com.seezoon.framework.utils.IdGen;
import com.seezoon.framework.utils.TreeHelper;

/**
 * 组织机构
 *
 * @author seezoon-generator 2021年1月12日 下午10:54:44
 */
@Service
public class SysDeptService extends AbstractCrudService<SysDeptDao, SysDept, Integer> {

    @Transactional(readOnly = true)
    public SysDept findByNameAndParentId(@NotEmpty String name, @NotNull Integer parentId) {
        SysDeptCondition sysDeptCondition = new SysDeptCondition();
        sysDeptCondition.setName(name);
        sysDeptCondition.setParentId(parentId);
        return this.findOne(sysDeptCondition);
    }

    /**
     * 查询子节点
     *
     * @param parentId
     * @return
     */
    @Transactional(readOnly = true)
    public List<SysDept> findByParentId(@NotNull Integer parentId) {
        SysDeptCondition sysDeptCondition = new SysDeptCondition();
        sysDeptCondition.setParentId(parentId);
        return this.find(sysDeptCondition);
    }

    /**
     * 找到自己的所有孩子
     *
     * @param id
     * @return
     */
    @Transactional(readOnly = true)
    public List<SysDept> findAllChildren(@NotNull Integer id) {
        SysDeptCondition sysDeptCondition = new SysDeptCondition();
        sysDeptCondition.setParentIds(TreeHelper.getQueryParentIds(id));
        return this.find(sysDeptCondition);
    }

    public int save(@NotNull SysDept record) {
        this.resolveParentIds(record);
        return super.save(record);
    }

    @Override
    public int updateSelective(@NotNull SysDept record) {
        // 检查是否修改过父部门
        SysDept current = this.find(record.getId());
        if (Objects.equals(current.getParentId(), record.getParentId())) {
            return super.updateSelective(record);
        } else {
            String oldParentIds = current.getParentIds();
            this.resolveParentIds(record);

            this.findAllChildren(record.getId()).forEach((child) -> {
                child.setParentIds(child.getParentIds().replace(oldParentIds, oldParentIds));
                super.updateSelective(child);
            });
            return super.updateSelective(record);
        }
    }

    /**
     * 生成新的parentIds
     *
     * @param record
     */
    private void resolveParentIds(SysDept record) {
        // 处理parentIds
        Integer parentId = record.getParentId();
        Assert.notNull(parentId, "parentId must be not null");
        if (parentId == TreeHelper.DEFAULT_PARENT_ID) {
            record.setParentIds(TreeHelper.DEFAULT_PARENT_IDS);
        } else {
            SysDept parent = this.find(parentId);
            Assert.notNull(parent, "父部门不存在");
            record.setParentIds(TreeHelper.getCurrentParentIds(record.getId(), parentId, parent.getParentIds()));
        }
    }

    /**
     * 删除本部门及子部门
     *
     * @param id
     * @return
     */
    public int delete(@NotNull @Min(1) Integer id) {
        List<Integer> childrenIds = this.findAllChildren(id).stream().map(SysDept::getId).collect(Collectors.toList());
        childrenIds.add(id);
        int cnt = super.delete(childrenIds.toArray(Integer[]::new));
        logger.info("delete dept size:[{}]", cnt);
        return cnt;
    }

    /**
     * 查询树结构
     *
     * @param parentId
     * @param includeChild
     *            是否包含子节点的子节点
     * @return
     */
    @Transactional(readOnly = true)
    public List<Tree> findTree(@NotNull Integer parentId, boolean includeChild) {
        List<Tree> trees = new ArrayList<>();
        List<SysDept> depts = this.findByParentId(parentId);
        depts.forEach((dept) -> {
            Tree tree = Tree.builder().key(IdGen.uuid()).value(dept.getId()).title(dept.getName())
                .children(includeChild ? this.findTree(dept.getId(), includeChild) : null).selectable(true).build();
            if (includeChild) {
                tree.setLeaf(CollectionUtils.isEmpty(tree.getChildren()) ? true : false);
            }
            trees.add(tree);
        });
        return trees;
    }

}
