package com.qmth.paper.library.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.paper.library.common.bean.dto.OrgDto;
import com.qmth.paper.library.common.bean.result.SysUserResult;
import com.qmth.paper.library.common.contant.SystemConstant;
import com.qmth.paper.library.common.entity.SysOrg;
import com.qmth.paper.library.common.entity.SysUser;
import com.qmth.paper.library.common.enums.ExceptionResultEnum;
import com.qmth.paper.library.common.enums.FieldUniqueEnum;
import com.qmth.paper.library.common.enums.OrgTypeEnum;
import com.qmth.paper.library.common.mapper.SysOrgMapper;
import com.qmth.paper.library.common.service.SysOrgService;
import com.qmth.paper.library.common.service.SysUserService;
import com.qmth.paper.library.common.util.ResultUtil;
import com.qmth.paper.library.common.util.ServletUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 学校组织架构 服务实现类
 * </p>
 *
 * @author xf
 * @since 2021-03-23
 */
@Service
public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> implements SysOrgService {

    @Resource
    private SysUserService sysUserService;
    @Resource
    private SysOrgMapper sysOrgMapper;

    @Override
    public List<OrgDto> listOrgTree() {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        List<SysUserResult> sysUserResultList = sysUserService.findSysUserResultList();

        List<OrgDto> orgList = this.listOrgAll(schoolId);
        Map<Long, OrgDto> map = orgList.stream()
                .peek(e -> {
                    // 加入机构下所有人员查询
                    e.setSysUserList(sysUserResultList.stream().filter(f -> Objects.nonNull(f.getOrgId()) && f.getOrgId().equals(e.getId())).collect(Collectors.toList()));
                })
                .collect(Collectors.toMap(OrgDto::getId, Function.identity(), (dto1, dto2) -> dto1));

        Iterator<Long> iterator = map.keySet().iterator();
        Set<Long> deleteKeys = new HashSet<>();
        while (iterator.hasNext()) {
            Long parentId = iterator.next();
            if (map.get(parentId).getParentId() != null) {
                if (Objects.nonNull(map.get(map.get(parentId).getParentId()))) {
                    map.get(map.get(parentId).getParentId()).getChildren().add(map.get(parentId));
                    deleteKeys.add(parentId);
                }
            }
        }
        for (Long key : deleteKeys) {
            map.remove(key);
        }
        return new ArrayList<>(map.values());
    }

    /**
     * 获取所有机构
     *
     * @param schoolId
     * @return
     */
    @Override
    public List<OrgDto> listOrgAll(Long schoolId) {
        return sysOrgMapper.listOrgAll(schoolId);
    }

    @Override
    @Transactional
    public boolean saveOrg(SysOrg org) {
        boolean success = false;
        try {
            Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
            org.setSchoolId(schoolId);
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            if (org.getId() == null) {// 新增
                org.insertInfo(sysUser.getId());
                success = this.save(org);
            } else { // 修改
                org.updateInfo(sysUser.getId());
                success = this.updateById(org);
            }
        } catch (Exception e) {
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length()).replaceAll("'", "");
                throw ExceptionResultEnum.SQL_ERROR.exception("[" + FieldUniqueEnum.convertToTitle(columnStr) + "]数据不允许重复插入");
            } else if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return success;
    }

    @Transactional
    @Override
    public boolean saveReportOrg(SysOrg org) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        org.setSchoolId(schoolId);
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();

        QueryWrapper<SysOrg> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysOrg::getSchoolId, org.getSchoolId()).eq(SysOrg::getParentId, org.getParentId()).eq(SysOrg::getCode, org.getCode());
        SysOrg sysOrg = this.getOne(queryWrapper);

        // 新增
        if (org.getId() == null) {
            if (sysOrg != null) {
                throw ExceptionResultEnum.ERROR.exception("机构代码已存在");
            }
            org.setId(SystemConstant.getDbUuid());
            org.setCreateId(sysUser.getId());
            org.setCreateTime(System.currentTimeMillis());
            org.setCreateTime(System.currentTimeMillis());
            return this.save(org);
        }
        // 修改
        else {
            if (sysOrg != null && org.getId().longValue() != sysOrg.getId().longValue()) {
                throw ExceptionResultEnum.ERROR.exception("机构代码已存在");
            }
            org.setUpdateId(sysUser.getId());
            org.setUpdateTime(System.currentTimeMillis());
            return this.updateById(org);
        }
    }

    @Override
    public SysOrg findByForeignKey(Long schoolId, String collegeCode, String collegeName) {
        List<SysOrg> tbSchoolCollegeList = this.list(new QueryWrapper<SysOrg>().lambda()
                .eq(SysOrg::getSchoolId, schoolId)
                .eq(SysOrg::getCode, collegeCode)
                .eq(SysOrg::getName, collegeName));
        if (tbSchoolCollegeList.size() != 1) {
            throw ExceptionResultEnum.ERROR.exception("学院基础数据信息异常");
        }
        return tbSchoolCollegeList.get(0);
    }

    /**
     * 查询机构ids
     *
     * @param orgId
     * @return
     */
    @Override
    public List<Long> findCollegeIds(Long orgId) {
        QueryWrapper<SysOrg> sysOrgQueryWrapper = new QueryWrapper<>();
        sysOrgQueryWrapper.lambda().eq(SysOrg::getParentId, orgId)
                .eq(SysOrg::getEnable, true);
        List<SysOrg> sysOrgList = this.list(sysOrgQueryWrapper);
        return Objects.nonNull(sysOrgList) ? sysOrgList.stream().map(s -> s.getId()).collect(Collectors.toList()) : null;
    }

    /**
     * 递归树查询(同级及以上)
     *
     * @param orgId
     * @param school
     * @param college
     * @return
     */
    @Override
    public List<SysOrg> findByConnectByParentId(Long orgId, Boolean school, Boolean college) {
        return sysOrgMapper.findByConnectByParentId(orgId, school, college);
    }

    /**
     * 递归树查询(同级及以下)
     *
     * @param orgId
     * @return
     */
    @Override
    public List<SysOrg> findByConnectByRootOrgId(Long orgId) {
        return sysOrgMapper.findByConnectByRootOrgId(orgId);
    }

    @Override
    public List<SysOrg> findDeepByOrgIdAndType(String orgType) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        if (Objects.nonNull(orgType) && (Objects.equals(orgType, "SCHOOL") || Objects.equals(orgType, "PRINTING_HOUSE"))) {
            return sysOrgMapper.findByConnectByRootOrgIdAndType(sysUser.getOrgId(), OrgTypeEnum.valueOf(orgType).name());
        } else {
            return sysOrgMapper.findByConnectByRootOrgIdAndFilter(sysUser.getOrgId(), true);
        }
