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

import cn.hutool.core.collection.ListUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.skr.mdm.api.content.service.ContentDocumentService;
import com.skr.mdm.bean.bo.QuartzJobBO;
import com.skr.mdm.bean.qo.BaseQueryQO;
import com.skr.mdm.bean.qo.StrategyUserQO;
import com.skr.mdm.bean.qo.content.DocumentIssuedQO;
import com.skr.mdm.bean.so.content.ContentDocumentSO;
import com.skr.mdm.constant.JobConstant;
import com.skr.mdm.constant.MinIoMessageConstants;
import com.skr.mdm.dao.ContentDocumentDao;
import com.skr.mdm.dao.ContentDocumentUserDao;
import com.skr.mdm.dto.LoginAdminDTO;
import com.skr.mdm.entity.ContentDocumentDO;
import com.skr.mdm.entity.ContentDocumentUserDO;
import com.skr.mdm.enums.*;
import com.skr.mdm.handle.IssuedUserHandler;
import com.skr.mdm.handle.LogAdminHandler;
import com.skr.mdm.manager.DeviceManager;
import com.skr.mdm.manager.oss.MinIoManager;
import com.skr.mdm.quartz.SchedulerHandler;
import com.skr.mdm.quartz.job.ContentJob;
import com.skr.mdm.result.ResponseResult;
import com.skr.mdm.util.JobUtil;
import com.skr.mdm.util.MdmListUtil;
import com.skr.mdm.util.PageUtil;
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.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;


@Service
@Slf4j
public class ContentDocumentServiceImpl extends ServiceImpl<ContentDocumentDao, ContentDocumentDO> implements ContentDocumentService {

    private final String MODULE = "文档管理";

    @Autowired
    private ContentDocumentDao contentDocumentDao;
    @Autowired
    private ContentDocumentUserDao contentDocumentUserDao;
    @Autowired
    private IssuedUserHandler issuedUserHandler;
    @Autowired
    private MinIoManager minIoManager;
    @Autowired
    private SchedulerHandler schedulerHandler;
    @Autowired
    private DeviceManager deviceManager;
    @Autowired
    private LogAdminHandler logAdminHandler;


