package com.skr.mdm.api.system.service.impl;

import cn.hutool.core.collection.ListUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.skr.mdm.api.system.excel.DepartmentImportData;
import com.skr.mdm.api.system.excel.DepartmentImportDataListener;
import com.skr.mdm.api.system.service.DepartmentService;
import com.skr.mdm.bean.qo.system.DepartmentMoveQO;
import com.skr.mdm.bean.qo.system.DepartmentQO;
import com.skr.mdm.bean.vo.DepartmentTreeVO;
import com.skr.mdm.dao.*;
import com.skr.mdm.dto.LoginAdminDTO;
import com.skr.mdm.entity.AuDepartmentDO;
import com.skr.mdm.entity.AuUserDO;
import com.skr.mdm.entity.DeviceEquipmentDO;
import com.skr.mdm.entity.MdmImportHistoryDO;
import com.skr.mdm.enums.DeleteStateEnum;
import com.skr.mdm.enums.LogAdminTypeEnum;
import com.skr.mdm.enums.ResultCodeEnum;
import com.skr.mdm.exception.ServiceException;
import com.skr.mdm.handle.LogAdminHandler;
import com.skr.mdm.manager.oss.MinIoManager;
import com.skr.mdm.result.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author nfq
 * @date 2022-11-14 16:09
 */
@Service
@Slf4j
public class DepartmentServiceImpl implements DepartmentService {

    private final String MODULE = "部门管理";

    @Autowired
    private AuDepartmentDao departmentDao;
    @Autowired
    private AuAdminDepartmentDao adminDepartmentDao;
    @Autowired
    private AuAdminRoleDao adminRoleDao;
    @Autowired
    private AuUserDao userDao;
    @Autowired
    private DeviceEquipmentDao equipmentDao;
    @Autowired
    private LogAdminHandler logAdminHandler;
    @Autowired
    private MdmImportHistoryDao importHistoryDao;
    @Autowired
    private MinIoManager minIoManager;

