package com.yingpaipay.business.service.impl;

import cn.hutool.core.lang.Dict;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yingpaipay.business.constant.DictTypeConst;
import com.yingpaipay.business.constant.QcTaskStatusConst;
import com.yingpaipay.business.domain.*;
import com.yingpaipay.business.domain.bo.DocumentQcAuditBo;
import com.yingpaipay.business.domain.bo.DocumentQcTaskDetailListBo;
import com.yingpaipay.business.domain.bo.TaskCenterQcListBo;
import com.yingpaipay.business.domain.bo.TaskCenterQcSubmitBo;
import com.yingpaipay.business.domain.vo.DocumentQcGenerateVo;
import com.yingpaipay.business.domain.vo.DocumentQcTaskDetailListVo;
import com.yingpaipay.business.domain.vo.TaskCenterQcListVo;
import com.yingpaipay.business.enumeration.DocumentQcStatusEnum;
import com.yingpaipay.business.enumeration.DocumentStatusEnum;
import com.yingpaipay.business.enumeration.MailTemplateEnum;
import com.yingpaipay.business.mapper.DocumentQcTaskLogMapper;
import com.yingpaipay.business.mapper.DocumentQcTaskMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mail.utils.MailUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysDictTypeService;
import org.dromara.system.service.ISysUserService;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import com.yingpaipay.business.mapper.DocumentQcTaskDetailMapper;
import com.yingpaipay.business.service.IDocumentQcTaskDetailService;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.*;

