package com.skr.mdm.api.system.excel;

import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.skr.mdm.api.system.service.DepartmentService;
import com.skr.mdm.bean.dto.DepartmentImportFailDTO;
import com.skr.mdm.constant.MinIoMessageConstants;
import com.skr.mdm.constant.RegexConstants;
import com.skr.mdm.dao.AuDepartmentDao;
import com.skr.mdm.entity.AuDepartmentDO;
import com.skr.mdm.manager.oss.MinIoManager;
import com.skr.mdm.util.ServerSystemUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author nfq
 * @date 2022/11/17 10:41
 */
@Slf4j
public class DepartmentImportDataListener extends AnalysisEventListener<DepartmentImportData> {

    private final AuDepartmentDao departmentDao;

    private final DepartmentService departmentService;
    private final Integer companyId;
    /**
     * 导入的部门全路径数据集合
     */
    private final Set<String> departmentSet = Sets.newHashSetWithExpectedSize(64);
    /**
     * 导入失败数据集合
     */
    @Getter
    private final List<DepartmentImportFailDTO> departmentImportFailDTOList = Lists.newArrayListWithCapacity(64);
    /**
     * 部门路径Map 如：key=“天宽”或“天宽/天津分公司”
     */
    private Map<String, Integer> deptPathMap;
    @Setter
    private MinIoManager minIoManager;
    /**
     * 成功导入的部门ID集合
     */
    @Getter
    private final Set<Integer> successDeptIdSet = Sets.newHashSetWithExpectedSize(64);

    /**
     * 导入总条数
     */
    @Getter
    private int allCount = 0;
    /**
     * 成功条数
     */
    @Getter
    private int successCount = 0;
    /**
     * 失败条数
     */
    @Getter
    private int failCount = 0;

    @Getter
    private String failFileUrl;

    private int headRowNumber = 1;

    public DepartmentImportDataListener(DepartmentService departmentService, AuDepartmentDao departmentDao, Integer companyId) {
        this.departmentService = departmentService;
        this.departmentDao = departmentDao;
        this.companyId = companyId;
        initData();
    }

    private static boolean checkExcelHead(Map<Integer, String> headMap) {
        return !headMap.containsKey(0) || !headMap.get(0).equals("部门");
    }

    private void initData() {
        deptPathMap = departmentService.getDeptPathMap(companyId);
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        // 第二行表头校验包括失败信息
        if (headRowNumber == 2) {
            // 表头数量校验
            if (headMap.size() != 1 && headMap.size() != 2) {
                throw new ExcelAnalysisException("您上传的文件格式与模板格式不一致，请检查后重新上传");
            }
            // 表头值校验
            if (checkExcelHead(headMap)) {
                throw new ExcelAnalysisException("您上传的文件格式与模板格式不一致，请检查后重新上传");
            }
        }
        headRowNumber++;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DepartmentImportData data, AnalysisContext context) {
        log.info("部门导入解析到一条数据:{}", JSONUtil.toJsonStr(data));
        if (StringUtils.isNotBlank(data.getDepartment())) {
            allCount++;
            // 检查导入数据是否合法 如有空格自动去掉空格
            String department = StringUtils.deleteWhitespace(data.getDepartment());
            // excel中数据判重
            if (departmentSet.contains(department)) {
                addDepartmentImportFailDTO(data, 1, "部门与导入文件中数据重复，请检查");
                failCount++;
                return;
            }
            departmentSet.add(department);
            // 检查数据库数据重复
            List<String> deptNameList = Arrays.stream(StringUtils.split(department, "/")).collect(Collectors.toList());
            // 处理根部门 如“天宽”
            if (deptNameList.size() == 1) {
                saveRootDept(data, deptNameList, true);
                return;
            }
            // 多级部门处理 如“天宽/天津分公司/研发部”
            saveMultilevelDept(data, department, deptNameList);
        }
    }

    private void addDepartmentImportFailDTO(DepartmentImportData data, Integer status, String msg) {
        DepartmentImportFailDTO departmentImportFailDTO = DepartmentImportFailDTO.builder()
                .department(data.getDepartment())
                .status(status)
                .msg(msg)
                .build();
        departmentImportFailDTOList.add(departmentImportFailDTO);
    }

