package com.keyware.shandan.bianmu.service.impl;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.keyware.shandan.bianmu.entity.DirectoryVo;
import com.keyware.shandan.bianmu.entity.MetadataBasicVo;
import com.keyware.shandan.bianmu.entity.ReviewRecordVo;
import com.keyware.shandan.bianmu.enums.DirectoryType;
import com.keyware.shandan.bianmu.enums.ReviewEntityType;
import com.keyware.shandan.bianmu.enums.ReviewStatus;
import com.keyware.shandan.bianmu.mapper.ReviewRecordMapper;
import com.keyware.shandan.bianmu.service.DirectoryService;
import com.keyware.shandan.bianmu.service.MetadataService;
import com.keyware.shandan.bianmu.service.ReviewRecordService;
import com.keyware.shandan.common.constants.DirConstant;
import com.keyware.shandan.common.enums.NotificationType;
import com.keyware.shandan.common.service.BaseServiceImpl;
import com.keyware.shandan.common.util.StreamUtil;
import com.keyware.shandan.frame.annotation.DataPermissions;
import com.keyware.shandan.frame.config.security.SecurityUtil;
import com.keyware.shandan.system.entity.SysFile;
import com.keyware.shandan.system.entity.SysNotification;
import com.keyware.shandan.system.entity.SysUser;
import com.keyware.shandan.system.queue.provider.EsSysFileProvider;
import com.keyware.shandan.system.service.SysFileService;
import com.keyware.shandan.system.service.SysNotificationService;
import com.keyware.shandan.system.service.SysOrgService;
import com.keyware.shandan.system.utils.NotificationUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 审核记录表 服务实现类
 * </p>
 *
 * @author GuoXin
 * @since 2021-06-03
 */
@Service
@Transactional
public class ReviewRecordServiceImpl extends BaseServiceImpl<ReviewRecordMapper, ReviewRecordVo, String> implements ReviewRecordService {

    @Autowired
    private DirectoryService directoryService;

    @Autowired
    private MetadataService metadataService;

    @Autowired
    private SysOrgService orgService;

    @Autowired
    private SysNotificationService notificationService;

    @Autowired
    private SysFileService fileService;

    @Autowired
    private EsSysFileProvider esSysFileProvider;

    @Override
    @DataPermissions
    public List<ReviewRecordVo> list(QueryWrapper<ReviewRecordVo> wrapper) {
        return super.list(wrapper);
    }

    @Override
    @DataPermissions
    public Page<ReviewRecordVo> page(Page<ReviewRecordVo> page, QueryWrapper<ReviewRecordVo> wrapper) {
        return super.page(page, wrapper);
    }