/**
 * 文档质控细节Service业务层处理
 *
 * @author Huanyi
 * @date 2026-01-08
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class DocumentQcTaskDetailServiceImpl implements IDocumentQcTaskDetailService {

    private final DocumentQcTaskDetailMapper baseMapper;
    private final DocumentQcTaskLogMapper logMapper;
    private final DocumentQcTaskMapper taskMapper;

    private final CommonDocumentService documentService;
    private final CommonProjectService projectService;
    private final ISysUserService userService;
    private final ISysDictTypeService dictTypeService;

    @Deprecated
    @Override
    public List<DocumentQcGenerateVo> generate(Long projectId) {

        List<Long> documentIds = new ArrayList<>();
        baseMapper.selectList(
            Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
                .eq(DocumentQcTaskDetail::getProjectId, projectId)
        ).forEach(e -> documentIds.add(e.getDocumentId()));
        List<Document> documents = documentService.selectByOutofIds(documentIds);

        // FIXME 先写死,后续走配置
        int count = 5;

        int selectCount = Math.min(count, documents.size());

        List<Document> shuffled = new ArrayList<>(documents);
        Collections.shuffle(shuffled);

        List<Document> selected = shuffled.subList(0, selectCount);

        List<Long> selectedUserIds = new ArrayList<>();
        Map<Long, SysUser> selectedUserMap = new HashMap<>();
        selected.forEach(e -> selectedUserIds.add(e.getCreateBy()));
        userService.selectUserByIds(selectedUserIds)
            .forEach(e -> selectedUserMap.put(e.getUserId(), e));

        List<DocumentQcGenerateVo> result = new ArrayList<>();
        for (Document document : selected) {
            DocumentQcGenerateVo vo = new DocumentQcGenerateVo();
            vo.setDocumentId(document.getId());
            vo.setDocumentName(document.getName());
            SysUser user = selectedUserMap.get(document.getCreateBy());
            if (user != null) {
                vo.setExecutor(user.getUserId());
                vo.setExecutorName(user.getNickName());
                vo.setExecutorStatus(user.getStatus());
            }
            result.add(vo);
        }

        if (result.isEmpty()) {
            throw new BusinessException("当前没有文档需要进行质控");
        }

        return result;

    }

    @Override
    public TableDataInfo<DocumentQcTaskDetailListVo> listPage(DocumentQcTaskDetailListBo bo, PageQuery pageQuery) {
        List<Long> documentIds = new ArrayList<>();
        Map<Long, Document> documentMap = new HashMap<>();
        documentService.getByName(bo.getProjectId(), bo.getDocumentName()).forEach(e -> {
            documentIds.add(e.getId());
            documentMap.put(e.getId(), e);
        });
        IPage<DocumentQcTaskDetail> page = baseMapper.selectPage(pageQuery.build(), buildListPageWrapper(bo, documentIds));
        return TableDataInfo.build(page.convert(e -> {
            DocumentQcTaskDetailListVo vo = new DocumentQcTaskDetailListVo();
            vo.setId(e.getId());

            Document document = documentMap.get(e.getDocumentId());
            vo.setDocument(document.getId());
            vo.setDocumentName(document.getName());
            vo.setActualDocument(document.getActualDocument());

            vo.setExecutor(e.getExecutor());
            vo.setStatus(e.getStatus());
            vo.setNote(e.getNote());
            vo.setExecutionTime(e.getExecutionTime());
            vo.setFinishTime(e.getFinishTime());
            return vo;
        }));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean audit(DocumentQcAuditBo bo) {

        DocumentQcTaskDetail detail = baseMapper.selectById(bo.getDetailId());
        DocumentQcTask task = taskMapper.selectById(bo.getTaskId());
        Document document = documentService.selectById(detail.getDocumentId());

        if (bo.getResult().equals(DocumentStatusEnum.QC_REJECT.getValue())) {
            Map<String, String> questionTypeMap = new HashMap<>();
            dictTypeService.selectDictDataByType(DictTypeConst.QC_QUESTION_TYPE)
                .forEach(e -> questionTypeMap.put(e.getDictValue(), e.getDictLabel()));
            MailTemplateEnum template = MailTemplateEnum.QC_REJECT;
            SysUserVo designatedDealer = userService.getById(bo.getDesignatedDealer());
            String typeContext = Optional.ofNullable(questionTypeMap.get(bo.getRejectionType()))
                .orElse("{\"zh_CN\":\"未知问题\",\"en_US\":\"Unknown Question\"}");
            Dict type = JsonUtils.parseMap(typeContext);
            MailUtils.sendHtml(
                designatedDealer.getEmail(),
                template.getTitle(),
                MessageFormat.format(
                    template.getContent(),
                    designatedDealer.getNickName(),
                    task.getName(),
                    projectService.queryById(detail.getProjectId()).getName(),
                    document.getName(),
                    LocaleContextHolder.getLocale().getLanguage().equals("zh") ? type.getStr("zh_CN") : type.getStr("en_US"),
                    bo.getOpinion(),
                    DateUtils.formatDate(bo.getDeadline())
                )
            );
        }

        DocumentQcTaskLog log = new DocumentQcTaskLog();
        log.setTaskId(detail.getTaskId());
        log.setDetailId(detail.getId());
        log.setExecutor(LoginHelper.getUserId());
        log.setExecuteTime(new Date());
        log.setResult(bo.getResult());
        log.setQuestionType(bo.getRejectionType());
        log.setOpinion(bo.getOpinion());
        log.setDesignatedDealer(bo.getDesignatedDealer());
        log.setDeadline(bo.getDeadline());
        log.setTenantId(TenantHelper.getTenantId());

        detail.setStatus(bo.getResult());
        detail.setNote(bo.getOpinion());
        detail.setExecutionTime(new Date());

        if (DocumentQcStatusEnum.pass(bo.getResult())) {
            detail.setFinishTime(new Date());
            document.setStatus(DocumentStatusEnum.QC_PASS.getValue());

        } else {
            document.setStatus(DocumentStatusEnum.QC_REJECT.getValue());
        }

        boolean detailFlag = baseMapper.updateById(detail) == 0;
        if (detailFlag) {
            throw new RuntimeException("修改质控细节失败");
        }

        boolean documentFlag = documentService.updateById(document);
        if (!documentFlag) {
            throw new RuntimeException("修改文档状态失败");
        }

        boolean logFlag = logMapper.insert(log) == 0;
        if (logFlag) {
            throw new RuntimeException("新增日志失败");
        }

        long count = baseMapper.selectCount(
            Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
                .eq(DocumentQcTaskDetail::getTaskId, detail.getTaskId())
                .ne(DocumentQcTaskDetail::getStatus, DocumentQcStatusEnum.PASS.getCode())
        );
        if (count == 0) {
            boolean taskFlag = taskMapper.update(
                Wrappers.lambdaUpdate(DocumentQcTask.class)
                    .eq(DocumentQcTask::getId, detail.getTaskId())
                    .set(DocumentQcTask::getStatus, QcTaskStatusConst.FINISHED)
            ) == 0;
            if (taskFlag) {
                throw new RuntimeException("更新质控任务状态失败");
            }
        }

        return true;
    }

    @Override
    public TableDataInfo<TaskCenterQcListVo> listOnTaskCenter(TaskCenterQcListBo bo, PageQuery pageQuery) {

        List<Long> taskIds = new ArrayList<>();
        List<Long> projectIds = new ArrayList<>();
        Map<Long, DocumentQcTask> taskMap = new HashMap<>();
        Map<Long, String> projectMap = new HashMap<>();
        taskMapper.selectList(Wrappers.lambdaQuery(DocumentQcTask.class).like(StringUtils.isNotBlank(bo.getTaskName()), DocumentQcTask::getName, bo.getTaskName()))
            .forEach(e -> {
                taskIds.add(e.getId());
                taskMap.put(e.getId(), e);
            });
        projectService.queryListByName(bo.getProjectName())
            .forEach(e -> {
                projectIds.add(e.getId());
                projectMap.put(e.getId(), e.getName());
            });

        IPage<DocumentQcTaskDetail> page = baseMapper.selectPage(
            pageQuery.build(),
            buildListOnTaskCenterWrapper(bo, taskIds, projectIds)
        );

        return TableDataInfo.build(page.convert(e -> {
            TaskCenterQcListVo vo = new TaskCenterQcListVo();
            vo.setId(e.getId());
            DocumentQcTask task = taskMap.get(e.getTaskId());
            vo.setName(task.getName());
            vo.setProjectName(projectMap.get(e.getProjectId()));
            vo.setStatus(e.getStatus());
            vo.setInitiator(task.getInitiator());
            vo.setExecutor(e.getExecutor());
            vo.setNote(e.getNote());
            vo.setExecuteTime(e.getExecutionTime());
            vo.setCreateTime(e.getCreateTime());
            return vo;
        }));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean submit(TaskCenterQcSubmitBo bo) {

        DocumentQcTaskDetail detail = baseMapper.selectById(bo.getDetailId());
        DocumentQcTaskLog log = logMapper.selectOne(
            Wrappers.lambdaQuery(DocumentQcTaskLog.class)
                .eq(DocumentQcTaskLog::getDetailId, bo.getDetailId())
                .orderByDesc(DocumentQcTaskLog::getId)
                .last("LIMIT 1")
        );
        Document document = documentService.getById(detail.getDocumentId());

        detail.setStatus(DocumentQcStatusEnum.TO_AUDIT.getCode());
        log.setActualDealer(LoginHelper.getUserId());
        log.setDealTime(new Date());
        document.setStatus(DocumentStatusEnum.UN_QC.getValue());
        document.setActualDocument(bo.getDocument());

        boolean detailFlag = baseMapper.updateById(detail) == 0;
        if (detailFlag) {
            throw new RuntimeException("修改质控细节失败");
        }
        boolean logFlag = logMapper.updateById(log) == 0;
        if (logFlag) {
            throw new RuntimeException("修改质控记录失败");
        }
        boolean documentFlag = documentService.updateById(document);
        if (!documentFlag) {
            throw new RuntimeException("修改文档失败");
        }

        return true;
    }

    @Override
    public List<DocumentQcTaskDetail> listOnWorkbench() {
        List<Long> pendingIds = logMapper.selectList(
            Wrappers.lambdaQuery(DocumentQcTaskLog.class)
                .eq(DocumentQcTaskLog::getDesignatedDealer, LoginHelper.getUserId())
                .isNull(DocumentQcTaskLog::getActualDealer)
        ).stream().map(DocumentQcTaskLog::getDetailId).toList();

        LambdaQueryWrapper<DocumentQcTaskDetail> wrapper = Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
            .in(!pendingIds.isEmpty(), DocumentQcTaskDetail::getId, pendingIds)
            .or(w -> w
                .eq(DocumentQcTaskDetail::getStatus, List.of(DocumentQcStatusEnum.UN_AUDIT.getCode(), DocumentQcStatusEnum.TO_AUDIT.getCode()))
                .eq(DocumentQcTaskDetail::getExecutor, LoginHelper.getUserId())
            );

        return baseMapper.selectList(wrapper);
    }

    private LambdaQueryWrapper<DocumentQcTaskDetail> buildListOnTaskCenterWrapper(TaskCenterQcListBo bo, List<Long> taskIds, List<Long> projectIds) {

        LambdaQueryWrapper<DocumentQcTaskDetail> wrapper = Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
            .eq(bo.getStatus() != null, DocumentQcTaskDetail::getStatus, bo.getStatus())
            .orderByDesc(DocumentQcTaskDetail::getId);

        if (StringUtils.isNotBlank(bo.getTaskName())) {
            wrapper.in(DocumentQcTaskDetail::getTaskId, taskIds.isEmpty() ? List.of(-1L) : taskIds);
        }

        wrapper.in(StringUtils.isNotBlank(bo.getProjectName()), DocumentQcTaskDetail::getProjectId, projectIds.isEmpty() ? List.of(-1L) : projectIds);

        if (StringUtils.isNotBlank(bo.getDocumentName())) {
            List<Long> documentIds = documentService.selectByName(bo.getDocumentName(), projectIds).stream().map(Document::getId).toList();
            wrapper.in(DocumentQcTaskDetail::getDocumentId, documentIds.isEmpty() ? List.of(-1L) : documentIds);
        }

        List<Long> logIds = logMapper.selectList(Wrappers.lambdaQuery(DocumentQcTaskLog.class).in(DocumentQcTaskLog::getTaskId, taskIds.isEmpty() ? List.of(-1L) : taskIds).eq(DocumentQcTaskLog::getDesignatedDealer, LoginHelper.getUserId()))
            .stream().map(DocumentQcTaskLog::getDetailId).toList();

        /**
         * 1、质控执行人应该看到未审核的
         * 2、指定处理人应该看到指控驳回的
         */
        wrapper
            .and(bo.getStatus() == null || bo.getStatus().equals(DocumentQcStatusEnum.UN_AUDIT.getCode()), w -> w
                .eq(DocumentQcTaskDetail::getExecutor, LoginHelper.getUserId())
                .in(DocumentQcTaskDetail::getStatus, List.of(DocumentQcStatusEnum.UN_AUDIT.getCode(), DocumentQcStatusEnum.TO_AUDIT.getCode()))
            )
            // 使用 <code>.or().and(action)</code> 莫名奇妙 <code>OR</code> 会失效,最终结果会变成 <code>AND ((executor = 12 AND status = 0) AND (id IN (-1) AND status = 2))</code>
            // 即使使用 <code>.and(w -> w.and().or().and())</code> 结果也是错误的
            .or(bo.getStatus() == null || bo.getStatus().equals(DocumentQcStatusEnum.REJECT.getCode()), w -> w
                .in(DocumentQcTaskDetail::getId, logIds.isEmpty() ? List.of(-1L) : logIds)
                .eq(DocumentQcTaskDetail::getStatus, DocumentQcStatusEnum.REJECT.getCode())
            );

        return wrapper;
    }

    private LambdaQueryWrapper<DocumentQcTaskDetail> buildListPageWrapper(DocumentQcTaskDetailListBo bo, List<Long> documentIds) {

        return Wrappers.lambdaQuery(DocumentQcTaskDetail.class)
            .eq(DocumentQcTaskDetail::getTaskId, bo.getTaskId())
            .in(StringUtils.isNotBlank(bo.getDocumentName()), DocumentQcTaskDetail::getDocumentId, documentIds.isEmpty() ? List.of(-1L) : documentIds)
            .orderByDesc(DocumentQcTaskDetail::getId);
    }
}