    private void saveMultilevelDept(DepartmentImportData data, String department, List<String> deptNameList) {
        if (deptPathMap.containsKey(department)) {
            addDepartmentImportFailDTO(data, 3, "该部门系统中已存在");
            failCount++;
            return;
        }
        Integer parentId;
        for (int i = 0; i < deptNameList.size(); i++) {
            // 根部门处理
            if (i == 0) {
                saveRootDept(data, deptNameList, false);
                continue;
            }
            String currentDeptName = deptNameList.get(i);
            if (!Pattern.matches(RegexConstants.DEPT_NAME_REGEX, currentDeptName)) {
                addDepartmentImportFailDTO(data, 4, "部门格式错误");
                failCount++;
                return;
            }
            // 查询上级部门完整路径
            String parentPath = StringUtils.substringBefore(department, "/".concat(currentDeptName));
            // 由于前面已添加完成根级部门，上级部门一定已存在
            if (deptPathMap.containsKey(parentPath)) {
                parentId = deptPathMap.get(parentPath);
                String deptPath = parentPath.concat("/").concat(currentDeptName);
                // 查询当前部门是否存在
                if (!deptPathMap.containsKey(deptPath)) {
                    // 当前部门不存在 添加
                    saveDept(parentId, currentDeptName, deptPath);
                    // 导入数据一行添加成功
                    if (i == deptNameList.size() - 1) {
                        successCount++;
                    }
                }
            }
        }
    }

    private void saveRootDept(DepartmentImportData data, List<String> deptNameList, boolean isOnlyRoot) {
        // 查询根部门是否存在
        String deptName = deptNameList.get(0);
        if (!Pattern.matches(RegexConstants.DEPT_NAME_REGEX, deptName)) {
            addDepartmentImportFailDTO(data, 4, "部门格式错误");
            failCount++;
            return;
        }
        if (deptPathMap.containsKey(deptName)) {
            if (deptNameList.size() == 1) {
                addDepartmentImportFailDTO(data, 3, "系统中已存在");
                failCount++;
            }
            return;
        }
        // 不存在根部门
        saveDept(0, deptName, deptName);
        if (isOnlyRoot) {
            successCount++;
        }
    }

    private void saveDept(Integer parentId, String currentDeptName, String deptPath) {
        AuDepartmentDO departmentDO = AuDepartmentDO.builder()
                .parentId(parentId)
                .departmentName(currentDeptName)
                .companyId(companyId)
                .isDefault(0)
                .weight(0)
                .build();
        departmentDao.insert(departmentDO);
        deptPathMap.put(deptPath, departmentDO.getId());
        successDeptIdSet.add(departmentDO.getId());
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("部门导入所有数据解析完成 总条数：{} 成功条数：{} 失败条数：{}", allCount, successCount, failCount);
        // 生成错误报告，并上传到minio
        uploadErrorReport();
        departmentSet.clear();
        deptPathMap.clear();
    }

    private void uploadErrorReport() {
        if (CollectionUtils.isNotEmpty(departmentImportFailDTOList)) {
            ClassPathResource classPathResource = new ClassPathResource("static/部门导入失败模板.xlsx");
            String fileName = "部门导入失败报告" + System.currentTimeMillis() + ".xlsx";
            String filePath = ServerSystemUtil.userDir() + File.separator + fileName;
            File file = new File(filePath);
            try (InputStream inputStream = classPathResource.getInputStream()) {
                EasyExcel.write(file)
                        .withTemplate(inputStream)
                        .sheet("部门导入模板")
                        .doWrite(departmentImportFailDTOList);
            } catch (IOException e) {
                log.error("生成部门导入错误报告异常", e);
            }
            if (file.exists()) {
                String minioObject = MinIoMessageConstants.IMPORT_ERROR + fileName;
                failFileUrl = minIoManager.putObjectMinIo(companyId, minioObject, file, false);
                log.info("部门导入错误报告上传成功");
                // 清除缓存数据
                departmentImportFailDTOList.clear();
                boolean isDelete = file.delete();
                if (isDelete) {
                    log.info("部门导入错误报告本地文件缓存清除成功");
                }
            }
        }
    }
}
