package com.tykj.workflowcore.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.tykj.workflowcore.base.result.ApiException;
import com.tykj.workflowcore.user.dao.AuthorityOrganizationDao;
import com.tykj.workflowcore.user.dao.OrganizationDao;
import com.tykj.workflowcore.user.dao.UserDao;
import com.tykj.workflowcore.user.pojo.AuthorityOrganization;
import com.tykj.workflowcore.user.pojo.Organization;
import com.tykj.workflowcore.user.pojo.User;
import com.tykj.workflowcore.user.pojo.vo.OrganizationVo;
import com.tykj.workflowcore.user.service.OrganizationService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import junit.framework.Assert;
import org.springframework.util.StringUtils;


import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class OrganizationServiceImpl implements OrganizationService {
    @Autowired
    private OrganizationDao organizationDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private AuthorityOrganizationDao authorityOrganizationDao;

    @Override
    public List<Organization> findAll() {
        List<Organization> organizationList = organizationDao.findAll();
        if (organizationList!= null){
            return organizationList;
        }
        return null;
    }

    @Override
    public Organization add(Organization organization) {
        if (!checkCodeIsUnique(organization)){
            throw new ApiException("机构编码不能重复");
        }
        Organization organization1 = organizationDao.save(organization);
        if (organization1 != null){
            return organization1;
        }
        return null;
    }

    @Override
    public Organization update(Organization organization) {
        if (!checkCodeIsUnique(organization)){
            throw new ApiException("机构编码不能重复");
        }
        Organization organization1 = organizationDao.save(organization);
        if (organization1 != null){
            return organization1;
        }
        return null;
    }

    @Override
    public void delete(Integer id) {
        Organization organization = organizationDao.findById(id).get();
        organizationDao.delete(organization);
        //删除组织架构的同时  需要删除组织架构和人员 权限之间的绑定关系
        List<User> users = userDao.findAllByOrganizationIdAndDelStatus(id, 1);
        for (User user : users) {
            user.setOrganizationId(-1);
            user.setOrganizationCode("");
            user.setOrganizationName("");
            user.setOrganizationPost("");
            userDao.save(user);
        }
        List<AuthorityOrganization> authorityOrganizationList = authorityOrganizationDao.findByOrgId(id);
        for (AuthorityOrganization authorityOrganization : authorityOrganizationList) {
            authorityOrganizationDao.delete(authorityOrganization);
        }
    }

    @Override
    public Organization findById(Integer id) {
        Organization organization = organizationDao.findById(id).get();
        if (organization != null){
            return organization;
        }
        return null;
    }

    @Override
    public List<OrganizationVo> find(List<Organization> organizationList) {
        Map<Integer, Organization> orgNodeMap = new HashMap<>();
        //1.从DB中查询出List<Organization>
       // List<Organization> organizationList = organizationDao.findAll();
        System.out.println("_____"+JSONObject.toJSON(organizationList));
        for (Organization organization : organizationList) {
            orgNodeMap.put(organization.getId(),organization);
        }

        List<Organization> orgNodeList = new ArrayList<>(orgNodeMap.values());
        //2.将List<OrgNode>解析并转化为Node<OrgNodeVo>对象
        List<OrganizationVo> rootNodeList = parseTreeFromDown(orgNodeList,
                Organization::getId,
                organization -> organizationDao.findById(organization.getPid()),
                Organization::toVo,
                OrganizationVo::addChildNode);

        Assert.assertTrue(rootNodeList.size() >= 1);
        System.out.println("*****:"+JSONObject.toJSON(rootNodeList));

        return rootNodeList;
    }



    public Optional<Organization> getParentOrgNode(Integer pid) {
        return organizationDao.findById(pid);
    }




    /**
     * 自底向上整理出树结构，并存放在Node对象中
     * 利用Map将原始List的对象的位置重新整理，并将所有的指针都设置好
     *
     * @param originList      原始待整理的列表
     * @param getId           列表里每个对象获取自身id的方法，用于避免重复计算
     * @param getParent       列表里每个对象获取父对象的方法
     * @param mapToTargetNode 列表里的原始对象转化为最终对象的方法，默认为t->t，即不变(传入null即可)
     * @param targetSetChild  用于最终对象设置子节点的方法，形如R.setChild(R),或者R.addChildList(R)
     * @param <R>             最终对象
     * @param <V>             原始对象
     * @param <T>             主键类型
     * @return 根节点对象集合
     */
    public <R, V, T extends Number> List<R> parseTreeFromDown(List<V> originList,
                                                              Function<V, T> getId,
                                                              Function<V, Optional<V>> getParent,
                                                              Function<V, R> mapToTargetNode,
                                                              BiConsumer<R, R> targetSetChild) {
        //K为主键id , Value 为最终对象
        Map<T, R> map = new HashMap<>(32);
        List<T> rootIds = new ArrayList<>();

        for (V originNode : originList) {
            //对于所有节点，如果已经遍历过了则直接取已经设置过子节点的引用
            R targetNode = map.getOrDefault(getId.apply(originNode), mapToTargetNode.apply(originNode));

            Optional<V> parentNode = getParent.apply(originNode);
            //查询父节点，如果不存在父节点则证明该节点为根节点，直接放入map中
            if (parentNode.isPresent()) {
                //否则查询该父节点是否已经已经被连接过指针，如果连接过则取出连接过的继续连接，否则进行第一次连接并存入map
                V parent = parentNode.get();
                T parentId = getId.apply(parent);
                R parentInMap = map.get(parentId);
                if (parentInMap == null) {
                    R newNode = mapToTargetNode.apply(parent);
                    targetSetChild.accept(newNode, targetNode);
                    map.put(parentId, newNode);
                } else {
                    targetSetChild.accept(parentInMap, targetNode);
                }
                //连接完处理之后还需要将自身这个节点存入map
                map.put(getId.apply(originNode), targetNode);
            } else {
                //root node
                map.put(getId.apply(originNode), targetNode);
                rootIds.add(getId.apply(originNode));
            }

        }

        //根据rootIds返回所有的顶层节点
        return rootIds.stream().map(map::get).collect(Collectors.toList());
    }


    /**
     * 打印出树状对象结构
     *
     * @param rootNode      树的根节点
     * @param printProperty 想打印的属性
     * @param getChild      子节点
     * @param stackDepth    栈深度
     * @param <T>           节点对象类型
     * @param <R>           要打印的属性类型
     */
    public <T, R> void printTreeNode(T rootNode,
                                     Function<T, R> printProperty,
                                     Function<T, List<T>> getChild,
                                     int stackDepth) {
        StringJoiner joiner = new StringJoiner("\t");
        IntStream.range(0, stackDepth).mapToObj(i -> "").forEach(joiner::add);
        joiner.add(printProperty.apply(rootNode).toString());
        for (T childNode : getChild.apply(rootNode)) {
            int currentLevel = stackDepth + 1;
            printTreeNode(childNode, printProperty, getChild, currentLevel);
        }
    }

    @Override
    public List<Organization> findByOrganizationNameLike(String organizationName) {
        List<Organization> organizationList = organizationDao.findByOrganizationNameLike("%" + organizationName + "%");
        return organizationList;
    }

    @Override
    public List<Organization> findByCode(String code) {
        List<Organization> allByCode = organizationDao.findAllByCode(code);
        return allByCode;
    }

    @Override
    public List<Organization> findOrganizationListByCode(String code){
        List<Organization> byCode = findByCode(code);
        if (byCode==null||byCode.size()<=0){
            return null;
        }
        Organization organization = byCode.get(0);
        List<Organization> all = findAll();
        Map<Integer, Organization> nodeCollect =
                all.stream().collect(Collectors.toMap(Organization::getId, org->org));
        List<Organization> returnList = new ArrayList<>();
        returnList.add(organization);
        getOrganizationByPid(organization.getPid(), nodeCollect, returnList);
        return returnList;
    }

    public List<Organization> getOrganizationByPid(Integer pid,Map<Integer, Organization> sourceMap,List<Organization> returnList){
        Organization organization = sourceMap.get(pid);
        if (organization!=null){
            returnList.add(organization);
            if (organization.getPid()!=null&&organization.getPid()>0){
                getOrganizationByPid(organization.getPid(),sourceMap,returnList);
            }
        }
        return returnList;
    }

    public Boolean checkCodeIsUnique(Organization organization){
        if (!StringUtils.isEmpty(organization.getCode())){
            List<Organization> allByCode = organizationDao.findAllByCode(organization.getCode());
            if (allByCode.size()>0){
                return false;
            }
        }
        return true;
    }


}
