package com.yingpaipay.business.service.impl;

import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.baomidou.lock.executor.RedissonLockExecutor;
import com.yingpaipay.business.constant.QcTaskDetailStatusConst;
import com.yingpaipay.business.constant.QcTaskStatusConst;
import com.yingpaipay.business.domain.Document;
import com.yingpaipay.business.domain.DocumentQcTask;
import com.yingpaipay.business.domain.DocumentQcTaskDetail;
import com.yingpaipay.business.domain.Folder;
import com.yingpaipay.business.domain.bo.DocumentQcTaskBo;
import com.yingpaipay.business.domain.bo.TaskCenterQcListBo;
import com.yingpaipay.business.domain.vo.DocumentQcTaskVo;
import com.yingpaipay.business.domain.vo.TaskCenterQcListVo;
import com.yingpaipay.business.enumeration.DocumentStatusEnum;
import com.yingpaipay.business.mapper.DocumentQcTaskDetailMapper;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.service.ISysUserService;
import org.springframework.stereotype.Service;
import com.yingpaipay.business.mapper.DocumentQcTaskMapper;
import com.yingpaipay.business.service.IDocumentQcTaskService;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 文档质控任务Service业务层处理
 *
 * @author Huanyi
 * @date 2026-01-06
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class DocumentQcTaskServiceImpl implements IDocumentQcTaskService {

    private final DocumentQcTaskMapper baseMapper;
    private final DocumentQcTaskDetailMapper detailMapper;

    private final CommonProjectService projectService;
    private final CommonFolderService folderService;
    private final CommonDocumentService documentService;
    private final ISysUserService userService;

    private final LockTemplate lockTemplate;

    /**
     * 查询文档质控任务
     *
     * @param id 主键
     * @return 文档质控任务
     */
    @Override
    public DocumentQcTaskVo queryById(Long id) {
        DocumentQcTaskVo vo = baseMapper.selectVoById(id);
        vo.setProjectName(projectService.queryById(vo.getProjectId()).getName());
        List<DocumentQcTaskDetail> details = detailMapper.selectList(
            Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
                .eq(DocumentQcTaskDetail::getTaskId, vo.getId())
        );
        long count = details.stream().filter(e -> !Objects.equals(e.getStatus(), QcTaskDetailStatusConst.UN_AUDIT)).count();
        vo.setSchedule(count / details.size());
        return vo;
    }

    /**
     * 分页查询文档质控任务列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 文档质控任务分页列表
     */
    @Override
    public TableDataInfo<DocumentQcTaskVo> queryPageList(DocumentQcTaskBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<DocumentQcTask> lqw = buildQueryWrapper(bo);
        Page<DocumentQcTaskVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        buildVo(result.getRecords());
        return TableDataInfo.build(result);
    }

    private void buildVo(List<DocumentQcTaskVo> result) {
        if (result.isEmpty()) {
            return;
        }
        List<Long> projectIds = new ArrayList<>();
        result.forEach(e -> projectIds.add(e.getProjectId()));

        Map<Long, List<DocumentQcTaskDetail>> detailMap = detailMapper.selectList(
            Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
                .in(DocumentQcTaskDetail::getProjectId, projectIds)
        ).stream().collect(Collectors.groupingBy(DocumentQcTaskDetail::getProjectId));

        Map<Long, String> projectMap = new HashMap<>();
        Map<Long, Long> scheduleMap = new HashMap<>();
        projectService.queryByIds(projectIds).forEach(e -> projectMap.put(e.getId(), e.getName()));

        detailMap.forEach((projectId, list) -> {
            List<DocumentQcTaskDetail> details = detailMap.get(projectId);
            long count = details.stream().filter(e -> !Objects.equals(e.getStatus(), QcTaskDetailStatusConst.UN_AUDIT)).count();
            scheduleMap.put(projectId, count / details.size());
        });

        result.forEach(e -> {
            e.setProjectName(projectMap.get(e.getProjectId()));
            e.setSchedule(scheduleMap.get(e.getProjectId()));
        });

    }

    /**
     * 查询符合条件的文档质控任务列表
     *
     * @param bo 查询条件
     * @return 文档质控任务列表
     */
    @Override
    public List<DocumentQcTaskVo> queryList(DocumentQcTaskBo bo) {
        LambdaQueryWrapper<DocumentQcTask> lqw = buildQueryWrapper(bo);
        List<DocumentQcTaskVo> vos = baseMapper.selectVoList(lqw);
        buildVo(vos);
        return vos;
    }

    private LambdaQueryWrapper<DocumentQcTask> buildQueryWrapper(DocumentQcTaskBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<DocumentQcTask> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(DocumentQcTask::getId);
        lqw.like(StringUtils.isNotBlank(bo.getName()), DocumentQcTask::getName, bo.getName());
        lqw.eq(bo.getInitiator() != null, DocumentQcTask::getInitiator, bo.getInitiator());
        lqw.eq(bo.getProjectId() != null, DocumentQcTask::getProjectId, bo.getProjectId());
        lqw.between(params.get("beginDeadline") != null && params.get("endDeadline") != null,
            DocumentQcTask::getDeadline, params.get("beginDeadline"), params.get("endDeadline"));
        lqw.eq(bo.getStatus() != null, DocumentQcTask::getStatus, bo.getStatus());
        lqw.eq(bo.getCreateBy() != null, DocumentQcTask::getCreateBy, bo.getCreateBy());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            DocumentQcTask::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.eq(bo.getUpdateBy() != null, DocumentQcTask::getUpdateBy, bo.getUpdateBy());
        lqw.between(params.get("beginUpdateTime") != null && params.get("endUpdateTime") != null,
            DocumentQcTask::getUpdateTime, params.get("beginUpdateTime"), params.get("endUpdateTime"));
        return lqw;
    }

    /**
     * 新增文档质控任务
     *
     * @param bo 文档质控任务
     * @return 是否新增成功
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean insertByBo(DocumentQcTaskBo bo) {

        DocumentQcTask add = MapstructUtils.convert(bo, DocumentQcTask.class);
        validEntityBeforeSave(add);

        boolean tastFlag = baseMapper.insert(add) == 0;
        if (tastFlag) {
            throw new RuntimeException("任务新增失败");
        }

        /**
         * 避免同一个文件反复参与质控
         * FIXME 需要思考一个更好的方案,尽可能避免所有操作全部串行化
         */
        LockInfo lock = lockTemplate.lock("qc:" + bo.getProjectId(), 30000L, 5000L, RedissonLockExecutor.class);
        if (lock == null) {
            throw new BusinessException(MessageUtils.message("document.qc.taskisinitializing"));
        }

        try {
            List<Document> selectedDocuments;

            List<Long> historyIds = new ArrayList<>();
            detailMapper.selectList(
                Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
                    .eq(DocumentQcTaskDetail::getProjectId, bo.getProjectId())
            ).forEach(e -> historyIds.add(e.getDocumentId()));

            List<Long> folderIds = new ArrayList<>();
            folderService.queryByProjectIds(List.of(bo.getProjectId())).forEach(e -> folderIds.add(e.getId()));

            List<Document> documents = documentService.selectByFolderIds(folderIds);

            if (documents.isEmpty()) {
                throw new BusinessException(MessageUtils.message("document.qc.taskdetail.notask"));
            }

            selectedDocuments = documents.stream()
                .filter(e -> e.getStatus().equals(DocumentStatusEnum.ARCHIEVED.getValue()))
                .filter(e -> !historyIds.contains(e.getId()))
                .collect(Collectors.toList());
            if (selectedDocuments.isEmpty()) {
                throw new BusinessException(MessageUtils.message("document.qc.taskdetail.notask"));
            }
            long count = (long) bo.getProportion() * selectedDocuments.size() / 100;
            Collections.shuffle(selectedDocuments, new Random(count));
            selectedDocuments = selectedDocuments.subList(0, (int) count);

            List<DocumentQcTaskDetail> details = new ArrayList<>();
            selectedDocuments.forEach(e -> {
                e.setStatus(DocumentStatusEnum.UN_QC.getValue());
                DocumentQcTaskDetail detail = new DocumentQcTaskDetail();
                detail.setTaskId(add.getId());
                detail.setDocumentId(e.getId());
                detail.setExecutor(LoginHelper.getUserId());
                detail.setProjectId(bo.getProjectId());
                detail.setTenantId(TenantHelper.getTenantId());
                details.add(detail);
            });

            boolean flag = detailMapper.insertBatch(details);
            if (!flag) {
                throw new RuntimeException("批量插入失败");
            }

            boolean statusFlag = documentService.updateByIds(documents);
            if (!statusFlag) {
                throw new RuntimeException("批量修改状态失败");
            }
        } finally {
            lockTemplate.releaseLock(lock);
        }

        return true;
    }

    /**
     * 修改文档质控任务
     *
     * @param bo 文档质控任务
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(DocumentQcTaskBo bo) {
        DocumentQcTask update = MapstructUtils.convert(bo, DocumentQcTask.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(DocumentQcTask entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除文档质控任务信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        boolean taskFlag = baseMapper.deleteByIds(ids) == 0;
        if (taskFlag) {
            throw new RuntimeException("删除主任务失败");
        }

        List<DocumentQcTaskDetail> details = detailMapper.selectList(
            Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
                .in(DocumentQcTaskDetail::getTaskId, ids)
        );

        boolean detailFlag = detailMapper.deleteByIds(details) == 0;
        if (detailFlag) {
            throw new RuntimeException("删除细节失败");
        }

        List<Long> documentIds = details.stream().map(DocumentQcTaskDetail::getDocumentId).toList();
        boolean documentFlag = documentService.updateStatusByIds(documentIds, DocumentStatusEnum.ARCHIEVED);
        if (!documentFlag) {
            throw new RuntimeException("批量修改文档状态失败");
        }

        return true;
    }

    @Override
    public boolean start(Long id) {
        return baseMapper.update(
            Wrappers.lambdaUpdate(DocumentQcTask.class)
                .eq(DocumentQcTask::getId, id)
                .set(DocumentQcTask::getStatus, QcTaskStatusConst.UNDERWAY)
        ) > 0;
    }
}
