package org.springblade.file.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.constant.DictTypeEnum;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.file.convert.FileTaskConvert;
import org.springblade.file.listener.UnzipStartEvent;
import org.springblade.file.pojo.dto.BusinessUnzipTaskAddDTO;
import org.springblade.file.pojo.dto.BusinessUnzipTaskQueryDTO;
import org.springblade.file.pojo.dto.BusinessUnzipTaskUpdateDTO;
import org.springblade.file.pojo.entity.Attachment;
import org.springblade.file.pojo.entity.BusinessUnzipTask;
import org.springblade.file.mapper.BusinessUnzipTaskMapper;
import org.springblade.file.pojo.entity.FileTask;
import org.springblade.file.pojo.enums.FileTaskStatus;
import org.springblade.file.pojo.enums.UnzipTask;
import org.springblade.file.pojo.vo.BusinessUnzipDetailListVO;
import org.springblade.file.pojo.vo.BusinessUnzipTaskListVO;
import org.springblade.file.pojo.vo.FileVO;
import org.springblade.file.service.IAttachmentService;
import org.springblade.file.service.IBusinessUnzipDetailService;
import org.springblade.file.service.IBusinessUnzipTaskService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.file.service.IFileTaskService;
import org.springblade.system.cache.DictCache;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;

/**
 * <p>
 * 业务解压任务表 服务实现类
 * </p>
 *
 * @author bfhuange
 * @since 2024-10-28
 */
@Slf4j
@AllArgsConstructor
@Service
public class BusinessUnzipTaskServiceImpl extends ServiceImpl<BusinessUnzipTaskMapper, BusinessUnzipTask> implements IBusinessUnzipTaskService {

    private final FileTaskConvert convert;
    private final IFileTaskService fileTaskService;
    private final IBusinessUnzipDetailService detailService;
    private final IAttachmentService attachmentService;
    private final ApplicationContext applicationContext;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean save(BusinessUnzipTaskAddDTO param) {
        BusinessUnzipTask addParam = convert.dto2entity(param);
        String status = getStatus(param.getRelateType(), param.getRelateId());
        addParam.setStatus(status);
        boolean result = this.save(addParam);
        if (result && UnzipTask.Status.UPLOADED.getCode().equals(status)) {
            // 解压任务状态是上传完成，发布解压开始事件
            applicationContext.publishEvent(new UnzipStartEvent(addParam));
        }
        return result;
    }

    @Override
    public String getObjectKey(BusinessUnzipTask task) {
        return this.getFile(task).getObjectKey();
    }

    @Override
    public FileVO getFile(BusinessUnzipTask task) {
        Func.requireNotNull(task, "文件任务不存在");
        String relateType = task.getRelateType();
        if (StringUtil.isBlank(relateType)) {
            throw new ServiceException("文件任务关联类型不能为空");
        }
        Long relateId = task.getRelateId();
        Func.requireNotNull(relateId, "文件任务关联id不能为空");
        if (UnzipTask.RelateType.FILE_TASK.getCode().equals(relateType)) {
            // 文件任务关联类型
            FileTask fileTask = fileTaskService.getById(relateId);
            Func.requireNotNull(fileTask, "文件任务不存在");
            return new FileVO(fileTask.getObjectKey(), fileTask.getMd5());
        }
        if (UnzipTask.RelateType.ATTACHMENT.getCode().equals(relateType)) {
            // 附件关联类型
            Attachment attachment = attachmentService.getById(relateId);
            Func.requireNotNull(attachment, "附件不存在");
            // 附件暂时没有md5
            return new FileVO(attachment.getObjectKey(), null);
        }
        throw new ServiceException("文件任务关联类型错误");
    }