    /**
     * 审核操作
     *
     * @param entityId   数据ID
     * @param entityType 数据对象类型
     * @param status     审核状态
     * @param opinion    审核意见
     * @return
     */
    @Override
    public Boolean review(String entityId, String entityType, String status,
                          String opinion, String metadataName, String proType) throws Exception {
        ReviewEntityType reviewEntityType = Enum.valueOf(ReviewEntityType.class, entityType);
        ReviewStatus reviewStatus = Enum.valueOf(ReviewStatus.class, status);
        if (ReviewEntityType.DIRECTORY.name().equals(entityType)) {
            DirectoryVo vo = directoryService.getById(entityId);
            Assert.notNull(vo, "资源目录审核异常，目录未找到！id=" + entityId);
            ReviewStatus oldStatus = vo.getReviewStatus();
            vo.setReviewStatus(reviewStatus);
            directoryService.updateById(vo);
            //如果审核通过则需要把目录下文件保存到ES
            if (vo.getReviewStatus() == ReviewStatus.SUBMITTED || vo.getReviewStatus() == ReviewStatus.PASS) {
                appendFileToEs(vo);
                //设置上级目录们为通过
                updateParentsStatus(vo, ReviewStatus.PASS);
                //设置子级目录们为通过
                updateDirAndChildrenStatus(vo, ReviewStatus.PASS);
            } else if (oldStatus.equals(ReviewStatus.PASS)) {
                removeFileFromEs(vo);
            }


        } else if (ReviewEntityType.METADATA.name().equals(entityType)) {
            MetadataBasicVo vo = metadataService.getById(entityId);
            Assert.notNull(vo, "数据资源审核异常，数据未找到！id=" + entityId);
            vo.setReviewStatus(reviewStatus);
            metadataService.updateById(vo);
        }

        ReviewRecordVo record = new ReviewRecordVo(entityId, reviewEntityType, reviewStatus, opinion);
        if (save(record)) {
            // 发送系统通知
            notificationService.sendNotification(newReviewNotify(record, metadataName, proType));
            return true;
        }

        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean metadataReview(String id, ReviewStatus status, String opinion, Boolean sendNotice) {
        ReviewEntityType type = ReviewEntityType.METADATA;
        MetadataBasicVo metadata = metadataService.getById(id);
        Assert.notNull(metadata, "数据资源审核异常，数据未找到！id=" + id);
        metadata.setReviewStatus(status);
        metadataService.updateById(metadata);
        ReviewRecordVo record = saveReviewRecord(id, type, status, opinion);
        if (sendNotice) {
            notificationService.sendNotification(newReviewNotify(record, metadata.getMetadataName(), metadata.getMetadataComment()));
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean directoryReview(String id, ReviewStatus status, String opinion, Boolean sendNotice) throws Exception {
        ReviewEntityType type = ReviewEntityType.DIRECTORY;
        DirectoryVo dir = directoryService.getById(id);
        boolean isDemotion = dir.getReviewStatus() == ReviewStatus.PASS;
        Assert.notNull(dir, "数据资源审核异常，数据未找到！id=" + id);
        dir.setReviewStatus(status);
        directoryService.updateById(dir);
        //修改所有子级目录的状态
        List<DirectoryVo> dirs = updateDirAndChildrenStatus(dir, status, opinion);
        if (status == ReviewStatus.SUBMITTED || status == ReviewStatus.PASS) {
            appendFileToEs(dir);
            //设置上级目录们为通过
        } else if (isDemotion) {
            removeFileFromEs(dir);
        }
        //保存审核记录
        ReviewRecordVo record = saveReviewRecord(id, type, status, opinion);
        if (sendNotice) {
            notificationService.sendNotification(newReviewNotify(record, dir.getDirectoryName(), dir.getDirectoryName()));
        }
        return true;
    }


    private ReviewRecordVo saveReviewRecord(String entityId, ReviewEntityType entityType, ReviewStatus reviewOperate, String reviewOpinion) {
        ReviewRecordVo record = new ReviewRecordVo(entityId, entityType, reviewOperate, reviewOpinion);
        save(record);
        return record;
    }

    /**
     * 将目录下的文件同步到ES
     *
     * @param dir 目录
     */
    private void appendFileToEs(DirectoryVo dir) {
        List<SysFile> files = getSysFileByDirs(dir);
        esSysFileProvider.appendQueue(files);
    }

    private List<SysFile> getSysFileByDirs(DirectoryVo dir) {
        QueryWrapper<SysFile> wrapper = new QueryWrapper<>();
        wrapper.exists("select D.ID from B_DIRECTORY D where d.RESOURCE_ID = SYS_FILE.ID and D.DIRECTORY_TYPE in('FILE','LINK_FILE') and D.DIRECTORY_PATH like '" + dir.getDirectoryPath() + "/%'");
        return fileService.list(wrapper);
    }

    /**
     * 删除文件在ES中的索引
     *
     * @param dir 目录
     */
    private void removeFileFromEs(DirectoryVo dir) {
        SysFile condition = new SysFile();
        condition.setEntityId(dir.getId());
        List<SysFile> files = fileService.list(new QueryWrapper<>(condition));
        fileService.removeFileFromEs(files);
    }

    /**
     * 递归更新所有父级目录为PASS
     *
     * @param dir 目录
     */
    private void updateParentsStatus(DirectoryVo dir, ReviewStatus status) throws Exception {
        DirectoryVo parent = directoryService.getById(dir.getParentId());
        if (parent != null) {
            parent.setReviewStatus(status);
            directoryService.updateById(parent);
            if (!DirConstant.DIR_ROOT_ID.equals(parent.getParentId())) {
                updateParentsStatus(parent, status);
            }
        }
    }

    private List<DirectoryVo> updateDirAndChildrenStatus(DirectoryVo dir, ReviewStatus status, String opinion) {
        List<DirectoryVo> list = updateDirAndChildrenStatus(dir, status);
        List<ReviewRecordVo> records = StreamUtil.as(list).map(d -> new ReviewRecordVo(d.getId(), ReviewEntityType.DIRECTORY, status, opinion)).toList();
        saveBatch(records);
        return list;
    }

    private List<DirectoryVo> updateDirAndChildrenStatus(DirectoryVo dir, ReviewStatus status) {
        QueryWrapper<DirectoryVo> wrapper = new QueryWrapper<>();
        wrapper.likeRight("DIRECTORY_PATH", dir.getDirectoryPath() + "/").in("DIRECTORY_TYPE", DirectoryType.DIRECTORY, DirectoryType.LINK_DIR);
        List<DirectoryVo> list = directoryService.list(wrapper);
        list.add(dir);
        list.forEach(item -> item.setReviewStatus(status));
        directoryService.saveOrUpdateBatch(list);
        return list;
    }

    /**
     * 生成审核通知
     *
     * @param record
     * @return
     */
    private SysNotification newReviewNotify(ReviewRecordVo record, String metadataName, String proType) {
        String content = genNotificationContent(record);
        String receiveIds = judgingReceiver(record);
        //数据资源
        if (record.getEntityType() == ReviewEntityType.METADATA) {
            if (record.getReviewOperate() == ReviewStatus.PASS) {
                return NotificationUtil.newNotification(NotificationType.METADATA_REVIEW_PASS, null, content, receiveIds, record.getReviewOpinion(), metadataName, proType);
            } else if (record.getReviewOperate() == ReviewStatus.FAIL) {
                return NotificationUtil.newNotification(NotificationType.METADATA_REVIEW_FAIL, null, content, receiveIds, record.getReviewOpinion(), metadataName, proType);
            }
            return NotificationUtil.newMetadataReviewNotify(content, receiveIds, record.getReviewOpinion(), metadataName, proType);
        } else {
            //目录资源
            if (record.getReviewOperate() == ReviewStatus.PASS) {
                return NotificationUtil.newNotification(NotificationType.DIRECTORY_REVIEW_PASS, null, content, receiveIds, record.getReviewOpinion(), metadataName, proType);
            } else if (record.getReviewOperate() == ReviewStatus.FAIL) {
                return NotificationUtil.newNotification(NotificationType.DIRECTORY_REVIEW_FAIL, null, content, receiveIds, record.getReviewOpinion(), metadataName, proType);
            }
            return NotificationUtil.newDirectoryReviewNotify(content, receiveIds, record.getReviewOpinion(), metadataName, proType);
        }
    }

    /**
     * 判断通知接收人
     *
     * @param reviewRecord
     */
    private String judgingReceiver(ReviewRecordVo reviewRecord) {
        String receiveId = "";
        SysUser currentUser = SecurityUtil.getLoginSysUser();
        if (reviewRecord.getReviewOperate() == ReviewStatus.SUBMITTED) {
            // 提交操作，接收人应该是当前用户所属部门管理员
            receiveId = orgService.getOrgAdmins(currentUser.getOrgId())
                    .stream().map(SysUser::getUserId)
                    .reduce("", (a, b) -> a + "," + b);
        } else {
            // 数据资源类型
            if (reviewRecord.getEntityType() == ReviewEntityType.METADATA) {
                receiveId = metadataService.getById(reviewRecord.getEntityId()).getCreateUser();
            } else {
                // 目录数据类型
                receiveId = directoryService.getById(reviewRecord.getEntityId()).getCreateUser();
            }
        }
        return receiveId;
    }

    /**
     * 生成通知内容
     *
     * @param record
     */
    private String genNotificationContent(ReviewRecordVo record) {
        String content = "";
        if (record.getReviewOperate() == ReviewStatus.SUBMITTED) {
            content = "您收到一条" + record.getEntityType().getRemark() + "审核，请查看";
        } else if (record.getReviewOperate() == ReviewStatus.PASS) {
            content = "您有一条" + record.getEntityType().getRemark() + "审核通过了";
        } else if (record.getReviewOperate() == ReviewStatus.FAIL) {
            content = "您有一条" + record.getEntityType().getRemark() + "审核失败";
        }
        return content;
    }
}
