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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.paper.library.business.bean.vo.FilePathVo;
import com.qmth.paper.library.business.bean.vo.PathSequenceVo;
import com.qmth.paper.library.business.entity.PaperScanTask;
import com.qmth.paper.library.business.service.BasicExamService;
import com.qmth.paper.library.business.service.BusinessCacheService;
import com.qmth.paper.library.business.service.FileUploadService;
import com.qmth.paper.library.business.service.PaperLibraryCommonService;
import com.qmth.paper.library.common.bean.dto.OrgDto;
import com.qmth.paper.library.common.config.DictionaryConfig;
import com.qmth.paper.library.common.contant.SpringContextHolder;
import com.qmth.paper.library.common.contant.SystemConstant;
import com.qmth.paper.library.common.entity.*;
import com.qmth.paper.library.common.enums.*;
import com.qmth.paper.library.common.service.*;
import com.qmth.paper.library.common.util.FileStoreUtil;
import com.qmth.paper.library.common.util.ResultUtil;
import com.qmth.paper.library.common.util.ServletUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

/**
 * @Description: 公共服务service impl
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2021/3/26
 */
@Service
public class PaperLibraryCommonServiceImpl implements PaperLibraryCommonService {
    private final static Logger log = LoggerFactory.getLogger(PaperLibraryCommonServiceImpl.class);
    @Resource
    private BasicSemesterService basicSemesterService;
    @Resource
    private BasicExamService basicExamService;
    @Resource
    private BusinessCacheService businessCacheService;
    @Resource
    private DictionaryConfig dictionaryConfig;
    @Resource
    private BasicAttachmentService basicAttachmentService;
    @Resource
    private SysOrgService sysOrgService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private FileUploadService fileUploadService;
    @Resource
    private TBTaskService tbTaskService;
    @Resource
    private FileStoreUtil fileStoreUtil;
    @Resource
    private BasicSchoolService basicSchoolService;

    @Override
    public String getScanTaskCode(Long schoolId) {
        return SystemConstant.SCAN_TASK_CODE_PREFIX + String.format("%07d", businessCacheService.getScanTaskCodeSequence(schoolId));
    }