    @Override
    public ResponseResult uploadAndSaveFile(MultipartFile file, Integer globalType, LoginAdminDTO loginAdminDTO) throws Exception {
        String filename = file.getOriginalFilename();
        if (StringUtils.isNotEmpty(filename)) {
            filename = filename.replaceAll(" ", "");
            Long count = new LambdaQueryChainWrapper<>(contentDocumentDao)
                    .eq(ContentDocumentDO::getFileName, filename)
                    .eq(ContentDocumentDO::getCompanyId, loginAdminDTO.getCompanyId())
                    .count();
            if (count > 0) {
                return ResponseResult.failure(ResultCodeEnum.SERVER_MESSAGE_EXIST);
            }
        }
        // 存储路径
        String object = MinIoMessageConstants.MINIO_CONTENT_OBJECT.concat("/" + filename);
        String fileUrl = null;
        fileUrl = minIoManager.putObjectMinIo(loginAdminDTO.getCompanyId(), object, file, true);
        if (StringUtils.contains(fileUrl, "?")) {
            fileUrl = StringUtils.substringBefore(fileUrl, "?");
        }
        BigDecimal bg = BigDecimal.valueOf(file.getSize() / (double) (1024 * 1024));
        ContentDocumentDO contentDocumentDO = ContentDocumentDO.builder()
                .companyId(loginAdminDTO.getCompanyId())
                .fileName(filename)
                .fileUrl(fileUrl)
                .operator(loginAdminDTO.getAdminName())
                .globalType(globalType)
                .fileSize(bg.setScale(2, RoundingMode.HALF_UP).doubleValue() + "M")
                .build();
        contentDocumentDao.insert(contentDocumentDO);
        // log
        String content = "文档上传 文档名：" + contentDocumentDO.getFileName();
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.CONTENT_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult list(BaseQueryQO baseQueryQO, LoginAdminDTO loginAdminDTO) {
        PageHelper.startPage(baseQueryQO.getPageNum(), baseQueryQO.getPageSize());
        return ResponseResult.success(PageUtil.getPageResult(contentDocumentDao.listContentVO(loginAdminDTO.getCompanyId())));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult issuedDocument(DocumentIssuedQO documentIssuedQO, LoginAdminDTO loginAdminDTO) {
        // 更新全局部门ID
        if (IssuedTypeEnum.DEPT_GLOBAL.getIssuedType().equals(documentIssuedQO.getGlobalType()) &&
                CollectionUtils.isNotEmpty(documentIssuedQO.getCandidateIdList())) {
            new LambdaUpdateChainWrapper<>(contentDocumentDao)
                    .set(ContentDocumentDO::getDepartmentIds, JSONUtil.toJsonStr(documentIssuedQO.getCandidateIdList()))
                    .in(ContentDocumentDO::getId, documentIssuedQO.getStrategyIdList())
                    .update();
        }
        // 更新推送数据
        if (IssuedTypeEnum.DEPT_GLOBAL.getIssuedType().equals(documentIssuedQO.getGlobalType()) ||
                IssuedTypeEnum.GLOBAL.getIssuedType().equals(documentIssuedQO.getGlobalType())) {
            updateDocumentPush(documentIssuedQO);
        }

        List<Integer> equipmentIdList = deviceManager.getDeviceIdList(documentIssuedQO.getGlobalType(),
                documentIssuedQO.getCandidateIdList(), loginAdminDTO.getCompanyId());
        // 操作日志
        adminLogIssuedStrategy(documentIssuedQO, loginAdminDTO, equipmentIdList);
        // 保存并下发
        if (CollectionUtils.isNotEmpty(equipmentIdList)) {
            // copy new equipmentIdList
            List<ContentDocumentSO> contentDocumentSOList = Lists.newArrayListWithCapacity(documentIssuedQO.getStrategyIdList().size());
            Date date = new Date();
            documentIssuedQO.getStrategyIdList().forEach(strategyId -> {
                List<Integer> listEquipmentIdByStrategyIds = contentDocumentUserDao.listEquipmentIdByStrategyIds(ListUtil.toList(strategyId));
                List<Integer> addEquipmentIdList = MdmListUtil.difference(equipmentIdList, listEquipmentIdByStrategyIds);
                List<Integer> updateEquipmentIdList = MdmListUtil.intersection(equipmentIdList, listEquipmentIdByStrategyIds);
                // 构建下发数据
                ContentDocumentSO contentDocumentSO = getContentDocumentSO(documentIssuedQO, strategyId);
                Integer state = StrategyEquipmentStateEnum.ISSUING.getState();
                // 定时推送
                if (documentIssuedQO.getSendPushTime() != null && documentIssuedQO.getSendPushTime().after(new Date())) {
                    state = StrategyEquipmentStateEnum.WAIT_ISSUED.getState();
                    contentDocumentSO.setSendTime(documentIssuedQO.getSendPushTime());
                    QuartzJobBO quartzJobBO = QuartzJobBO.builder()
                            .cmd(SendCmdConfigEnum.CONTENT_SILENT_INSTALLATION.getCmd())
                            .message(JSONUtil.toJsonStr(Collections.singletonList(contentDocumentSO)))
                            .id(strategyId)
                            .companyId(loginAdminDTO.getCompanyId())
                            .executeTime(documentIssuedQO.getSendPushTime())
                            .prefix(JobConstant.JOB_CONTENT_KEY_PREFIX)
                            .build();
                    schedulerHandler.handleJob(quartzJobBO, ContentJob.class);
                }
                // 新增
                saveStrategyUser(documentIssuedQO, addEquipmentIdList, date, strategyId, state);
                // 更新
                updateStrategyUser(documentIssuedQO, date, strategyId, updateEquipmentIdList, state);
                contentDocumentSOList.add(contentDocumentSO);

            });
            // 下发
            if (documentIssuedQO.getSendPushTime() == null) {
                issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(),
                        SendCmdConfigEnum.CONTENT_SILENT_INSTALLATION.getCmd(), JSONUtil.toJsonStr(contentDocumentSOList));
            }
        }
        return ResponseResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult deleteDocument(List<Integer> strategyIdList, LoginAdminDTO loginAdminDTO) throws Exception {
        List<String> sourceNameList = new ArrayList<>();
        List<String> quartzKeyList = new ArrayList<>();
        // 下发
        List<Integer> equipmentIdList = contentDocumentUserDao.listEquipmentIdByStrategyIds(strategyIdList);
        issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(),
                SendCmdConfigEnum.CONTENT_SILENT_UNINSTALL.getCmd(), JSONUtil.toJsonStr(strategyIdList));

        for (Integer strategyId : strategyIdList) {
            ContentDocumentDO contentDocumentDO = contentDocumentDao.selectById(strategyId);
            sourceNameList.add(contentDocumentDO.getFileUrl());
            List<String> sendPushTimeList = contentDocumentUserDao.selectSendPushTimeList(strategyId);
            if (CollectionUtils.isNotEmpty(sendPushTimeList)) {
                for (String sendPushTime : sendPushTimeList) {
                    quartzKeyList.add(JobUtil.getQuartzKey(JobConstant.JOB_CONTENT_KEY_PREFIX, strategyId, sendPushTime));
                }
            }
        }
        // log
        adminLogDeleteStrategy(strategyIdList, loginAdminDTO);
        // 删除定时任务
        schedulerHandler.deleteJob(quartzKeyList);
        // 删除文件
        minIoManager.removeObject(sourceNameList);
        // 删除中间表
        contentDocumentUserDao.delete(Wrappers.lambdaQuery(ContentDocumentUserDO.class)
                .in(ContentDocumentUserDO::getFileId, strategyIdList));
        // 删除主表
        contentDocumentDao.deleteBatchIds(strategyIdList);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult listDocumentUser(StrategyUserQO strategyUserQO) {
        PageHelper.startPage(strategyUserQO.getPageNum(), strategyUserQO.getPageSize());
        return ResponseResult.success(PageUtil.getPageResult(contentDocumentUserDao.listStrategyUser(
                strategyUserQO.getStrategyId(), strategyUserQO.getSearchType(), strategyUserQO.getSearchValue())));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult deleteDocumentUser(Integer strategyId, List<Integer> equipmentIdList, LoginAdminDTO loginAdminDTO) {
        // 下发
        issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(),
                SendCmdConfigEnum.CONTENT_SILENT_UNINSTALL.getCmd(), JSONUtil.toJsonStr(ListUtil.toList(strategyId)));
        // 删除
        contentDocumentUserDao.delete(new LambdaUpdateWrapper<ContentDocumentUserDO>()
                .eq(ContentDocumentUserDO::getFileId, strategyId)
                .in(ContentDocumentUserDO::getEquipmentId, equipmentIdList));

        ContentDocumentDO contentDocumentDO = contentDocumentDao.selectById(strategyId);
        String content = "文档作用人删除 文档名：" + contentDocumentDO.getFileName();
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.CONTENT_TYPE.getLogType(), content, MODULE, equipmentIdList);
        return ResponseResult.success();
    }


    private ContentDocumentSO getContentDocumentSO(DocumentIssuedQO documentIssuedQO, Integer strategyId) {
        ContentDocumentDO contentDocumentDO = contentDocumentDao.selectById(strategyId);
        ContentDocumentSO contentDocumentSO = new ContentDocumentSO();
        BeanUtils.copyProperties(documentIssuedQO, contentDocumentSO);
        contentDocumentSO.setFileSize(contentDocumentDO.getFileSize());
        contentDocumentSO.setFileName(contentDocumentDO.getFileName());
        contentDocumentSO.setId(contentDocumentDO.getId());
        contentDocumentSO.setFileUrl(contentDocumentDO.getFileUrl());
        contentDocumentSO.setSendTime(new Date());
        return contentDocumentSO;
    }

    private void updateStrategyUser(DocumentIssuedQO documentIssuedQO, Date date, Integer strategyId,
                                    List<Integer> equipmentIdList, Integer state) {
        if (CollectionUtils.isNotEmpty(equipmentIdList)) {
            new LambdaUpdateChainWrapper<>(contentDocumentUserDao)
                    .set(ContentDocumentUserDO::getState, state)
                    .set(ContentDocumentUserDO::getIssuedTime, date)
                    .set(ContentDocumentUserDO::getSendPushTime, documentIssuedQO.getSendPushTime())
                    .set(ContentDocumentUserDO::getReviewDelete, documentIssuedQO.getReviewDelete())
                    .set(ContentDocumentUserDO::getReviewDeleteTime, documentIssuedQO.getReviewDeleteTime())
                    .set(ContentDocumentUserDO::getAllowShare, documentIssuedQO.getAllowShare())
                    .set(ContentDocumentUserDO::getValidTime, documentIssuedQO.getValidTime())
                    .set(ContentDocumentUserDO::getWaterMark, documentIssuedQO.getWaterMark())
                    .in(ContentDocumentUserDO::getEquipmentId, equipmentIdList)
                    .eq(ContentDocumentUserDO::getFileId, strategyId)
                    .update();
        }
    }

    private void saveStrategyUser(DocumentIssuedQO documentIssuedQO, List<Integer> equipmentIdList, Date date,
                                  Integer strategyId, Integer state) {
        if (CollectionUtils.isNotEmpty(equipmentIdList)) {
            List<ContentDocumentUserDO> documentUserDOList = equipmentIdList.stream().map(equipmentId -> {
                ContentDocumentUserDO contentDocumentUserDO = ContentDocumentUserDO.builder()
                        .equipmentId(equipmentId)
                        .fileId(strategyId)
                        .state(state)
                        .issuedTime(date)
                        .build();
                BeanUtils.copyProperties(documentIssuedQO, contentDocumentUserDO);
                return contentDocumentUserDO;
            }).collect(Collectors.toList());
            contentDocumentUserDao.insertBatch(documentUserDOList);
        }
    }

    private void adminLogIssuedStrategy(DocumentIssuedQO documentIssuedQO, LoginAdminDTO admin, List<Integer> equipmentIdList) {
        String content = "";
        List<ContentDocumentDO> contentDocumentDOList = contentDocumentDao.selectBatchIds(documentIssuedQO.getStrategyIdList());
        if (CollectionUtils.isNotEmpty(contentDocumentDOList)) {
            List<String> strategyNameList = contentDocumentDOList.stream()
                    .map(ContentDocumentDO::getFileName)
                    .collect(Collectors.toList());
            content = "下发文档 文档名：" + StringUtils.join(strategyNameList, "，");
        }
        logAdminHandler.log(documentIssuedQO.getGlobalType(), documentIssuedQO.getCandidateIdList(), admin,
                LogAdminTypeEnum.CONTENT_TYPE.getLogType(), content, MODULE);
    }

    private void updateDocumentPush(DocumentIssuedQO documentIssuedQO) {
        new LambdaUpdateChainWrapper<>(contentDocumentDao)
                .set(ContentDocumentDO::getGlobalType, documentIssuedQO.getGlobalType())
                .set(ContentDocumentDO::getWaterMark, documentIssuedQO.getWaterMark())
                .set(ContentDocumentDO::getReviewDeleteTime, documentIssuedQO.getReviewDeleteTime())
                .set(ContentDocumentDO::getReviewDelete, documentIssuedQO.getReviewDelete())
                .set(ContentDocumentDO::getValidTime, documentIssuedQO.getValidTime())
                .set(ContentDocumentDO::getAllowShare, documentIssuedQO.getAllowShare())
                .set(ContentDocumentDO::getSendPushTime, documentIssuedQO.getSendPushTime())
                .in(ContentDocumentDO::getId, documentIssuedQO.getStrategyIdList())
                .update();
    }

    private void adminLogDeleteStrategy(List<Integer> strategyIdList, LoginAdminDTO loginAdminDTO) {
        strategyIdList.forEach(strategyId -> {
            ContentDocumentDO contentDocumentDO = contentDocumentDao.selectById(strategyId);
            List<Integer> listEquipmentIdByStrategyIds = contentDocumentUserDao.listEquipmentIdByStrategyIds(Collections.singletonList(strategyId));
            String content = "删除文档 文档名：" + contentDocumentDO.getFileName();
            logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.CONTENT_TYPE.getLogType(), content, MODULE, listEquipmentIdByStrategyIds);
        });
    }
}