package com.lesingle.modules.biz.review.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lesingle.common.enums.ErrorCode;
import com.lesingle.common.enums.WorkStatus;
import com.lesingle.common.exception.BusinessException;
import com.lesingle.common.result.PageResult;
import com.lesingle.modules.biz.contest.entity.BizContest;
import com.lesingle.modules.biz.contest.entity.BizContestRegistration;
import com.lesingle.modules.biz.contest.entity.BizContestWork;
import com.lesingle.modules.biz.contest.mapper.ContestMapper;
import com.lesingle.modules.biz.contest.mapper.ContestRegistrationMapper;
import com.lesingle.modules.biz.contest.mapper.ContestWorkMapper;
import com.lesingle.modules.biz.review.dto.CreateScoreDto;
import com.lesingle.modules.biz.review.dto.MarkContestWorkViolationDto;
import com.lesingle.modules.biz.review.entity.BizContestJudge;
import com.lesingle.modules.biz.review.entity.BizContestReviewRule;
import com.lesingle.modules.biz.review.entity.BizContestWorkJudgeAssignment;
import com.lesingle.modules.biz.review.entity.BizContestWorkScore;
import com.lesingle.modules.biz.review.mapper.ContestJudgeMapper;
import com.lesingle.modules.biz.review.mapper.ContestReviewRuleMapper;
import com.lesingle.modules.biz.review.mapper.ContestWorkJudgeAssignmentMapper;
import com.lesingle.modules.biz.review.mapper.ContestWorkScoreMapper;
import com.lesingle.modules.biz.review.service.IContestReviewService;
import com.lesingle.modules.sys.entity.SysUser;
import com.lesingle.modules.sys.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ContestReviewServiceImpl implements IContestReviewService {

    private final ContestWorkJudgeAssignmentMapper assignmentMapper;
    private final ContestWorkScoreMapper scoreMapper;
    private final ContestJudgeMapper judgeMapper;
    private final ContestWorkMapper workMapper;
    private final ContestMapper contestMapper;
    private final ContestRegistrationMapper registrationMapper;
    private final ContestReviewRuleMapper reviewRuleMapper;
    private final SysUserMapper sysUserMapper;

    /**
     * 仅允许将「已在本活动评委表 t_biz_contest_judge 中显式配置」的评委新分配到作品。
     */
    private void assertJudgeAssignedToContest(Long contestId, Long judgeId) {
        LambdaQueryWrapper<BizContestJudge> w = new LambdaQueryWrapper<>();
        w.eq(BizContestJudge::getContestId, contestId);
        w.eq(BizContestJudge::getJudgeId, judgeId);
        w.eq(BizContestJudge::getValidState, 1);
        if (judgeMapper.selectCount(w) == 0) {
            throw BusinessException.of(ErrorCode.BAD_REQUEST, "所选评委未加入本活动，请先在活动评委管理中配置");
        }
    }

    // ====== 作品分配 ======

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> assignWork(Long contestId, Long workId, List<Long> judgeIds, Long creatorId) {
        if (judgeIds == null) {
            judgeIds = Collections.emptyList();
        }
        // 去重后保持顺序，作为本次期望的评委集合
        List<Long> desiredOrder = new ArrayList<>(new LinkedHashSet<>(judgeIds));
        Set<Long> desired = new HashSet<>(desiredOrder);

        log.info("分配作品（同步），活动ID：{}，作品ID：{}，期望评委数：{}", contestId, workId, desired.size());

        // 1. 取消已分配但不在本次列表中的评委（仅删除未评分的分配行）
        LambdaQueryWrapper<BizContestWorkJudgeAssignment> existWrapper = new LambdaQueryWrapper<>();
        existWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
        existWrapper.eq(BizContestWorkJudgeAssignment::getWorkId, workId);
        List<BizContestWorkJudgeAssignment> existing = assignmentMapper.selectList(existWrapper);

        int removed = 0;
        for (BizContestWorkJudgeAssignment a : existing) {
            if (desired.contains(a.getJudgeId())) {
                continue;
            }
            LambdaQueryWrapper<BizContestWorkScore> scoreWrapper = new LambdaQueryWrapper<>();
            scoreWrapper.eq(BizContestWorkScore::getAssignmentId, a.getId());
            scoreWrapper.eq(BizContestWorkScore::getValidState, 1);
            if (scoreMapper.selectCount(scoreWrapper) > 0) {
                throw BusinessException.of(ErrorCode.BAD_REQUEST, "部分评委已提交评分，无法取消其分配，请先处理评分记录");
            }
            assignmentMapper.deleteById(a.getId());
            removed++;
        }

        int created = 0;
        int skipped = 0;

        for (Long judgeId : desiredOrder) {
            // 检查是否已分配
            LambdaQueryWrapper<BizContestWorkJudgeAssignment> dupWrapper = new LambdaQueryWrapper<>();
            dupWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
            dupWrapper.eq(BizContestWorkJudgeAssignment::getWorkId, workId);
            dupWrapper.eq(BizContestWorkJudgeAssignment::getJudgeId, judgeId);
            if (assignmentMapper.selectCount(dupWrapper) > 0) {
                skipped++;
                continue;
            }

            assertJudgeAssignedToContest(contestId, judgeId);

            BizContestWorkJudgeAssignment assignment = new BizContestWorkJudgeAssignment();
            assignment.setContestId(contestId);
            assignment.setWorkId(workId);
            assignment.setJudgeId(judgeId);
            assignment.setAssignmentTime(LocalDateTime.now());
            assignment.setStatus("assigned");
            assignment.setCreator(creatorId != null ? creatorId.intValue() : null);
            assignment.setCreateTime(LocalDateTime.now());

            assignmentMapper.insert(assignment);
            created++;
        }

        log.info("作品分配完成，移除：{}，新建：{}，跳过（已存在）：{}", removed, created, skipped);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("workId", workId);
        result.put("removed", removed);
        result.put("created", created);
        result.put("skipped", skipped);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> batchAssignWorks(Long contestId, List<Long> workIds, List<Long> judgeIds, Long creatorId) {
        log.info("批量分配作品，活动ID：{}，作品数：{}，评委数：{}", contestId, workIds.size(), judgeIds.size());

        int totalCreated = 0;
        int totalSkipped = 0;

        for (Long workId : workIds) {
            Map<String, Object> single = assignWork(contestId, workId, judgeIds, creatorId);
            totalCreated += (int) single.get("created");
            totalSkipped += (int) single.get("skipped");
        }

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("worksCount", workIds.size());
        result.put("created", totalCreated);
        result.put("skipped", totalSkipped);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> autoAssignWorks(Long contestId, Long creatorId) {
        log.info("自动分配作品，活动ID：{}", contestId);

        // 1. 获取所有未分配的作品
        LambdaQueryWrapper<BizContestWork> workWrapper = new LambdaQueryWrapper<>();
        workWrapper.eq(BizContestWork::getContestId, contestId);
        workWrapper.eq(BizContestWork::getIsLatest, true);
        workWrapper.eq(BizContestWork::getValidState, 1);
        workWrapper.notInSql(BizContestWork::getId,
                "SELECT work_id FROM t_biz_contest_work_judge_assignment WHERE contest_id = " + contestId);
        List<BizContestWork> unassignedWorks = workMapper.selectList(workWrapper);

        if (unassignedWorks.isEmpty()) {
            throw BusinessException.of(ErrorCode.BAD_REQUEST, "没有需要分配的作品");
        }

        // 2. 获取活动所有评委
        LambdaQueryWrapper<BizContestJudge> judgeWrapper = new LambdaQueryWrapper<>();
        judgeWrapper.eq(BizContestJudge::getContestId, contestId);
        judgeWrapper.eq(BizContestJudge::getValidState, 1);
        List<BizContestJudge> judges = judgeMapper.selectList(judgeWrapper);

        if (judges.isEmpty()) {
            throw BusinessException.of(ErrorCode.BAD_REQUEST, "活动尚未配置评委");
        }

        // 3. 轮询分配：每个作品分配给3个评委
        int judgesPerWork = 3;
        int judgeIndex = 0;
        int created = 0;

        for (BizContestWork work : unassignedWorks) {
            for (int i = 0; i < judgesPerWork && i < judges.size(); i++) {
                BizContestJudge judge = judges.get((judgeIndex + i) % judges.size());

                BizContestWorkJudgeAssignment assignment = new BizContestWorkJudgeAssignment();
                assignment.setContestId(contestId);
                assignment.setWorkId(work.getId());
                assignment.setJudgeId(judge.getJudgeId());
                assignment.setAssignmentTime(LocalDateTime.now());
                assignment.setStatus("assigned");
                assignment.setCreator(creatorId != null ? creatorId.intValue() : null);
                assignment.setCreateTime(LocalDateTime.now());

                assignmentMapper.insert(assignment);
                created++;
            }
            judgeIndex = (judgeIndex + 1) % judges.size();
        }

        log.info("自动分配完成，作品数：{}，分配记录数：{}", unassignedWorks.size(), created);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("worksCount", unassignedWorks.size());
        result.put("created", created);
        result.put("judgesPerWork", Math.min(judgesPerWork, judges.size()));
        return result;
    }

    // ====== 评分 ======

    @Override
    public Map<String, Object> score(CreateScoreDto dto, Long judgeId, Long tenantId) {
        log.info("评委打分，评委ID：{}，作品ID：{}，分配ID：{}", judgeId, dto.getWorkId(), dto.getAssignmentId());

        // 1. 验证分配记录
        BizContestWorkJudgeAssignment assignment = assignmentMapper.selectById(dto.getAssignmentId());
        if (assignment == null) {
            throw BusinessException.of(ErrorCode.NOT_FOUND, "分配记录不存在");
        }
        if (!assignment.getJudgeId().equals(judgeId)) {
            throw BusinessException.of(ErrorCode.FORBIDDEN, "无权对此作品评分");
        }
        if (!assignment.getWorkId().equals(dto.getWorkId())) {
            throw BusinessException.of(ErrorCode.BAD_REQUEST, "作品ID与分配记录不匹配");
        }

        // 2. 检查是否已评分
        LambdaQueryWrapper<BizContestWorkScore> existWrapper = new LambdaQueryWrapper<>();
        existWrapper.eq(BizContestWorkScore::getAssignmentId, dto.getAssignmentId());
        existWrapper.eq(BizContestWorkScore::getValidState, 1);
        if (scoreMapper.selectCount(existWrapper) > 0) {
            throw BusinessException.of(ErrorCode.BAD_REQUEST, "该分配记录已评分，请勿重复提交");
        }

        // 3. 获取评委姓名
        SysUser judgeUser = sysUserMapper.selectById(judgeId);
        String judgeName = judgeUser != null ? judgeUser.getNickname() : "";

        // 4. 创建评分记录
        BizContestWorkScore scoreEntity = new BizContestWorkScore();
        scoreEntity.setTenantId(tenantId);
        scoreEntity.setContestId(assignment.getContestId());
        scoreEntity.setWorkId(dto.getWorkId());
        scoreEntity.setAssignmentId(dto.getAssignmentId());
        scoreEntity.setJudgeId(judgeId);
        scoreEntity.setJudgeName(judgeName);
        scoreEntity.setDimensionScores(dto.getDimensionScores());
        scoreEntity.setTotalScore(dto.getTotalScore());
        scoreEntity.setComments(dto.getComments());
        scoreEntity.setScoreTime(LocalDateTime.now());

        scoreMapper.insert(scoreEntity);

        // 5. 更新分配状态为已完成
        assignment.setStatus("completed");
        assignment.setModifyTime(LocalDateTime.now());
        assignmentMapper.updateById(assignment);

        // 6. 更新作品状态为评审中
        BizContestWork work = workMapper.selectById(dto.getWorkId());
        if (work != null && !WorkStatus.REVIEWING.getValue().equals(work.getStatus())) {
            work.setStatus(WorkStatus.REVIEWING.getValue());
            workMapper.updateById(work);
        }

        log.info("评分成功，评分ID：{}", scoreEntity.getId());

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("scoreId", scoreEntity.getId());
        result.put("totalScore", scoreEntity.getTotalScore());
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> markViolation(Long workId, MarkContestWorkViolationDto dto, Long judgeId) {
        log.info("评委标记违规，评委ID：{}，作品ID：{}，分配ID：{}", judgeId, workId, dto.getAssignmentId());

        BizContestWorkJudgeAssignment assignment = assignmentMapper.selectById(dto.getAssignmentId());
        if (assignment == null) {
            throw BusinessException.of(ErrorCode.NOT_FOUND, "分配记录不存在");
        }
        if (!assignment.getJudgeId().equals(judgeId)) {
            throw BusinessException.of(ErrorCode.FORBIDDEN, "无权标记该作品违规");
        }
        if (!assignment.getWorkId().equals(workId)) {
            throw BusinessException.of(ErrorCode.BAD_REQUEST, "作品ID与分配记录不匹配");
        }

        BizContestWork work = workMapper.selectById(workId);
        if (work == null) {
            throw BusinessException.of(ErrorCode.NOT_FOUND, "作品不存在");
        }
        if (work.getValidState() != null && work.getValidState() != 1) {
            throw BusinessException.of(ErrorCode.NOT_FOUND, "作品不存在");
        }
        if (!work.getContestId().equals(assignment.getContestId())) {
            throw BusinessException.of(ErrorCode.BAD_REQUEST, "活动与作品不匹配");
        }

        if (WorkStatus.VIOLATION.getValue().equals(work.getStatus())) {
            Map<String, Object> done = new LinkedHashMap<>();
            done.put("workId", workId);
            done.put("status", work.getStatus());
            return done;
        }

        work.setStatus(WorkStatus.VIOLATION.getValue());
        if (StringUtils.hasText(dto.getReason())) {
            work.setViolationReason(dto.getReason().trim());
        }
        work.setViolationMarkTime(LocalDateTime.now());
        work.setViolationJudgeId(judgeId);
        work.setModifyTime(LocalDateTime.now());
        workMapper.updateById(work);

        assignment.setStatus("completed");
        assignment.setModifyTime(LocalDateTime.now());
        assignmentMapper.updateById(assignment);

        log.info("作品已标记违规，作品ID：{}，评委ID：{}", workId, judgeId);
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("workId", workId);
        result.put("status", WorkStatus.VIOLATION.getValue());
        return result;
    }

    @Override
    public Map<String, Object> updateScore(Long scoreId, CreateScoreDto dto, Long judgeId) {
        log.info("更新评分，评分ID：{}，评委ID：{}", scoreId, judgeId);

        BizContestWorkScore scoreEntity = scoreMapper.selectById(scoreId);
        if (scoreEntity == null) {
            throw BusinessException.of(ErrorCode.NOT_FOUND, "评分记录不存在");
        }
        if (!scoreEntity.getJudgeId().equals(judgeId)) {
            throw BusinessException.of(ErrorCode.FORBIDDEN, "无权修改此评分");
        }

        // 检查作品的终分是否已计算，已计算则拒绝修改
        BizContestWork work = workMapper.selectById(scoreEntity.getWorkId());
        if (work != null && work.getFinalScore() != null) {
            throw BusinessException.of(ErrorCode.BAD_REQUEST, "终分已计算完成，无法修改评分");
        }

        if (dto.getDimensionScores() != null) {
            scoreEntity.setDimensionScores(dto.getDimensionScores());
        }
        if (dto.getTotalScore() != null) {
            scoreEntity.setTotalScore(dto.getTotalScore());
        }
        if (dto.getComments() != null) {
            scoreEntity.setComments(dto.getComments());
        }
        scoreEntity.setScoreTime(LocalDateTime.now());

        scoreMapper.updateById(scoreEntity);
        log.info("评分更新成功，评分ID：{}", scoreId);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("scoreId", scoreEntity.getId());
        result.put("totalScore", scoreEntity.getTotalScore());
        return result;
    }

    // ====== 评委视角查询 ======

    @Override
    public List<Map<String, Object>> getAssignedWorks(Long judgeId, Long contestId) {
        log.info("查询评委已分配作品，评委ID：{}，活动ID：{}", judgeId, contestId);

        LambdaQueryWrapper<BizContestWorkJudgeAssignment> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BizContestWorkJudgeAssignment::getJudgeId, judgeId);
        wrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
        wrapper.orderByAsc(BizContestWorkJudgeAssignment::getAssignmentTime);

        List<BizContestWorkJudgeAssignment> assignments = assignmentMapper.selectList(wrapper);

        // 批量查询作品信息
        Set<Long> workIds = assignments.stream().map(BizContestWorkJudgeAssignment::getWorkId).collect(Collectors.toSet());
        Map<Long, BizContestWork> workMap = new HashMap<>();
        if (!workIds.isEmpty()) {
            List<BizContestWork> works = workMapper.selectBatchIds(workIds);
            for (BizContestWork w : works) {
                workMap.put(w.getId(), w);
            }
        }

        return assignments.stream().map(a -> {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("assignmentId", a.getId());
            map.put("workId", a.getWorkId());
            map.put("status", a.getStatus());
            map.put("assignmentTime", a.getAssignmentTime());

            BizContestWork work = workMap.get(a.getWorkId());
            if (work != null) {
                map.put("workNo", work.getWorkNo());
                map.put("title", work.getTitle());
                map.put("previewUrl", work.getPreviewUrl());
                map.put("previewUrls", work.getPreviewUrls());
                map.put("submitterAccountNo", work.getSubmitterAccountNo());
            }
            return map;
        }).collect(Collectors.toList());
    }

    @Override
    public List<Map<String, Object>> getJudgeContests(Long judgeId) {
        log.info("查询评委关联活动，评委ID：{}", judgeId);

        Set<Long> contestIds = new LinkedHashSet<>();

        LambdaQueryWrapper<BizContestJudge> judgeQw = new LambdaQueryWrapper<>();
        judgeQw.eq(BizContestJudge::getJudgeId, judgeId);
        judgeQw.eq(BizContestJudge::getValidState, 1);
        for (BizContestJudge r : judgeMapper.selectList(judgeQw)) {
            contestIds.add(r.getContestId());
        }

        LambdaQueryWrapper<BizContestWorkJudgeAssignment> assignQw = new LambdaQueryWrapper<>();
        assignQw.eq(BizContestWorkJudgeAssignment::getJudgeId, judgeId);
        assignQw.select(BizContestWorkJudgeAssignment::getContestId);
        for (BizContestWorkJudgeAssignment a : assignmentMapper.selectList(assignQw)) {
            contestIds.add(a.getContestId());
        }

        if (contestIds.isEmpty()) {
            return Collections.emptyList();
        }

        List<BizContest> contests = contestMapper.selectBatchIds(contestIds);
        return contests.stream()
                .sorted(Comparator.comparing(BizContest::getId).reversed())
                .map(c -> {
                    Map<String, Object> map = new LinkedHashMap<>();
                    map.put("contestId", c.getId());
                    map.put("contestName", c.getContestName());
                    map.put("contestState", c.getContestState());
                    map.put("status", c.getStatus());
                    map.put("reviewStartTime", c.getReviewStartTime());
                    map.put("reviewEndTime", c.getReviewEndTime());

                    LambdaQueryWrapper<BizContestWorkJudgeAssignment> totalW = new LambdaQueryWrapper<>();
                    totalW.eq(BizContestWorkJudgeAssignment::getContestId, c.getId());
                    totalW.eq(BizContestWorkJudgeAssignment::getJudgeId, judgeId);
                    long totalAssigned = assignmentMapper.selectCount(totalW);

                    LambdaQueryWrapper<BizContestWorkJudgeAssignment> doneW = new LambdaQueryWrapper<>();
                    doneW.eq(BizContestWorkJudgeAssignment::getContestId, c.getId());
                    doneW.eq(BizContestWorkJudgeAssignment::getJudgeId, judgeId);
                    doneW.eq(BizContestWorkJudgeAssignment::getStatus, "completed");
                    long reviewed = assignmentMapper.selectCount(doneW);

                    map.put("totalAssigned", totalAssigned);
                    map.put("reviewed", reviewed);
                    map.put("pending", totalAssigned - reviewed);
                    return map;
                })
                .collect(Collectors.toList());
    }

    @Override
    public PageResult<Map<String, Object>> getJudgeContestWorks(Long judgeId, Long contestId, Long page, Long pageSize,
                                                                  String workNo, String accountNo, String reviewStatus) {
        log.info("查询评委活动作品，评委ID：{}，活动ID：{}，页码：{}", judgeId, contestId, page);

        // 分页查询分配记录
        LambdaQueryWrapper<BizContestWorkJudgeAssignment> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BizContestWorkJudgeAssignment::getJudgeId, judgeId);
        wrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);

        if (StringUtils.hasText(reviewStatus)) {
            if ("reviewed".equalsIgnoreCase(reviewStatus)) {
                wrapper.eq(BizContestWorkJudgeAssignment::getStatus, "completed");
            } else if ("pending".equalsIgnoreCase(reviewStatus)) {
                wrapper.ne(BizContestWorkJudgeAssignment::getStatus, "completed");
            } else {
                wrapper.eq(BizContestWorkJudgeAssignment::getStatus, reviewStatus);
            }
        }

        wrapper.orderByAsc(BizContestWorkJudgeAssignment::getAssignmentTime);

        Page<BizContestWorkJudgeAssignment> pageObj = new Page<>(page, pageSize);
        Page<BizContestWorkJudgeAssignment> assignmentPage = assignmentMapper.selectPage(pageObj, wrapper);

        List<BizContestWorkJudgeAssignment> assignments = assignmentPage.getRecords();

        // 批量查询作品和评分
        Set<Long> workIds = assignments.stream().map(BizContestWorkJudgeAssignment::getWorkId).collect(Collectors.toSet());
        Map<Long, BizContestWork> workMap = new HashMap<>();
        if (!workIds.isEmpty()) {
            List<BizContestWork> works = workMapper.selectBatchIds(workIds);
            for (BizContestWork w : works) {
                workMap.put(w.getId(), w);
            }
        }

        Set<Long> assignmentIds = assignments.stream().map(BizContestWorkJudgeAssignment::getId).collect(Collectors.toSet());
        Map<Long, BizContestWorkScore> scoreMap = new HashMap<>();
        if (!assignmentIds.isEmpty()) {
            LambdaQueryWrapper<BizContestWorkScore> scoreWrapper = new LambdaQueryWrapper<>();
            scoreWrapper.in(BizContestWorkScore::getAssignmentId, assignmentIds);
            scoreWrapper.eq(BizContestWorkScore::getValidState, 1);
            List<BizContestWorkScore> scores = scoreMapper.selectList(scoreWrapper);
            for (BizContestWorkScore s : scores) {
                scoreMap.put(s.getAssignmentId(), s);
            }
        }

        // 批量查询报名信息，用于补充submitterAccountNo
        Set<Long> registrationIds = workMap.values().stream()
                .map(BizContestWork::getRegistrationId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, String> regAccountMap = new HashMap<>();
        if (!registrationIds.isEmpty()) {
            List<BizContestRegistration> registrations = registrationMapper.selectBatchIds(registrationIds);
            for (BizContestRegistration r : registrations) {
                regAccountMap.put(r.getId(), r.getAccountNo());
            }
        }

        // 组装VO并应用筛选
        List<Map<String, Object>> voList = new ArrayList<>();
        for (BizContestWorkJudgeAssignment a : assignments) {
            BizContestWork work = workMap.get(a.getWorkId());
            if (work == null) continue;

            // 优先取作品上的账号，为空则从报名记录补充
            String submitterAcc = work.getSubmitterAccountNo();
            if (submitterAcc == null && work.getRegistrationId() != null) {
                submitterAcc = regAccountMap.get(work.getRegistrationId());
            }

            // workNo筛选
            if (StringUtils.hasText(workNo) && (work.getWorkNo() == null
                    || !work.getWorkNo().contains(workNo))) {
                continue;
            }
            // accountNo筛选
            if (StringUtils.hasText(accountNo) && (submitterAcc == null
                    || !submitterAcc.contains(accountNo))) {
                continue;
            }

            Map<String, Object> map = new LinkedHashMap<>();
            map.put("assignmentId", a.getId());
            map.put("workId", a.getWorkId());
            map.put("status", a.getStatus());
            map.put("assignmentTime", a.getAssignmentTime());
            map.put("workNo", work.getWorkNo());
            map.put("title", work.getTitle());
            map.put("previewUrl", work.getPreviewUrl());
            map.put("previewUrls", work.getPreviewUrls());
            map.put("submitterAccountNo", submitterAcc);
            map.put("workStatus", work.getStatus());

            BizContestWorkScore scoreRecord = scoreMap.get(a.getId());
            if (scoreRecord != null) {
                map.put("scoreId", scoreRecord.getId());
                map.put("totalScore", scoreRecord.getTotalScore());
                map.put("dimensionScores", scoreRecord.getDimensionScores());
                map.put("comments", scoreRecord.getComments());
                map.put("scoreTime", scoreRecord.getScoreTime());
            }

            voList.add(map);
        }

        return PageResult.from(assignmentPage, voList);
    }

    // ====== 评审进度 ======

    @Override
    public Map<String, Object> getReviewProgress(Long contestId) {
        log.info("查询评审进度，活动ID：{}", contestId);

        // 总作品数
        LambdaQueryWrapper<BizContestWork> workWrapper = new LambdaQueryWrapper<>();
        workWrapper.eq(BizContestWork::getContestId, contestId);
        workWrapper.eq(BizContestWork::getIsLatest, true);
        workWrapper.eq(BizContestWork::getValidState, 1);
        long totalWorks = workMapper.selectCount(workWrapper);

        // 已分配作品数（去重）
        LambdaQueryWrapper<BizContestWorkJudgeAssignment> assignWrapper = new LambdaQueryWrapper<>();
        assignWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
        assignWrapper.select(BizContestWorkJudgeAssignment::getWorkId);
        assignWrapper.groupBy(BizContestWorkJudgeAssignment::getWorkId);
        long assignedWorks = assignmentMapper.selectList(assignWrapper).size();

        // 总分配记录和已完成记录
        LambdaQueryWrapper<BizContestWorkJudgeAssignment> allAssignWrapper = new LambdaQueryWrapper<>();
        allAssignWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
        long totalAssignments = assignmentMapper.selectCount(allAssignWrapper);

        LambdaQueryWrapper<BizContestWorkJudgeAssignment> completedWrapper = new LambdaQueryWrapper<>();
        completedWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
        completedWrapper.eq(BizContestWorkJudgeAssignment::getStatus, "completed");
        long completedAssignments = assignmentMapper.selectCount(completedWrapper);

        // 每个评委的进度
        LambdaQueryWrapper<BizContestJudge> judgeWrapper = new LambdaQueryWrapper<>();
        judgeWrapper.eq(BizContestJudge::getContestId, contestId);
        judgeWrapper.eq(BizContestJudge::getValidState, 1);
        List<BizContestJudge> judges = judgeMapper.selectList(judgeWrapper);

        List<Map<String, Object>> judgeProgress = new ArrayList<>();
        for (BizContestJudge judge : judges) {
            LambdaQueryWrapper<BizContestWorkJudgeAssignment> jAssignWrapper = new LambdaQueryWrapper<>();
            jAssignWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
            jAssignWrapper.eq(BizContestWorkJudgeAssignment::getJudgeId, judge.getJudgeId());
            long jTotal = assignmentMapper.selectCount(jAssignWrapper);

            LambdaQueryWrapper<BizContestWorkJudgeAssignment> jCompletedWrapper = new LambdaQueryWrapper<>();
            jCompletedWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
            jCompletedWrapper.eq(BizContestWorkJudgeAssignment::getJudgeId, judge.getJudgeId());
            jCompletedWrapper.eq(BizContestWorkJudgeAssignment::getStatus, "completed");
            long jCompleted = assignmentMapper.selectCount(jCompletedWrapper);

            SysUser user = sysUserMapper.selectById(judge.getJudgeId());

            Map<String, Object> jp = new LinkedHashMap<>();
            jp.put("judgeId", judge.getJudgeId());
            jp.put("judgeName", user != null ? user.getNickname() : "");
            jp.put("totalAssigned", jTotal);
            jp.put("completed", jCompleted);
            jp.put("pending", jTotal - jCompleted);
            judgeProgress.add(jp);
        }

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("totalWorks", totalWorks);
        result.put("assignedWorks", assignedWorks);
        result.put("unassignedWorks", totalWorks - assignedWorks);
        result.put("totalAssignments", totalAssignments);
        result.put("completedAssignments", completedAssignments);
        result.put("pendingAssignments", totalAssignments - completedAssignments);
        result.put("judgeProgress", judgeProgress);
        return result;
    }

    @Override
    public Map<String, Object> getWorkStatusStats(Long contestId) {
        log.info("查询作品状态统计，活动ID：{}", contestId);

        LambdaQueryWrapper<BizContestWork> baseWrapper = new LambdaQueryWrapper<>();
        baseWrapper.eq(BizContestWork::getContestId, contestId);
        baseWrapper.eq(BizContestWork::getIsLatest, true);
        baseWrapper.eq(BizContestWork::getValidState, 1);
        long total = workMapper.selectCount(baseWrapper);

        String[] statuses = {
                WorkStatus.SUBMITTED.getValue(),
                WorkStatus.LOCKED.getValue(),
                WorkStatus.REVIEWING.getValue(),
                WorkStatus.REJECTED.getValue(),
                WorkStatus.ACCEPTED.getValue(),
                WorkStatus.VIOLATION.getValue()
        };
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("total", total);

        for (String status : statuses) {
            LambdaQueryWrapper<BizContestWork> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(BizContestWork::getContestId, contestId);
            wrapper.eq(BizContestWork::getIsLatest, true);
            wrapper.eq(BizContestWork::getValidState, 1);
            wrapper.eq(BizContestWork::getStatus, status);
            result.put(status, workMapper.selectCount(wrapper));
        }

        return result;
    }

    @Override
    public List<Map<String, Object>> getWorkScores(Long workId) {
        log.info("查询作品评分列表，作品ID：{}", workId);

        LambdaQueryWrapper<BizContestWorkScore> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BizContestWorkScore::getWorkId, workId);
        wrapper.eq(BizContestWorkScore::getValidState, 1);
        wrapper.orderByAsc(BizContestWorkScore::getScoreTime);

        List<BizContestWorkScore> scores = scoreMapper.selectList(wrapper);

        Set<Long> judgeIds = scores.stream()
                .map(BizContestWorkScore::getJudgeId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, SysUser> judgeUserById = new HashMap<>();
        if (!judgeIds.isEmpty()) {
            for (SysUser u : sysUserMapper.selectBatchIds(judgeIds)) {
                if (u != null && u.getId() != null) {
                    judgeUserById.put(u.getId(), u);
                }
            }
        }

        return scores.stream().map(s -> {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("id", s.getId());
            map.put("scoreId", s.getId());
            map.put("assignmentId", s.getAssignmentId());
            map.put("judgeId", s.getJudgeId());
            SysUser judgeUser = s.getJudgeId() != null ? judgeUserById.get(s.getJudgeId()) : null;
            String judgeName = s.getJudgeName();
            if (!StringUtils.hasText(judgeName) && judgeUser != null) {
                judgeName = StringUtils.hasText(judgeUser.getNickname())
                        ? judgeUser.getNickname()
                        : judgeUser.getUsername();
            }
            map.put("judgeName", judgeName);
            if (judgeUser != null) {
                Map<String, Object> judge = new LinkedHashMap<>();
                judge.put("id", judgeUser.getId());
                judge.put("username", judgeUser.getUsername());
                judge.put("nickname", judgeUser.getNickname());
                map.put("judge", judge);
            }
            map.put("dimensionScores", s.getDimensionScores());
            map.put("totalScore", s.getTotalScore());
            map.put("comments", s.getComments());
            map.put("scoreTime", s.getScoreTime());
            return map;
        }).collect(Collectors.toList());
    }

    // ====== 终分计算 ======

    @Override
    public Map<String, Object> calculateFinalScore(Long workId) {
        log.info("计算作品终分，作品ID：{}", workId);

        // 1. 获取作品、活动、评审规则
        BizContestWork work = workMapper.selectById(workId);
        if (work == null) {
            throw BusinessException.of(ErrorCode.NOT_FOUND, "作品不存在");
        }

        BizContest contest = contestMapper.selectById(work.getContestId());
        if (contest == null) {
            throw BusinessException.of(ErrorCode.NOT_FOUND, "活动不存在");
        }

        String calculationRule = "average"; // 默认
        if (contest.getReviewRuleId() != null) {
            BizContestReviewRule rule = reviewRuleMapper.selectById(contest.getReviewRuleId());
            if (rule != null && StringUtils.hasText(rule.getCalculationRule())) {
                calculationRule = rule.getCalculationRule();
            }
        }

        // 2. 获取所有有效评分
        LambdaQueryWrapper<BizContestWorkScore> scoreWrapper = new LambdaQueryWrapper<>();
        scoreWrapper.eq(BizContestWorkScore::getWorkId, workId);
        scoreWrapper.eq(BizContestWorkScore::getValidState, 1);
        List<BizContestWorkScore> scores = scoreMapper.selectList(scoreWrapper);

        if (scores.isEmpty()) {
            throw BusinessException.of(ErrorCode.BAD_REQUEST, "该作品尚无评分记录");
        }

        // 3. 获取评委权重
        LambdaQueryWrapper<BizContestJudge> judgeWrapper = new LambdaQueryWrapper<>();
        judgeWrapper.eq(BizContestJudge::getContestId, work.getContestId());
        judgeWrapper.eq(BizContestJudge::getValidState, 1);
        List<BizContestJudge> judges = judgeMapper.selectList(judgeWrapper);
        Map<Long, BigDecimal> weightMap = new HashMap<>();
        for (BizContestJudge j : judges) {
            weightMap.put(j.getJudgeId(), j.getWeight() != null ? j.getWeight() : BigDecimal.ONE);
        }

        // 4. 计算终分
        List<BigDecimal> scoreValues = scores.stream()
                .map(BizContestWorkScore::getTotalScore)
                .sorted()
                .collect(Collectors.toList());

        BigDecimal finalScore;

        switch (calculationRule) {
            case "max":
                finalScore = Collections.max(scoreValues);
                break;
            case "min":
                finalScore = Collections.min(scoreValues);
                break;
            case "weighted":
                BigDecimal weightedSum = BigDecimal.ZERO;
                BigDecimal totalWeight = BigDecimal.ZERO;
                for (BizContestWorkScore s : scores) {
                    BigDecimal w = weightMap.getOrDefault(s.getJudgeId(), BigDecimal.ONE);
                    weightedSum = weightedSum.add(s.getTotalScore().multiply(w));
                    totalWeight = totalWeight.add(w);
                }
                finalScore = totalWeight.compareTo(BigDecimal.ZERO) > 0
                        ? weightedSum.divide(totalWeight, 2, RoundingMode.HALF_UP)
                        : BigDecimal.ZERO;
                break;
            case "remove_max_min":
                if (scoreValues.size() < 3) {
                    throw BusinessException.of(ErrorCode.BAD_REQUEST, "去掉最高最低分需要至少3个评分");
                }
                List<BigDecimal> trimmed = scoreValues.subList(1, scoreValues.size() - 1);
                BigDecimal trimmedSum = trimmed.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
                finalScore = trimmedSum.divide(BigDecimal.valueOf(trimmed.size()), 2, RoundingMode.HALF_UP);
                break;
            case "remove_min":
                if (scoreValues.size() < 2) {
                    throw BusinessException.of(ErrorCode.BAD_REQUEST, "去掉最低分需要至少2个评分");
                }
                List<BigDecimal> withoutMin = scoreValues.subList(1, scoreValues.size());
                BigDecimal withoutMinSum = withoutMin.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
                finalScore = withoutMinSum.divide(BigDecimal.valueOf(withoutMin.size()), 2, RoundingMode.HALF_UP);
                break;
            case "average":
            default:
                BigDecimal sum = scoreValues.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
                finalScore = sum.divide(BigDecimal.valueOf(scoreValues.size()), 2, RoundingMode.HALF_UP);
                break;
        }

        log.info("作品终分计算完成，作品ID：{}，终分：{}，规则：{}", workId, finalScore, calculationRule);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("workId", workId);
        result.put("finalScore", finalScore);
        result.put("scoreCount", scores.size());
        result.put("calculationRule", calculationRule);
        return result;
    }

    // ====== 评委活动详情 ======

    @Override
    public Map<String, Object> getJudgeContestDetail(Long judgeId, Long contestId) {
        log.info("获取评委活动详情，评委ID：{}，活动ID：{}", judgeId, contestId);

        // 显式评委 或 已有作品分配记录
        LambdaQueryWrapper<BizContestJudge> judgeWrapper = new LambdaQueryWrapper<>();
        judgeWrapper.eq(BizContestJudge::getContestId, contestId);
        judgeWrapper.eq(BizContestJudge::getJudgeId, judgeId);
        judgeWrapper.eq(BizContestJudge::getValidState, 1);
        boolean explicitJudge = judgeMapper.selectCount(judgeWrapper) > 0;
        if (!explicitJudge) {
            LambdaQueryWrapper<BizContestWorkJudgeAssignment> assignCheck = new LambdaQueryWrapper<>();
            assignCheck.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
            assignCheck.eq(BizContestWorkJudgeAssignment::getJudgeId, judgeId);
            if (assignmentMapper.selectCount(assignCheck) == 0) {
                throw BusinessException.of(ErrorCode.FORBIDDEN, "您不是该活动的评委");
            }
        }

        BizContest contest = contestMapper.selectById(contestId);
        if (contest == null) {
            throw BusinessException.of(ErrorCode.NOT_FOUND, "活动不存在");
        }

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("contestId", contest.getId());
        result.put("contestName", contest.getContestName());
        result.put("contestType", contest.getContestType());
        result.put("contestState", contest.getContestState());
        result.put("status", contest.getStatus());
        result.put("reviewStartTime", contest.getReviewStartTime());
        result.put("reviewEndTime", contest.getReviewEndTime());

        // 评审规则（前端ReviewWorkModal需要此数据）
        if (contest.getReviewRuleId() != null) {
            BizContestReviewRule rule = reviewRuleMapper.selectById(contest.getReviewRuleId());
            if (rule != null) {
                Map<String, Object> ruleMap = new LinkedHashMap<>();
                ruleMap.put("id", rule.getId());
                ruleMap.put("ruleName", rule.getRuleName());
                ruleMap.put("ruleDescription", rule.getRuleDescription());
                ruleMap.put("judgeCount", rule.getJudgeCount());
                ruleMap.put("dimensions", rule.getDimensions());
                ruleMap.put("calculationRule", rule.getCalculationRule());
                result.put("reviewRule", ruleMap);
            }
        }

        return result;
    }
}