//        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
//        Set<Long> orgIds = new HashSet<>(); // 下级机构集合
//        Set<Long> authOrgIdSet = new HashSet<>(); // 当前用户数据权限下的机构集合
//        Set<Long> orgIdSet = new HashSet<>(); // 输入机构下的所有下级机构集合
//        if (auth) {
//            authOrgIdSet = teachcloudCommonService.listSubOrgIds(null);
//        }
//        if (SystemConstant.longNotNull(orgId)) {
//            orgIdSet = teachcloudCommonService.listSubOrgIds(orgId);
//        }
//        orgIds.addAll(authOrgIdSet);
//        orgIds.addAll(orgIdSet);
//        QueryWrapper<SysOrg> queryWrapper = new QueryWrapper<>();
//        queryWrapper.lambda().eq(SysOrg::getSchoolId, schoolId);
//        if (Objects.nonNull(orgTypeEnum)) {
//            queryWrapper.lambda().eq(SysOrg::getType, orgTypeEnum);
//        }
//        queryWrapper.lambda().eq(SysOrg::getEnable, true);
//        if (orgIds.size() > 0) {
//            queryWrapper.lambda().in(SysOrg::getId, orgIds);
//        }
//        return this.list(queryWrapper);
    }

    @Override
    public List<SysOrg> findParentsByOrgId(Long orgId) {
        SysOrg sysOrg = this.getById(orgId);
        Long parentId = sysOrg.getParentId();
        return this.findTopDFS(null, parentId);
    }

    @Override
    public Set<Long> findDeepOrgIdListByUserId(Long userId) {
        List<Long> orgIdList = sysUserService.findOrgIdListByUserId(userId);
        return this.findDeepOrgIdListByOrgIdList(orgIdList);
    }

    @Override
    public Set<Long> findDeepOrgIdListByOrgIdList(List<Long> orgIdList) {
        Set<Long> orgIdSet = new HashSet<>(orgIdList);
        Set<Long> finishedIdSet = this.findDeepDFS(new HashSet<>(), orgIdSet, new HashSet<>());

        // 加上他自己所在的机构
        finishedIdSet.addAll(orgIdSet);
        return finishedIdSet;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, Long> createOrGetOrgByOrgInfo(List<String> orgInfoList, SysUser requestUser) {
        Map<String, Long> map = new HashMap<>();
        for (String orgInfo : orgInfoList) {
            // 父机构id
            Long schoolId = requestUser.getSchoolId();
            List<SysOrg> topLevelOrg = this.list(new QueryWrapper<SysOrg>().lambda().eq(SysOrg::getSchoolId, schoolId).eq(SysOrg::getType, OrgTypeEnum.SCHOOL));
            if (topLevelOrg.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception("顶级机构信息获取异常");
            }
            Long parentId = topLevelOrg.get(0).getId();

            String[] arr = orgInfo.split(SystemConstant.ORG_SPLIT);
            for (int i = 0; i < arr.length; i++) {
                SysOrg targetOrg;
                String orgDetailName = "";
                String orgName = arr[i];

                // 拼机构元素详细名称
                for (int j = 0; j <= i; j++) {
                    orgDetailName = orgDetailName + arr[j] + SystemConstant.ORG_SPLIT;
                }
                if (orgDetailName.length() > 0) {
                    orgDetailName = orgDetailName.substring(0, orgDetailName.length() - SystemConstant.ORG_SPLIT.length());
                }
                if (!map.containsKey(orgDetailName)) {
                    // 在map中没有找到，就去数据库查找
                    targetOrg = this.getOne(new QueryWrapper<SysOrg>()
                            .lambda()
                            .eq(SysOrg::getSchoolId, schoolId)
                            .eq(SysOrg::getName, orgName)
                            .eq(SysOrg::getParentId, parentId));

                    // 如果在数据库中也没有找到这个机构元素，则新建当前机构元素并保存在数据库
                    if (Objects.isNull(targetOrg)) {
                        targetOrg = this.saveOrgElement(parentId, orgName, requestUser);
                    }

                    // 不管是已有的还是新建的机构，都保存在map中方便后面查询 格式：（k -> 化学学院/核化学/聚变化学 v -> 11111111111）
                    map.put(orgDetailName, targetOrg.getId());
                    // 更新parentId为该机构元素的id，并作为数组下一个节点的父机构id
                    parentId = targetOrg.getId();
                } else {
                    // 如果map中本身就有该机构元素，则直接获取该机构元素id作为 数组下一个节点的父机构id
                    parentId = map.get(orgDetailName);
                }
            }
        }
        return map;
    }


    @Override
    public SysOrg findCollegeLevelOrgByOrgId(Long orgId) {
        if (!SystemConstant.longNotNull(orgId)) {
            throw ExceptionResultEnum.ERROR.exception("机构id不存在");
        }

        List<SysOrg> orgList = this.findParentsByOrgId(orgId).stream().distinct().collect(Collectors.toList());
        List<SysOrg> schoolOrgList = orgList.stream().filter(e -> OrgTypeEnum.SCHOOL.equals(e.getType())).collect(Collectors.toList());
        if (schoolOrgList.size() != 1) {
            throw ExceptionResultEnum.ERROR.exception("学校信息异常");
        }
        SysOrg schoolOrg = schoolOrgList.get(0);
        Long schoolOrgId = schoolOrg.getId();
        List<SysOrg> collegeOrgList = orgList.stream().filter(e -> schoolOrgId.equals(e.getParentId())).collect(Collectors.toList());
        if (collegeOrgList.size() != 1) {
            throw ExceptionResultEnum.ERROR.exception("开课学院异常");
        }
        return collegeOrgList.get(0);
    }

    @Override
    public List<SysOrg> findCollegeLevelOrgList(Long schoolId) {
        SysOrg schoolOrg = this.getOne(new QueryWrapper<SysOrg>()
                .lambda()
                .eq(SysOrg::getSchoolId, schoolId)
                .eq(SysOrg::getType, OrgTypeEnum.SCHOOL));

        Long schoolOrgId = schoolOrg.getId();

        return this.list(new QueryWrapper<SysOrg>()
                        .lambda()
                        .eq(SysOrg::getSchoolId, schoolId)
                        .eq(SysOrg::getParentId, schoolOrgId))
                .stream()
                .filter(e -> !OrgTypeEnum.PRINTING_HOUSE.equals(e.getType()))
                .collect(Collectors.toList());
    }

    /**
     * 根据子机构id深度优先搜索其父机构们
     *
     * @param sysOrgList 目标父机构们
     * @param orgId      子机构id
     * @return 父亲们
     */
    private List<SysOrg> findTopDFS(List<SysOrg> sysOrgList, Long orgId) {
        if (CollectionUtils.isEmpty(sysOrgList)) {
            sysOrgList = new ArrayList<>();
        }
        SysOrg sysOrg = this.getById(orgId);
        if (Objects.isNull(sysOrg)) {
            throw ExceptionResultEnum.ERROR.exception("找不到机构");
        }
        sysOrgList.add(sysOrg);
        Long parentId = sysOrg.getParentId();
        if (SystemConstant.longNotNull(parentId)) {
            this.findTopDFS(sysOrgList, parentId);
        }
        return sysOrgList;
    }

    /**
     * 根据机构id集合深度搜索他们的子机构并且避免重复搜索（机构集合不在同一级导致的重复搜索）
     *
     * @param searchedIdSet 已经进行搜索过的机构id集合（标记）
     * @param targetIdSet   目标机构集合（下一级机构）
     * @param finishedIdSet 完成搜索的所有符合条件机构id集合（结果）
     * @return finishedIdSet
     */
    private Set<Long> findDeepDFS(Set<Long> searchedIdSet, Set<Long> targetIdSet, Set<Long> finishedIdSet) {
        for (Long targetId : targetIdSet) {
            if (!searchedIdSet.contains(targetId)) {
                Set<Long> orgIdSet = this.list(new QueryWrapper<SysOrg>()
                                .lambda()
                                .eq(SysOrg::getParentId, targetId))
                        .stream()
                        .map(SysOrg::getId)
                        .collect(Collectors.toSet());

                if (orgIdSet.size() > 0) {
                    searchedIdSet.add(targetId);
                    finishedIdSet.addAll(orgIdSet);
                    this.findDeepDFS(searchedIdSet, orgIdSet, finishedIdSet);
                }
            }
        }
        return finishedIdSet;
    }

    /**
     * 新增机构元素
     *
     * @param parentId    父机构id
     * @param orgName     机构名称
     * @param requestUser 请求用户
     * @return 新增后的机构id
     */
    private SysOrg saveOrgElement(Long parentId, String orgName, SysUser requestUser) {
        SysOrg checkOrg = this.getOne(new QueryWrapper<SysOrg>().lambda().eq(SysOrg::getName, orgName).eq(SysOrg::getSchoolId, requestUser.getSchoolId()));
        if (Objects.nonNull(checkOrg)) {
            // 数据库中存在该名字的机构
            SysOrg p1 = this.getById(checkOrg.getParentId());
            SysOrg p2 = this.getById(parentId);
            if (SystemConstant.isOneNull(p1, p2)) {
                throw ExceptionResultEnum.ERROR.exception("未找到父机构");
            }
            throw ExceptionResultEnum.ERROR.exception("机构创建失败：机构【" + orgName + "】存在多个不同的上级机构 " +
                    "-> 已存在机构关系【..." + p1.getName() + "/" + orgName + "...】，却又创建机构关系【..." + p2.getName() + "/" + orgName + "...】");
        }

        SysOrg sysOrg = new SysOrg();
        sysOrg.setSchoolId(requestUser.getSchoolId());
        sysOrg.insertInfo(requestUser.getId());
        sysOrg.setParentId(parentId);
        sysOrg.setName(orgName);
        sysOrg.setEnable(true);
        this.save(sysOrg);

        return sysOrg;
    }
}