    @Override
    public IPage<BusinessUnzipTaskListVO> selectPage(IPage<BusinessUnzipTask> page, BusinessUnzipTaskQueryDTO param) {
        List<BusinessUnzipTaskListVO> list = baseMapper.selectTaskList(page, param);
        if (CollectionUtil.isNotEmpty(list)) {
            // 处理状态枚举值名称
            list.forEach(vo -> {
                String status = vo.getStatus();
                if (StringUtil.isNotBlank(status)) {
                    vo.setStatusStr(DictCache.getValue(DictTypeEnum.FILE_TASK_STATUS.getType(), vo.getStatus()));
                }
            });
        }
        IPage<BusinessUnzipTaskListVO> newPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        return newPage.setRecords(list);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateReupload(BusinessUnzipTaskUpdateDTO param) {
        log.info("修改业务解压任务重新上传参数：{}", JSON.toJSONString(param));
        if (param == null || (param.getId() == null && param.getBizId() == null)) {
            throw new ServiceException("参数不能为空");
        }
        // 校验业务解压任务
        BusinessUnzipTask unzipTask = param.getId() != null ? this.getById(param.getId()) : this.getOne(Wrappers.<BusinessUnzipTask>lambdaQuery()
                .eq(BusinessUnzipTask::getBizId, param.getBizId())
                .last("limit 1")
        );
        Func.requireNotNull(unzipTask, "业务任务不存在");
        // 上传中或解压失败才可以重新上传
        if (!UnzipTask.Status.UPLOADING.getCode().equals(unzipTask.getStatus())
                && !UnzipTask.Status.UNZIP_FAIL.getCode().equals(unzipTask.getStatus())) {
            throw new ServiceException("只有上传中或解压失败的任务才可以重新上传");
        }

        if (UnzipTask.Status.UPLOADING.getCode().equals(unzipTask.getStatus())) {
            // 上传中的才需要取消上传任务，解压失败的无需取消
            try {
                // 取消文件任务
                fileTaskService.cancelBatchByIds(Lists.newArrayList(unzipTask.getRelateId()));
            } catch (Exception e) {
                log.error("取消上传异常", e);
            }
        }

        // 更新业务解压任务
        BusinessUnzipTask updateParam = new BusinessUnzipTask();
        updateParam.setId(unzipTask.getId());
        updateParam.setRelateId(param.getRelateId());
        String status = getStatus(param.getRelateType(), param.getRelateId());
        updateParam.setStatus(status);
        boolean result = this.updateById(updateParam);
        if (result && UnzipTask.Status.UPLOADED.getCode().equals(status)) {
            // 重新查询，防止解压开始事件中查询时事务未提交查询不到最新的数据
            unzipTask = this.getById(unzipTask.getId());
            // 解压任务状态是上传完成，发布解压开始事件
            applicationContext.publishEvent(new UnzipStartEvent(unzipTask));
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean cancelBusinessUnzipTask(Long bizId, boolean cancelFile) {
        List<BusinessUnzipTask> list = this.list(Wrappers.<BusinessUnzipTask>lambdaQuery()
                .eq(BusinessUnzipTask::getBizId, bizId)
        );
        if (CollectionUtil.isEmpty(list)) {
            return true;
        }
        if (cancelFile) {
            boolean hasUpload = list.stream()
                    .anyMatch(task -> !UnzipTask.Status.UPLOADING.getCode().equals(task.getStatus()));
            if (hasUpload) {
                throw new ServiceException("当前任务已上传完成，不能取消");
            }
            // 取消文件任务
            List<Long> taskIds = list.stream()
                    .filter(task -> UnzipTask.RelateType.FILE_TASK.getCode().equals(task.getRelateType()))
                    .map(BusinessUnzipTask::getRelateId)
                    .toList();
            fileTaskService.cancelBatchByIds(taskIds);
        }
        // 删除业务文件解压任务
        List<Long> unzipTaskIds = list.stream()
                .map(BusinessUnzipTask::getId)
                .toList();
        this.removeBatchByIds(unzipTaskIds);
        return true;
    }

    @Override
    public List<BusinessUnzipDetailListVO> queryBusinessUnzipDetail(Long bizId) {
        return baseMapper.queryBusinessUnzipDetail(bizId);
    }

    @Override
    public List<BusinessUnzipTaskListVO> getBusinessUnzipTasksByBizIds(List<Long> bizIds) {
        if (CollectionUtil.isEmpty(bizIds)) {
            return Collections.emptyList();
        }
        BusinessUnzipTaskQueryDTO param = new BusinessUnzipTaskQueryDTO();
        param.setBizIds(bizIds);
        return baseMapper.selectTaskList(null, param);
    }

    /**
     *  获取解压任务状态
     * @param relateType 关联类型
     * @param relateId 关联id
     * @return
     */
    private String getStatus(String relateType, Long relateId) {
        if (UnzipTask.RelateType.ATTACHMENT.getCode().equals(relateType)) {
            // 关联类型是附件，状态直接就是上传完成
            return UnzipTask.Status.UPLOADED.getCode();
        }
        if (UnzipTask.RelateType.FILE_TASK.getCode().equals(relateType)) {
            // 关联类型是文件任务，查询文件任务状态
            FileTask fileTask = fileTaskService.getById(relateId);
            Func.requireNotNull(fileTask, "文件任务不存在");
            if (FileTaskStatus.COMPLETED.getCode().equals(fileTask.getStatus())) {
                // 文件任务上传完成，解压任务设置为上传完成
                return UnzipTask.Status.UPLOADED.getCode();
            }
            // 否则解压任务设置为上传中状态
            return UnzipTask.Status.UPLOADING.getCode();
        }
        throw new ServiceException("关联类型错误");
    }
}