    @Override
    public ResponseResult deptTree(Integer adminId, Boolean userFlag, LoginAdminDTO loginAdminDTO) {
        List<AuDepartmentDO> departmentDOList = new LambdaQueryChainWrapper<>(departmentDao)
                .eq(AuDepartmentDO::getCompanyId, loginAdminDTO.getCompanyId())
                .list();
        List<Integer> listDepartmentIdByAdminId = adminDepartmentDao.listDepartmentIdByAdminId(adminId);
        if (CollectionUtils.isNotEmpty(departmentDOList)) {
            return ResponseResult.success(setChildren(0, departmentDOList, listDepartmentIdByAdminId, userFlag));
        }
        return ResponseResult.success();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult addDept(DepartmentQO departmentQO, LoginAdminDTO loginAdminDTO) {
        // 名称判重
        checkDuplicate(departmentQO, loginAdminDTO.getCompanyId());
        Integer maxWeight = departmentDao.getMaxWeight(departmentQO.getParentId());
        AuDepartmentDO departmentDO = AuDepartmentDO.builder()
                .companyId(loginAdminDTO.getCompanyId())
                .isDefault(0)
                .weight(maxWeight == null ? 0 : maxWeight + 1)
                .build();
        BeanUtils.copyProperties(departmentQO, departmentDO);
        departmentDao.insert(departmentDO);
        saveAdminDept(ListUtil.toList(departmentDO.getId()), loginAdminDTO);
        // log
        String content = "创建部门：" + departmentQO.getDepartmentName();
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult updateDept(DepartmentQO departmentQO, LoginAdminDTO loginAdminDTO) {
        // 部门判重
        checkDuplicate(departmentQO, loginAdminDTO.getCompanyId());
        AuDepartmentDO departmentDO = AuDepartmentDO.builder().build();
        BeanUtils.copyProperties(departmentQO, departmentDO);
        departmentDao.updateById(departmentDO);
        // 更新用户表部门
        new LambdaUpdateChainWrapper<>(userDao)
                .set(AuUserDO::getDepartmentName, departmentQO.getDepartmentName())
                .eq(AuUserDO::getDepartmentId, departmentQO.getId())
                .update();
        // 更新设备表部门
        new LambdaUpdateChainWrapper<>(equipmentDao)
                .set(DeviceEquipmentDO::getDepartmentName, departmentQO.getDepartmentName())
                .eq(DeviceEquipmentDO::getDepartmentId, departmentQO.getId())
                .update();
        // log
        String content = "编辑部门：" + departmentQO.getDepartmentName();
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult deleteDept(List<Integer> deptIdList, LoginAdminDTO loginAdminDTO) {
        // 检查部门下是否存在用户
        Long count = new LambdaQueryChainWrapper<>(userDao)
                .eq(AuUserDO::getIsDelete, DeleteStateEnum.NON_DELETE.getState())
                .in(AuUserDO::getDepartmentId, deptIdList)
                .count();
        if (count > 0) {
            return ResponseResult.failure(ResultCodeEnum.DEPARTMENT_EXIST_USER);
        }
        // log
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), "删除部门", deptIdList, MODULE);
        departmentDao.deleteBatchIds(deptIdList);
        return ResponseResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult moveDept(List<DepartmentMoveQO> departmentMoveQOList, LoginAdminDTO loginAdminDTO) {
        // move
        departmentMoveQOList.forEach(departmentMoveQO -> {
            new LambdaUpdateChainWrapper<>(departmentDao)
                    .set(AuDepartmentDO::getWeight, departmentMoveQO.getWeight())
                    .set(AuDepartmentDO::getParentId, departmentMoveQO.getParentId())
                    .eq(AuDepartmentDO::getId, departmentMoveQO.getId())
                    .update();
        });
        // log
        Integer id = departmentMoveQOList.stream()
                .filter(departmentMoveQO -> departmentMoveQO.getIsMove().equals(1))
                .findFirst()
                .map(DepartmentMoveQO::getId)
                .orElse(0);
        AuDepartmentDO departmentDO = departmentDao.selectById(id);
        AuDepartmentDO parentDepartmentDO = departmentDao.selectById(departmentMoveQOList.get(0).getParentId());
        String content = "";
        if (parentDepartmentDO == null) {
            // 移动到根部门
            content = "部门：" + departmentDO.getDepartmentName() + "移动到根级";
        } else {
            content = "部门：" + departmentDO.getDepartmentName() + "移动到部门:" + parentDepartmentDO.getDepartmentName();
        }
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult importDept(MultipartFile file, LoginAdminDTO loginAdminDTO) throws IOException {
        Date date = new Date();
        DepartmentImportDataListener departmentImportDataListener = new DepartmentImportDataListener(this,
                departmentDao, loginAdminDTO.getCompanyId());
        departmentImportDataListener.setMinIoManager(minIoManager);
        EasyExcel.read(file.getInputStream(), DepartmentImportData.class, departmentImportDataListener)
                .sheet()
                .headRowNumber(2)
                .doRead();
        // 保存部门数据权限
        if (CollectionUtils.isNotEmpty(departmentImportDataListener.getSuccessDeptIdSet())) {
            saveAdminDept(ListUtil.toList(departmentImportDataListener.getSuccessDeptIdSet()), loginAdminDTO);
        }
        MdmImportHistoryDO mdmImportHistoryDO = MdmImportHistoryDO.builder()
                .importType(1)
                .operator(loginAdminDTO.getAdminName())
                .allCount(departmentImportDataListener.getAllCount())
                .successCount(departmentImportDataListener.getSuccessCount())
                .failCount(departmentImportDataListener.getFailCount())
                .finishTime(new Date())
                .failFileUrl(departmentImportDataListener.getFailFileUrl())
                .companyId(loginAdminDTO.getCompanyId())
                .createTime(date)
                .build();
        importHistoryDao.insert(mdmImportHistoryDO);
        // log
        logAdminHandler.log(loginAdminDTO, "", LogAdminTypeEnum.OTHER_TYPE.getLogType(), "部门导入", MODULE);
        return ResponseResult.success(mdmImportHistoryDO);
    }

    private List<DepartmentTreeVO> setChildren(Integer parentId, List<AuDepartmentDO> departmentDOList,
                                               List<Integer> adminDepartmentIdList, Boolean flag) {
        // 筛选子部门
        List<AuDepartmentDO> childrenDepartmentDOList = departmentDOList.stream()
                .filter(departmentDO -> departmentDO.getParentId().equals(parentId))
                .sorted(Comparator.comparing(AuDepartmentDO::getWeight))
                .collect(Collectors.toList());
        List<DepartmentTreeVO> departmentTreeVOList = new ArrayList<>(childrenDepartmentDOList.size());
        if (CollectionUtils.isNotEmpty(childrenDepartmentDOList)) {
            childrenDepartmentDOList.forEach(departmentDO -> {
                DepartmentTreeVO departmentTreeVO = new DepartmentTreeVO();
                departmentTreeVO.setId(departmentDO.getId());
                departmentTreeVO.setParentId(departmentDO.getParentId());
                departmentTreeVO.setName(departmentDO.getDepartmentName());
                departmentTreeVO.setIsDisplay(0);
                // 管理员是否有部门权限
                if (adminDepartmentIdList.contains(departmentDO.getId())) {
                    departmentTreeVO.setIsDisplay(1);
                }
                if (flag) {
                    departmentTreeVO.setUserList(userDao.selectDepartmentUserVOList(departmentDO.getId()));
                }
                departmentTreeVO.setChildren(setChildren(departmentDO.getId(), departmentDOList, adminDepartmentIdList, flag));
                departmentTreeVOList.add(departmentTreeVO);
            });
        }
        return departmentTreeVOList;
    }

    private void checkDuplicate(DepartmentQO departmentQO, Integer companyId) {
        // 同一上级部门的部门名称不允许重复 如：a/b/c a/b/c
        Long count = new LambdaQueryChainWrapper<>(departmentDao)
                .eq(AuDepartmentDO::getDepartmentName, departmentQO.getDepartmentName())
                .eq(AuDepartmentDO::getParentId, departmentQO.getParentId())
                .eq(AuDepartmentDO::getCompanyId, companyId)
                .ne(departmentQO.getId() != null, AuDepartmentDO::getId, departmentQO.getId())
                .count();
        if (count > 0) {
            throw new ServiceException("该部门已存在");
        }
    }

    @Override
    public void saveAdminDept(List<Integer> deptIdList, LoginAdminDTO loginAdminDTO) {
        // 为当前管理员添加部门权限
        adminDepartmentDao.insertBatch(loginAdminDTO.getId(), deptIdList);
        // 公司超级管理员添加部门权限
        Integer superAdminId = adminRoleDao.getSuperAdminId(loginAdminDTO.getCompanyId(), 0);
        if (!superAdminId.equals(loginAdminDTO.getId())) {
            adminDepartmentDao.insertBatch(superAdminId, deptIdList);
        }
    }

    @Override
    public String getFullDept(Integer deptId) {
        List<String> deptList = getDeptTreeById(deptId, Lists.newArrayListWithCapacity(8));
        if (CollectionUtils.isNotEmpty(deptList)) {
            return StringUtils.join(Lists.reverse(deptList), "/");
        }
        return null;
    }

    @Override
    public Map<String, Integer> getDeptPathMap(Integer companyId) {
        List<AuDepartmentDO> departmentDOList = new LambdaQueryChainWrapper<>(departmentDao)
                .eq(AuDepartmentDO::getCompanyId, companyId)
                .list();
        Map<String, Integer> deptPathMap = Maps.newHashMapWithExpectedSize(departmentDOList.size() + 16);
        if (CollectionUtils.isNotEmpty(departmentDOList)) {
            Integer parentId = 0;
            for (AuDepartmentDO departmentDO : departmentDOList) {
                String parentName = departmentDO.getDepartmentName();
                if (departmentDO.getParentId().equals(parentId)) {
                    deptPathMap.put(parentName, departmentDO.getId());
                    setDeptMap(departmentDO.getId(), parentName, departmentDOList, deptPathMap);
                }
            }
        }
        return deptPathMap;
    }

    private void setDeptMap(Integer parentId, String parentPath, List<AuDepartmentDO> departmentDOList, Map<String, Integer> deptPathMap) {
        for (AuDepartmentDO departmentDO : departmentDOList) {
            if (departmentDO.getParentId().equals(parentId)) {
                String deptPath = parentPath.concat("/").concat(departmentDO.getDepartmentName());
                deptPathMap.put(deptPath, departmentDO.getId());
                setDeptMap(departmentDO.getId(), deptPath, departmentDOList, deptPathMap);
            }
        }
    }

    private List<String> getDeptTreeById(Integer id, List<String> deptNameList) {
        AuDepartmentDO departmentDO = departmentDao.selectById(id);
        if (departmentDO != null) {
            deptNameList.add(departmentDO.getDepartmentName());
            if (departmentDO.getParentId().equals(0)) {
                return deptNameList;
            } else {
                getDeptTreeById(departmentDO.getParentId(), deptNameList);
            }
        }
        return deptNameList;
    }

}
