package com.qmth.teachcloud.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.teachcloud.common.bean.dto.OrgDto;
import com.qmth.teachcloud.common.bean.params.UserSaveParams;
import com.qmth.teachcloud.common.bean.result.SysUserResult;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.BasicCampus;
import com.qmth.teachcloud.common.entity.SysOrg;
import com.qmth.teachcloud.common.entity.SysRole;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.FieldUniqueEnum;
import com.qmth.teachcloud.common.enums.OrgTypeEnum;
import com.qmth.teachcloud.common.enums.RoleTypeEnum;
import com.qmth.teachcloud.common.enums.userPush.SpecialPrivilegeEnum;
import com.qmth.teachcloud.common.mapper.SysOrgMapper;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.ResultUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
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;
import java.util.stream.Stream;

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

    @Resource
    private SysUserService sysUserService;

    @Resource
    private SysRoleService sysRoleService;

    @Resource
    SysOrgMapper sysOrgMapper;

    @Resource
    TeachcloudCommonService teachcloudCommonService;

    @Resource
    BasicCampusService basicCampusService;

    @Override
    public List<OrgDto> listOrgTree(SpecialPrivilegeEnum specialPrivilegeEnum) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        List<SysUserResult> sysUserResultList = sysUserService.findSysUserResultList();
        List<SysUserResult> finalSysUserResultList = sysUserResultList.stream()
                .filter(e -> {
                    if (Objects.isNull(specialPrivilegeEnum)) {
                        return true;
                    } else {
                        // 用户特殊权限
                        SpecialPrivilegeEnum userP = sysUserService.findUserSpecialPrivilegeByUserId(e.getId());
                        // 如果用户是复合权限，条件为复合、评卷员、科组长都可以
                        if (SpecialPrivilegeEnum.COMPOSITE.equals(userP)) {
                            return SpecialPrivilegeEnum.COMPOSITE.equals(specialPrivilegeEnum) || SpecialPrivilegeEnum.MARKER.equals(specialPrivilegeEnum) || SpecialPrivilegeEnum.SUBJECT_HEADER.equals(specialPrivilegeEnum);
                        } else {
                            // 如果用户不是复合条件，用户权限和条件权限对应
                            return specialPrivilegeEnum.equals(userP);
                        }
                    }
                }).collect(Collectors.toList());

        List<OrgDto> orgList = this.listOrgAll(schoolId);
        Map<Long, OrgDto> map = orgList.stream()
                .peek(e -> {
                    // 加入机构下所有人员查询
                    e.setSysUserList(finalSysUserResultList.stream().filter(f -> 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();

            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("机构代码已存在");
                }
                checkOrgTree(org);
                org.insertInfo(sysUser.getId());
                success = this.save(org);
            } else { // 修改
                if (sysOrg != null && org.getId().longValue() != sysOrg.getId().longValue()) {
                    throw ExceptionResultEnum.ERROR.exception("机构代码已存在");
                }
                org.updateInfo(sysUser.getId());
                success = this.updateById(org);
            }
            if (Objects.nonNull(org.getCampusId())) {//更新校区
                BasicCampus basicCampus = basicCampusService.getById(org.getCampusId());
                if (Objects.nonNull(basicCampus) && Objects.nonNull(basicCampus.getPrintHouseId())
                        && basicCampus.getPrintHouseId().longValue() != org.getId().longValue()) {
                    throw ExceptionResultEnum.ERROR.exception("该校区已绑定其它印刷室");
                }

                UpdateWrapper<BasicCampus> basicCampusUpdateWrapper = new UpdateWrapper<>();
                basicCampusUpdateWrapper.lambda().eq(BasicCampus::getId, org.getCampusId())
                        .set(BasicCampus::getPrintHouseId, org.getId());
                basicCampusService.update(basicCampusUpdateWrapper);
            }
        } 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);
        }
    }

    /**
     * 校验各机构关系
     *
     * @param org
     */
    private void checkOrgTree(SysOrg org) {
        // 印刷厂不能设置子机构
        if (Objects.nonNull(org.getParentId())) {
            SysOrg parentOrg = this.getById(org.getParentId());
            if (Objects.nonNull(parentOrg) && OrgTypeEnum.COLLEGE.equals(parentOrg.getType())) {
                if (!OrgTypeEnum.FACULTY.equals(org.getType()) && !OrgTypeEnum.TEACHING_ROOM.equals(org.getType())) {
                    throw ExceptionResultEnum.ERROR.exception("学院下只能设置院系或者教研室");
                }
            } else if (Objects.nonNull(parentOrg) && OrgTypeEnum.FACULTY.equals(parentOrg.getType())) {
                if (!OrgTypeEnum.TEACHING_ROOM.equals(org.getType())) {
                    throw ExceptionResultEnum.ERROR.exception("院系下只能设置教研室");
                }
            } else if (Objects.nonNull(parentOrg) && OrgTypeEnum.TEACHING_ROOM.equals(parentOrg.getType())) {
                throw ExceptionResultEnum.ERROR.exception("教研室下不能设置子机构");
            } else if (Objects.nonNull(parentOrg) && OrgTypeEnum.PRINTING_HOUSE.equals(parentOrg.getType())) {
                throw ExceptionResultEnum.ERROR.exception("印刷厂不能设置子机构");
            }
        } else {
            if (!OrgTypeEnum.COLLEGE.equals(org.getType()) && !OrgTypeEnum.PRINTING_HOUSE.equals(org.getType())) {
                throw ExceptionResultEnum.ERROR.exception("一级机构只能设置学院或者印刷厂");
            }
        }
    }

    @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;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long searchOrInsert(Long schoolId, String collegeName) {
        Long orgId;
        List<SysOrg> sysOrgList = this.list(new QueryWrapper<SysOrg>().lambda().eq(SysOrg::getSchoolId, schoolId));
        List<SysOrg> parentOrgList = sysOrgList.stream().filter(e -> e.getParentId() == null || e.getParentId() == 0).collect(Collectors.toList());
        Long parentId;
        if (parentOrgList.size() == 0) {
            parentId = SystemConstant.getDbUuid();
            SysOrg parentTemp = new SysOrg();
            parentTemp.setId(parentId);
            parentTemp.setEnable(true);
            parentTemp.setSchoolId(schoolId);
            parentTemp.setName(SystemConstant.PARENT_ORG);
            parentTemp.setCode(SystemConstant.PARENT_ORG);
            this.save(parentTemp);
        } else {
            parentId = parentOrgList.get(0).getId();
        }

        SysOrg sysOrg = this.getOne(new QueryWrapper<SysOrg>().lambda().eq(SysOrg::getSchoolId, schoolId).eq(SysOrg::getName, collegeName));
        if (Objects.isNull(sysOrg)) {
            // 创建学院
            orgId = SystemConstant.getDbUuid();
            SysOrg temp = new SysOrg();
            temp.setId(orgId);
            temp.setName(collegeName);
            temp.setCode(collegeName);
            temp.setSchoolId(schoolId);
            temp.setEnable(true);
            temp.setParentId(parentId);
            this.save(temp);

            // 创建院长用户
            SysUser old = sysUserService.getOne(new QueryWrapper<SysUser>().lambda()
                    .eq(SysUser::getSchoolId, schoolId)
                    .eq(SysUser::getLoginName, collegeName)
                    .eq(SysUser::getRealName, collegeName));
            Long id = 0L;
            if (Objects.nonNull(old)) {
                id = old.getId();
            }
            SysRole role = sysRoleService.list(new QueryWrapper<SysRole>().lambda().eq(SysRole::getType, RoleTypeEnum.PRESIDENT)).get(0);
            List<Long> roleIds = new ArrayList<>();
            roleIds.add(role.getId());

            UserSaveParams userSaveParams = new UserSaveParams();
            if (id > 0) {
                userSaveParams.setId(id);
            }

            userSaveParams.setSchoolId(schoolId);
            userSaveParams.setLoginName(collegeName);
            userSaveParams.setRealName(collegeName);
            userSaveParams.setPassword(SystemConstant.DEFAULT_PASSWORD);
            userSaveParams.setOrgId(orgId);
            userSaveParams.setEnable(true);
            userSaveParams.setRoleIds(roleIds.toArray(new Long[0]));
            sysUserService.saveUserNoAuth(userSaveParams);
        } else {
            orgId = sysOrg.getId();
        }
        return orgId;
    }

    /**
     * 根据机构类型查找用户
     *
     * @param schoolId
     * @param orgId
     * @param orgTypeEnum
     * @return
     */
    @Override
    public List<SysUser> findUserByOrgType(Long schoolId, Long orgId, OrgTypeEnum orgTypeEnum) {
        return sysOrgMapper.findUserByOrgType(schoolId, orgId, orgTypeEnum.name());
    }

    /**
     * 递归树查询(同级及以上)
     *
     * @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
     * @param orgType
     * @return
     */
    @Override
    public List<SysOrg> findByJxzyyConnectByParentId(Long orgId, OrgTypeEnum orgType) {
        return sysOrgMapper.findByJxzyyConnectByParentId(orgId, Objects.nonNull(orgType) ? orgType.name() : null);
    }

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

    @Override
    public List<SysOrg> findDeepByOrgIdAndType(OrgTypeEnum orgTypeEnum, Long orgId, boolean auth) {
        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)
                .eq(SysOrg::getType, orgTypeEnum)
                .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;
    }

    @Override
    public List<SysOrg> findByOrgIds(List<Long> useOrgIds) {
        QueryWrapper<SysOrg> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(SysOrg::getId, useOrgIds);
        return this.list(queryWrapper);
    }

    /**
     * 根据子机构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;
    }
}