    /**
     * 保存附件公用
     *
     * @param file
     * @param md5
     * @param type
     * @param objId
     * @return
     */
    @Override
    public BasicAttachment saveAttachmentCommon(MultipartFile file, String md5, UploadFileEnum type, Long objId) {
        if (Objects.isNull(type)) {
            throw ExceptionResultEnum.ATTACHMENT_TYPE_EMPTY.exception();
        }
        BasicAttachment basicAttachment = null;
        try {
            SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
            int temp = file.getOriginalFilename().lastIndexOf(".");
            String fileName = file.getOriginalFilename().substring(0, temp);
            String format = file.getOriginalFilename().substring(temp, file.getOriginalFilename().length());
            List<String> attachmentTypeList = dictionaryConfig.sysDomain().getAttachmentType();
            if (Objects.nonNull(format)) {
                long count = attachmentTypeList.stream().filter(s -> format.equalsIgnoreCase(s)).count();
                if (count == 0) {
                    throw ExceptionResultEnum.ERROR.exception("文件格式只能为" + attachmentTypeList.toString());
                }
            }
            int attachmentLength = dictionaryConfig.sysDomain().getAttachmentLength().intValue();
            if (Objects.nonNull(fileName) && fileName.length() > attachmentLength) {
                throw ExceptionResultEnum.ERROR.exception("文件名长度不能超过" + attachmentLength + "个字符");
            }
            long size = file.getSize();
            BigDecimal b = new BigDecimal(size);
            BigDecimal num = new BigDecimal(1024);
            b = b.divide(num, 2, BigDecimal.ROUND_HALF_UP).divide(num, 2, BigDecimal.ROUND_HALF_UP)
                    .setScale(2, BigDecimal.ROUND_HALF_UP);
            double attachmentSize = dictionaryConfig.sysDomain().getAttachmentSize().doubleValue();
            if (b.doubleValue() > attachmentSize) {
                throw ExceptionResultEnum.ERROR.exception("文件大小不能超过" + attachmentSize + "MB");
            }
            log.info("fileName:{}", fileName);
            log.info("format:{}", format);
            log.info("size:{}", b);
            log.info("getOriginalFilename:{}", file.getOriginalFilename());
            String fileMd5 = DigestUtils.md5Hex(file.getBytes());
            log.info("fileMd5:{}", fileMd5);
            log.info("md5:{}", md5);
            if (!Objects.equals(fileMd5, md5)) {
                throw ExceptionResultEnum.MD5_EQUALS_FALSE.exception();
            }

            JSONObject jsonObject = new JSONObject();
            String rootPath = fileStoreUtil.buildPath(type, true);
            String dirName = rootPath + File.separator + SystemConstant.getUuid() + format;
            fileStoreUtil.fileUpload(dirName, file.getInputStream(), md5);
            jsonObject.put(SystemConstant.TYPE, fileStoreUtil.getType());
            jsonObject.put(SystemConstant.PATH, dirName);
            jsonObject.put(SystemConstant.UPLOAD_TYPE, type);

            basicAttachment = new BasicAttachment(jsonObject.toJSONString(), fileName, format, b, fileMd5, requestUser.getId(), objId);
            basicAttachmentService.save(basicAttachment);

            // pdf需要读取总页数
            int pages = 0;
            if (".pdf".equals(format)) {
                /*  itextpdf5的写法
                    PdfReader pdfReader = new PdfReader(file.getBytes());
                    pages = pdfReader.getNumberOfPages();
                */
                //itextpdf7的写法
                PdfReader pdfReader = new PdfReader(file.getInputStream());
                PdfDocument pdfDoc = new PdfDocument(pdfReader);
                pages = pdfDoc.getNumberOfPages();
                pdfDoc.close();
                pdfReader.close();
            }
            basicAttachment.setPages(pages);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            basicAttachmentService.deleteAttachment(basicAttachment);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return basicAttachment;
    }

    /**
     * 保存附件
     *
     * @param file
     * @param md5
     * @param type
     * @return
     */
    @Override
    @Transactional
    public BasicAttachment saveAttachment(MultipartFile file, String md5, UploadFileEnum type) {
        return saveAttachmentCommon(file, md5, type, null);
    }

    /**
     * 机构禁用
     *
     * @param org
     * @return
     */
    @Override
    public boolean enable(SysOrg org) {
        // 禁用时，校验机构
        if (!org.getEnable()) {
            validateOrg(org.getId());
        }
        UpdateWrapper<SysOrg> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(SysOrg::getEnable, org.getEnable()).eq(SysOrg::getId, org.getId());
        return sysOrgService.update(updateWrapper);
    }

    /**
     * 机构删除
     *
     * @param ids
     * @return
     */
    @Transactional
    @Override
    public boolean sysOrgRemove(String ids) {
        String[] idsArray = ids.split(",");
        for (String id : idsArray) {
            Long idLong = Long.valueOf(id);
            // 机构校验
            validateOrg(idLong);
            sysOrgService.removeById(idLong);
        }
        return true;
    }

    private void validateOrg(Long id) {
        // 机构下是否有用户
        List<SysUser> sysUsers = sysUserService.listByOrgId(id);
        if (sysUsers != null && sysUsers.size() > 0) {
            throw ExceptionResultEnum.ERROR.exception("该机构下有用户，不能删除");
        }

        // 机构下是否有子机构
        QueryWrapper<SysOrg> orgQueryWrapper = new QueryWrapper<>();
        orgQueryWrapper.lambda().eq(SysOrg::getParentId, id);
        List<SysOrg> sysOrgs = sysOrgService.list(orgQueryWrapper);
        if (sysOrgs != null && sysOrgs.size() > 0) {
            throw ExceptionResultEnum.ERROR.exception("该机构下有子机构，不能删除");
        }
    }

    /**
     * 保存任务
     *
     * @param file
     * @param taskTypeEnum
     * @return
     */
    @Override
    @Transactional
    public Map<String, Object> saveTask(MultipartFile file, TaskTypeEnum taskTypeEnum) {
        BasicAttachment basicAttachment = null;
        Map<String, Object> map = null;
        try {
            PaperLibraryCommonService paperLibraryCommonService = SpringContextHolder.getBean(PaperLibraryCommonService.class);
            basicAttachment = paperLibraryCommonService.saveAttachment(file, ServletUtil.getRequestMd5(), UploadFileEnum.FILE);
            if (Objects.isNull(basicAttachment)) {
                throw ExceptionResultEnum.ATTACHMENT_ERROR.exception();
            } else {
                map = new HashMap<>();
                TBTask tbTask = tbTaskService.saveTaskCommon(basicAttachment, taskTypeEnum, map, null);
                tbTaskService.save(tbTask);
            }
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (Objects.nonNull(basicAttachment)) {
                basicAttachmentService.deleteAttachment(basicAttachment);
            }
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return map;
    }

    @Override
    @Transactional
    public Map<String, Object> saveTask(MultipartFile file, Long examId, TaskTypeEnum taskTypeEnum) {
        Map<String, Object> map = new HashMap<>();
        try {
            String rootPath = fileStoreUtil.buildPath(UploadFileEnum.FILE, true);
            String dirName = rootPath + File.separator + SystemConstant.getUuid() + "." + FilenameUtils.getExtension(file.getOriginalFilename());
            FilePathVo filePathVo = fileUploadService.uploadFile(file, UploadFileEnum.FILE, dirName);
            SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
            TBTask tbTask = new TBTask(taskTypeEnum, TaskStatusEnum.INIT, file.getOriginalFilename(), JSON.toJSONString(filePathVo), requestUser.getId(), requestUser.getSchoolId());
            tbTask.setExamId(examId);
            tbTaskService.save(tbTask);

            map.putIfAbsent(SystemConstant.TASK, tbTask);
            map.putIfAbsent(SystemConstant.USER, requestUser);
            map.computeIfAbsent(SystemConstant.TB_TASK_ID, v -> tbTask.getId());
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return map;
    }

    @Override
    public PathSequenceVo saveLibraryFile(PaperScanTask paperScanTask, ExamStudent examStudent, UploadFileEnum uploadFileEnum, int studentSequence, MultipartFile... files) {
        BasicSchool basicSchool = basicSchoolService.getById(paperScanTask.getSchoolId());
        BasicExam basicExam = basicExamService.getById(paperScanTask.getExamId());
        BasicSemester basicSemester = basicSemesterService.getById(basicExam.getSemesterId());
        String courseNameCode = String.format("%s(%s)", paperScanTask.getCourseName(), paperScanTask.getCourseCode());
        String rootPath = fileStoreUtil.buildPath(UploadFileEnum.IMAGE, false, basicSchool.getName(), basicSemester.getName(), basicExam.getName(), courseNameCode);
        if (StoreTypeEnum.ROOM.equals(paperScanTask.getStoreType())) {
            rootPath = fileStoreUtil.buildPath(UploadFileEnum.IMAGE, false, basicSchool.getName(), basicSemester.getName(), basicExam.getName(), courseNameCode, examStudent.getExamRoom());
        } else if (StoreTypeEnum.CLASS.equals(paperScanTask.getStoreType())) {
            rootPath = fileStoreUtil.buildPath(UploadFileEnum.IMAGE, false, basicSchool.getName(), basicSemester.getName(), basicExam.getName(), courseNameCode, examStudent.getClassName());
        } else if (StoreTypeEnum.COURSE.equals(paperScanTask.getStoreType())) {
            rootPath = fileStoreUtil.buildPath(UploadFileEnum.IMAGE, false, basicSchool.getName(), basicSemester.getName(), basicExam.getName(), courseNameCode, examStudent.getStudentCode());
        }
        // 根目录
        List<FilePathVo> objects = new ArrayList<>();
        /*int sequence = businessCacheService.getSequence(paperScanTask.getId(), examStudent.getStudentCode());
        String sequenceStr = String.valueOf(sequence);*/
        try {
            int idx = 1;
            //以前的方式
            // String prefix = examStudent.getStudentCode() + "-" + sequenceStr;
            //现在的方式
            String prefix = examStudent.getStudentCode() + "-" + studentSequence;
            for (MultipartFile file : files) {
                String fileName = file.getOriginalFilename();
                String suffix = fileName.substring(fileName.lastIndexOf("."));
                // 目标文件名
                String dirName = rootPath + File.separator + prefix + "-" + idx + suffix;
                FilePathVo filePathVo = fileUploadService.uploadFile(file, uploadFileEnum, dirName);
                objects.add(filePathVo);
                idx++;
            }
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return new PathSequenceVo(JSON.toJSONString(objects), studentSequence);
    }

    @Override
    public PathSequenceVo saveLibraryOtherFile(PaperScanTask paperScanTask, UploadFileEnum uploadFileEnum, String fileNamePrefix, String classRoomName, MultipartFile... files) {
        BasicSchool basicSchool = basicSchoolService.getById(paperScanTask.getSchoolId());
        BasicExam basicExam = basicExamService.getById(paperScanTask.getExamId());
        BasicSemester basicSemester = basicSemesterService.getById(basicExam.getSemesterId());
        String courseNameCode = String.format("%s(%s)", paperScanTask.getCourseName(), paperScanTask.getCourseCode());

        String rootPath;
        if(StringUtils.isEmpty(classRoomName)) {
            rootPath = fileStoreUtil.buildPath(UploadFileEnum.IMAGE, false, basicSchool.getName(), basicSemester.getName(), basicExam.getName(), courseNameCode, "other");
        } else {
            rootPath = fileStoreUtil.buildPath(UploadFileEnum.IMAGE, false, basicSchool.getName(), basicSemester.getName(), basicExam.getName(), courseNameCode, classRoomName, "other");
        }

        // 根目录
        List<FilePathVo> objects = new ArrayList<>();
        try {
            int idx = 1;
            for (MultipartFile file : files) {
                String fileName = file.getOriginalFilename();
                String suffix = fileName.substring(fileName.lastIndexOf("."));
                // 目标文件名
                String dirName = rootPath + File.separator + fileNamePrefix + "-" + idx + suffix;
                FilePathVo filePathVo = fileUploadService.uploadFile(file, uploadFileEnum, dirName);
                objects.add(filePathVo);
                idx++;
            }
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return new PathSequenceVo(JSON.toJSONString(objects), null);
    }

    @Override
    public File downloadFile(String rootPath, String filePath, String uploadType, String type) {
        File localFile = new File(rootPath, subFileName(filePath));
        if (type.equals(SystemConstant.OSS)) {
            try {
                return fileStoreUtil.fileDownload(filePath, localFile.getPath());
            } catch (Exception e) {
                throw ExceptionResultEnum.ERROR.exception("从OSS上下载文件失败");
            }
        } else {
            File file = new File(filePath);
            if (!file.exists()) {
                throw ExceptionResultEnum.ERROR.exception("本地文件不存在");
            }
            try {
                if (!localFile.exists()) {
                    localFile.getParentFile().mkdirs(); //目标文件目录不存在的话需要创建目录
                }
                FileUtils.copyFile(file, localFile);
                return localFile;
            } catch (IOException e) {
                throw ExceptionResultEnum.ERROR.exception("本地复制文件失败");
            }
        }
    }

    private void getOrgIds(Set<Long> stringSet, List<OrgDto> orgDtoList, Long parentId) {
        for (OrgDto orgDto : orgDtoList) {
            Long tempParentId = orgDto.getParentId();
            if (parentId.equals(tempParentId) && stringSet.contains(parentId)) {
                stringSet.add(orgDto.getId());
            }
            if (orgDto.getChildren().isEmpty()) {
                continue;
            }
            getOrgIds(stringSet, orgDto.getChildren(), orgDto.getId());
        }
    }

    /**
     * 截取文件名
     *
     * @param path 文件路径
     */
    public String subFileName(String path) {
        path = path.replaceAll("\\\\", "/");
        return path.substring(path.lastIndexOf("/") + 1);
    }

    /**
     * 截取文件名后缀
     *
     * @param path 文件路径
     */
    public String subFileSuffix(String path) {
        return path.substring(path.lastIndexOf("."));
    }

}
