package com.lesingle.modules.pub.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lesingle.common.constants.TenantConstants;
import com.lesingle.common.enums.*;
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.BizContestAttachment;
import com.lesingle.modules.biz.contest.entity.BizContestNotice;
import com.lesingle.modules.biz.contest.entity.BizContestRegistration;
import com.lesingle.modules.biz.contest.entity.BizContestWork;
import com.lesingle.modules.biz.contest.mapper.ContestAttachmentMapper;
import com.lesingle.modules.biz.contest.mapper.ContestMapper;
import com.lesingle.modules.biz.contest.mapper.ContestNoticeMapper;
import com.lesingle.modules.biz.contest.mapper.ContestRegistrationMapper;
import com.lesingle.modules.biz.contest.mapper.ContestWorkMapper;
import com.lesingle.modules.biz.contest.service.IContestWorkService;
import com.lesingle.modules.pub.dto.PublicRegisterActivityDto;
import com.lesingle.modules.ugc.entity.UgcWork;
import com.lesingle.modules.ugc.entity.UgcWorkPage;
import com.lesingle.modules.ugc.mapper.UgcWorkMapper;
import com.lesingle.modules.ugc.mapper.UgcWorkPageMapper;
import com.lesingle.modules.sys.entity.SysUser;
import com.lesingle.modules.sys.mapper.SysUserMapper;
import com.lesingle.modules.user.entity.UserChild;
import com.lesingle.modules.user.mapper.UserChildMapper;
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.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class PublicActivityService {

    private final ContestMapper contestMapper;
    private final ContestRegistrationMapper contestRegistrationMapper;
    private final ContestWorkMapper contestWorkMapper;
    private final IContestWorkService contestWorkService;
    private final UserChildMapper userChildMapper;
    private final UgcWorkMapper ugcWorkMapper;
    private final UgcWorkPageMapper ugcWorkPageMapper;
    private final ContestNoticeMapper contestNoticeMapper;
    private final ContestAttachmentMapper contestAttachmentMapper;
    private final SysUserMapper sysUserMapper;

    /**
     * 活动列表（公开）
     */
    public PageResult<BizContest> listActivities(int page, int pageSize, String keyword, String contestType) {
        LambdaQueryWrapper<BizContest> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BizContest::getVisibility, Visibility.PUBLIC.getValue())
                .eq(BizContest::getContestState, PublishStatus.PUBLISHED.getValue());
        if (StringUtils.hasText(keyword)) {
            wrapper.like(BizContest::getContestName, keyword);
        }
        if (StringUtils.hasText(contestType)) {
            wrapper.eq(BizContest::getContestType, contestType);
        }
        wrapper.orderByDesc(BizContest::getCreateTime);

        IPage<BizContest> result = contestMapper.selectPage(new Page<>(page, pageSize), wrapper);
        return PageResult.from(result);
    }

    /**
     * 活动详情
     */
    public Map<String, Object> getActivityDetail(Long id) {
        BizContest contest = contestMapper.selectById(id);
        if (contest == null) {
            throw new BusinessException(404, "活动不存在");
        }

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("id", contest.getId());
        result.put("contestName", contest.getContestName());
        result.put("contestType", contest.getContestType());
        result.put("contestState", contest.getContestState());
        result.put("status", contest.getStatus());
        result.put("visibility", contest.getVisibility());
        result.put("startTime", contest.getStartTime());
        result.put("endTime", contest.getEndTime());
        result.put("coverUrl", contest.getCoverUrl());
        result.put("posterUrl", contest.getPosterUrl());
        result.put("content", contest.getContent());
        result.put("address", contest.getAddress());
        result.put("contactName", contest.getContactName());
        result.put("contactPhone", contest.getContactPhone());
        result.put("contactQrcode", contest.getContactQrcode());
        result.put("organizers", contest.getOrganizers());
        result.put("coOrganizers", contest.getCoOrganizers());
        result.put("sponsors", contest.getSponsors());
        result.put("registerStartTime", contest.getRegisterStartTime());
        result.put("registerEndTime", contest.getRegisterEndTime());
        result.put("registerState", contest.getRegisterState());
        result.put("submitStartTime", contest.getSubmitStartTime());
        result.put("submitEndTime", contest.getSubmitEndTime());
        result.put("submitRule", contest.getSubmitRule());
        result.put("reviewStartTime", contest.getReviewStartTime());
        result.put("reviewEndTime", contest.getReviewEndTime());
        result.put("workType", contest.getWorkType());
        result.put("workRequirement", contest.getWorkRequirement());
        result.put("resultState", contest.getResultState());
        result.put("resultPublishTime", contest.getResultPublishTime());

        // 活动公告（仅已发布：publish_time 非空），租户范围与活动授权租户一致
        result.put("notices", listPublishedNoticesForPublic(contest));

        // 附件（与租户端赛事详情一致，便于 C 端下载）
        LambdaQueryWrapper<BizContestAttachment> attWrapper = new LambdaQueryWrapper<>();
        attWrapper.eq(BizContestAttachment::getContestId, id);
        attWrapper.orderByAsc(BizContestAttachment::getCreateTime);
        List<BizContestAttachment> attachmentEntities = contestAttachmentMapper.selectList(attWrapper);
        List<Map<String, Object>> attachments = new ArrayList<>();
        for (BizContestAttachment a : attachmentEntities) {
            Map<String, Object> am = new LinkedHashMap<>();
            am.put("id", a.getId());
            am.put("fileName", a.getFileName());
            am.put("fileUrl", a.getFileUrl());
            am.put("fileType", a.getFileType());
            am.put("format", a.getFormat());
            am.put("size", a.getSize());
            attachments.add(am);
        }
        result.put("attachments", attachments);

        return result;
    }

    /**
     * 公众端可见公告：已发布且属于该活动授权租户（若有）；无授权租户列表时仅按 contest_id 过滤。
     */
    private List<Map<String, Object>> listPublishedNoticesForPublic(BizContest contest) {
        Long contestId = contest.getId();
        LambdaQueryWrapper<BizContestNotice> nw = new LambdaQueryWrapper<>();
        nw.eq(BizContestNotice::getContestId, contestId);
        nw.isNotNull(BizContestNotice::getPublishTime);
        List<Integer> tenantInts = contest.getContestTenants();
        if (tenantInts != null && !tenantInts.isEmpty()) {
            List<Long> tenantIds = tenantInts.stream().map(Integer::longValue).toList();
            nw.in(BizContestNotice::getTenantId, tenantIds);
        }
        nw.orderByDesc(BizContestNotice::getPublishTime);
        List<BizContestNotice> rows = contestNoticeMapper.selectList(nw);
        List<Map<String, Object>> list = new ArrayList<>();
        for (BizContestNotice n : rows) {
            Map<String, Object> m = new LinkedHashMap<>();
            m.put("id", n.getId());
            m.put("title", n.getTitle());
            m.put("content", n.getContent());
            m.put("noticeType", n.getNoticeType());
            m.put("publishTime", n.getPublishTime());
            m.put("createTime", n.getCreateTime());
            list.add(m);
        }
        return list;
    }

    /**
     * 查询当前用户的报名信息（包含作品提交状态）
     */
    public Map<String, Object> getMyRegistration(Long contestId, Long userId) {
        BizContestRegistration reg = contestRegistrationMapper.selectOne(
                new LambdaQueryWrapper<BizContestRegistration>()
                        .eq(BizContestRegistration::getContestId, contestId)
                        .eq(BizContestRegistration::getUserId, userId)
                        .last("LIMIT 1"));

        if (reg == null) {
            return null;
        }

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("id", reg.getId());
        result.put("contestId", reg.getContestId());
        result.put("userId", reg.getUserId());
        result.put("registrationType", reg.getRegistrationType());
        result.put("registrationState", reg.getRegistrationState());
        result.put("registrationTime", reg.getRegistrationTime());

        // 查询是否已提交作品（只统计最新版本）
        Long workCount = contestWorkMapper.selectCount(
                new LambdaQueryWrapper<BizContestWork>()
                        .eq(BizContestWork::getRegistrationId, reg.getId())
                        .eq(BizContestWork::getIsLatest, true));
        result.put("hasSubmittedWork", workCount > 0);
        result.put("workCount", workCount);

        BizContestWork latest = contestWorkMapper.selectOne(
                new LambdaQueryWrapper<BizContestWork>()
                        .eq(BizContestWork::getRegistrationId, reg.getId())
                        .eq(BizContestWork::getIsLatest, true)
                        .last("LIMIT 1"));
        if (latest != null) {
            result.put("latestWorkId", latest.getId());
            result.put("latestWorkStatus", latest.getStatus());
            result.put("violationReason", latest.getViolationReason());
            result.put("violationMarkTime", latest.getViolationMarkTime());
        }

        return result;
    }

    /**
     * 我的报名列表
     */
    public Map<String, Object> getMyRegistrations(Long userId, int page, int pageSize) {
        // 查询报名列表
        LambdaQueryWrapper<BizContestRegistration> regWrapper = new LambdaQueryWrapper<>();
        regWrapper.eq(BizContestRegistration::getUserId, userId)
                .orderByDesc(BizContestRegistration::getRegistrationTime);

        IPage<BizContestRegistration> regPage = contestRegistrationMapper.selectPage(new Page<>(page, pageSize), regWrapper);

        // 获取所有 contestId
        List<Long> contestIds = regPage.getRecords().stream()
                .map(BizContestRegistration::getContestId)
                .distinct()
                .toList();

        // 批量查询活动信息
        Map<Long, BizContest> contestMap = new HashMap<>();
        if (!contestIds.isEmpty()) {
            List<BizContest> contests = contestMapper.selectBatchIds(contestIds);
            for (BizContest contest : contests) {
                contestMap.put(contest.getId(), contest);
            }
        }

        // 查询所有报名对应的作品
        List<BizContestRegistration> registrations = regPage.getRecords();
        List<Long> registrationIds = registrations.stream()
                .map(BizContestRegistration::getId)
                .toList();

        Map<Long, List<BizContestWork>> worksMap = new HashMap<>();
        if (!registrationIds.isEmpty()) {
            LambdaQueryWrapper<BizContestWork> workWrapper = new LambdaQueryWrapper<>();
            workWrapper.in(BizContestWork::getRegistrationId, registrationIds);
            List<BizContestWork> works = contestWorkMapper.selectList(workWrapper);
            for (BizContestWork work : works) {
                worksMap.computeIfAbsent(work.getRegistrationId(), k -> new ArrayList<>()).add(work);
            }
        }

        // 获取所有 childId
        List<Long> childIds = registrations.stream()
                .map(BizContestRegistration::getChildId)
                .filter(Objects::nonNull)
                .distinct()
                .toList();

        // 批量查询子女信息
        Map<Long, UserChild> childMap = new HashMap<>();
        if (!childIds.isEmpty()) {
            List<UserChild> children = userChildMapper.selectBatchIds(childIds);
            for (UserChild child : children) {
                childMap.put(child.getId(), child);
            }
        }

        // 组装返回数据
        List<Map<String, Object>> list = new ArrayList<>();
        for (BizContestRegistration reg : registrations) {
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("id", reg.getId());
            item.put("contestId", reg.getContestId());
            item.put("userId", reg.getUserId());
            item.put("registrationType", reg.getRegistrationType());
            item.put("registrationState", reg.getRegistrationState());
            item.put("registrationTime", reg.getRegistrationTime());
            item.put("participantType", reg.getParticipantType());
            item.put("childId", reg.getChildId());
            item.put("teamId", reg.getTeamId());

            // 关联子女信息
            if (reg.getChildId() != null) {
                UserChild child = childMap.get(reg.getChildId());
                if (child != null) {
                    Map<String, Object> childInfo = new LinkedHashMap<>();
                    childInfo.put("id", child.getId());
                    childInfo.put("name", child.getName());
                    childInfo.put("gender", child.getGender());
                    childInfo.put("grade", child.getGrade());
                    item.put("child", childInfo);
                }
            }

            // 关联活动信息
            BizContest contest = contestMap.get(reg.getContestId());
            if (contest != null) {
                Map<String, Object> contestInfo = new LinkedHashMap<>();
                contestInfo.put("id", contest.getId());
                contestInfo.put("contestName", contest.getContestName());
                contestInfo.put("contestType", contest.getContestType());
                contestInfo.put("coverUrl", contest.getCoverUrl());
                contestInfo.put("posterUrl", contest.getPosterUrl());
                contestInfo.put("startTime", contest.getStartTime());
                contestInfo.put("endTime", contest.getEndTime());
                contestInfo.put("submitStartTime", contest.getSubmitStartTime());
                contestInfo.put("submitEndTime", contest.getSubmitEndTime());
                item.put("contest", contestInfo);
            }

            // 关联作品信息
            List<BizContestWork> works = worksMap.get(reg.getId());
            if (works != null && !works.isEmpty()) {
                List<Map<String, Object>> worksList = new ArrayList<>();
                for (BizContestWork work : works) {
                    Map<String, Object> workInfo = new LinkedHashMap<>();
                    workInfo.put("id", work.getId());
                    workInfo.put("title", work.getTitle());
                    workInfo.put("description", work.getDescription());
                    workInfo.put("previewUrl", work.getPreviewUrl());
                    workInfo.put("previewUrls", work.getPreviewUrls());
                    workInfo.put("attachments", work.getFiles());
                    workInfo.put("createTime", work.getCreateTime());
                    worksList.add(workInfo);
                }
                item.put("works", worksList);
            }

            list.add(item);
        }

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("list", list);
        result.put("total", regPage.getTotal());
        result.put("page", regPage.getCurrent());
        result.put("pageSize", regPage.getSize());

        return result;
    }

    /**
     * 报名活动
     */
    @Transactional
    public BizContestRegistration register(Long contestId, Long userId, Long tenantId, PublicRegisterActivityDto dto) {
        log.info("开始报名活动，contestId: {}, userId: {}, tenantId: {}", contestId, userId, tenantId);

        // 检查是否已报名
        Long existCount = contestRegistrationMapper.selectCount(
                new LambdaQueryWrapper<BizContestRegistration>()
                        .eq(BizContestRegistration::getContestId, contestId)
                        .eq(BizContestRegistration::getUserId, userId));
        if (existCount > 0) {
            throw new BusinessException(400, "您已报名该活动");
        }

        // 检查活动是否存在且可报名
        BizContest contest = contestMapper.selectById(contestId);
        if (contest == null) {
            throw new BusinessException(404, "活动不存在");
        }
        if (!PublishStatus.PUBLISHED.getValue().equals(contest.getContestState())) {
            throw new BusinessException(400, "活动未发布");
        }

        // 子女账号仅允许以本人报名，不可代报其他子女
        SysUser currentUser = sysUserMapper.selectById(userId);
        if (currentUser != null && UserType.CHILD.getValue().equals(currentUser.getUserType())) {
            String pt = dto.getParticipantType() != null ? dto.getParticipantType() : ParticipantType.SELF.getValue();
            if (ParticipantType.CHILD.getValue().equals(pt) || dto.getChildId() != null) {
                throw new BusinessException(400, "子女账号只能以本人身份报名");
            }
        }

        BizContestRegistration reg = new BizContestRegistration();
        reg.setContestId(contestId);
        reg.setUserId(userId);
        // 使用活动的授权租户ID（管理端按租户查询报名数据）
        if (contest.getContestTenants() != null && !contest.getContestTenants().isEmpty()) {
            reg.setTenantId(contest.getContestTenants().get(0).longValue());
        } else {
            reg.setTenantId(tenantId);
        }
        reg.setRegistrationType(contest.getContestType());
        reg.setParticipantType(dto.getParticipantType() != null ? dto.getParticipantType() : ParticipantType.SELF.getValue());
        reg.setChildId(dto.getChildId());
        reg.setTeamId(dto.getTeamId());
        reg.setRegistrationState(Boolean.TRUE.equals(contest.getRequireAudit()) ? RegistrationStatus.PENDING.getValue() : RegistrationStatus.PASSED.getValue());
        reg.setRegistrationTime(LocalDateTime.now());
        // 设置必填字段
        reg.setRegistrant(userId.intValue());
        reg.setAccountNo("user_" + userId);
        reg.setAccountName("user_" + userId);

        log.info("保存报名记录：{}", reg);
        contestRegistrationMapper.insert(reg);
        log.info("报名成功，ID: {}", reg.getId());
        return reg;
    }

    /**
     * 提交作品
     */
    @Transactional
    public BizContestWork submitWork(Long contestId, Long userId, Long tenantId, Map<String, Object> dto) {
        // 检查报名状态（区分不同状态给出明确提示）
        BizContestRegistration anyReg = contestRegistrationMapper.selectOne(
                new LambdaQueryWrapper<BizContestRegistration>()
                        .eq(BizContestRegistration::getContestId, contestId)
                        .eq(BizContestRegistration::getUserId, userId)
                        .last("LIMIT 1"));
        if (anyReg == null) {
            throw new BusinessException(400, "您尚未报名该活动，请先报名");
        }
        if (!RegistrationStatus.PASSED.getValue().equals(anyReg.getRegistrationState())) {
            String state = anyReg.getRegistrationState();
            String msg = switch (state) {
                case "pending" -> "报名审核中，请等待审核通过后再提交作品";  // RegistrationStatus.PENDING
                case "rejected" -> "报名已被拒绝，无法提交作品";  // RegistrationStatus.REJECTED
                case "withdrawn" -> "报名已撤回，请重新报名";
                default -> "报名状态异常（" + state + "），无法提交作品";
            };
            throw new BusinessException(400, msg);
        }
        BizContestRegistration reg = anyReg;

        // 查询活动提交规则
        BizContest contest = contestMapper.selectById(contestId);
        String submitRule = contest != null ? contest.getSubmitRule() : "once";

        // 查询已有作品
        BizContestWork existingWork = contestWorkMapper.selectOne(
                new LambdaQueryWrapper<BizContestWork>()
                        .eq(BizContestWork::getContestId, contestId)
                        .eq(BizContestWork::getRegistrationId, reg.getId())
                        .eq(BizContestWork::getIsLatest, true)
                        .orderByDesc(BizContestWork::getVersion)
                        .last("LIMIT 1"));

        if (existingWork != null) {
            boolean allowReplace = "resubmit".equals(submitRule)
                    || WorkStatus.VIOLATION.getValue().equals(existingWork.getStatus());
            if (!allowReplace) {
                throw new BusinessException(400, "该活动仅允许提交一次作品");
            }
            // resubmit 或评委标记违规后重提：将旧作品标记为非最新
            existingWork.setIsLatest(false);
            contestWorkMapper.updateById(existingWork);
        }

        int nextVersion = (existingWork != null) ? existingWork.getVersion() + 1 : 1;

        BizContestWork work = new BizContestWork();
        work.setContestId(contestId);
        work.setRegistrationId(reg.getId());
        // 使用报名记录的租户ID（已在 register 时设置为活动的租户，确保管理端可见）
        work.setTenantId(reg.getTenantId());
        work.setSubmitterUserId(userId);
        work.setStatus(WorkStatus.SUBMITTED.getValue());
        work.setSubmitTime(LocalDateTime.now());
        work.setVersion(nextVersion);
        work.setIsLatest(true);
        work.setWorkNo(contestWorkService.nextContestWorkNo(contestId));
        work.setSubmitterAccountNo(reg.getAccountNo());

        // 从作品库选择作品提交（快照复制）
        if (dto.get("userWorkId") != null) {
            Long userWorkId = Long.valueOf(dto.get("userWorkId").toString());
            work.setUserWorkId(userWorkId);

            // 查询用户作品
            UgcWork ugcWork = ugcWorkMapper.selectById(userWorkId);
            if (ugcWork == null || ugcWork.getIsDeleted() == 1) {
                throw new BusinessException(404, "作品不存在");
            }
            if (!ugcWork.getUserId().equals(userId)) {
                throw new BusinessException(403, "无权使用该作品");
            }
            if ("rejected".equals(ugcWork.getStatus()) || "taken_down".equals(ugcWork.getStatus())) {
                throw new BusinessException(400, "该作品状态不可提交");
            }

            // 查询绘本分页
            List<UgcWorkPage> pages = ugcWorkPageMapper.selectList(
                    new LambdaQueryWrapper<UgcWorkPage>()
                            .eq(UgcWorkPage::getWorkId, userWorkId)
                            .orderByAsc(UgcWorkPage::getPageNo));

            // 复制快照字段
            work.setTitle(ugcWork.getTitle());
            work.setDescription(ugcWork.getDescription());
            work.setPreviewUrl(ugcWork.getCoverUrl());
            work.setAiModelMeta(ugcWork.getAiMeta());

            // previewUrls = 所有页面图片
            List<String> previewUrls = pages.stream()
                    .map(UgcWorkPage::getImageUrl)
                    .filter(Objects::nonNull)
                    .toList();
            work.setPreviewUrls(previewUrls);

            // files = 分页完整快照
            List<Map<String, Object>> filesSnapshot = pages.stream()
                    .map(p -> {
                        Map<String, Object> m = new LinkedHashMap<>();
                        m.put("pageNo", p.getPageNo());
                        m.put("imageUrl", p.getImageUrl());
                        m.put("text", p.getText());
                        m.put("audioUrl", p.getAudioUrl());
                        return m;
                    })
                    .toList();
            work.setFiles(filesSnapshot);
        } else {
            // 旧逻辑：直接上传
            work.setTitle((String) dto.get("title"));
            work.setDescription((String) dto.get("description"));
            work.setFiles(dto.get("files"));
        }
        contestWorkMapper.insert(work);
        return work;
    }

    /**
     * 公开活动参赛作品列表：本活动下各报名记录的最新版本（排除违规、下架等不宜公开展示的状态）
     */
    public PageResult<Map<String, Object>> listPublicContestWorks(Long contestId, int page, int pageSize) {
        BizContest contest = contestMapper.selectById(contestId);
        if (contest == null) {
            throw new BusinessException(404, "活动不存在");
        }
        if (!Visibility.PUBLIC.getValue().equals(contest.getVisibility())) {
            throw new BusinessException(403, "活动不可公开访问");
        }
        if (!PublishStatus.PUBLISHED.getValue().equals(contest.getContestState())) {
            throw new BusinessException(400, "活动未发布");
        }

        LambdaQueryWrapper<BizContestWork> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BizContestWork::getContestId, contestId)
                .eq(BizContestWork::getIsLatest, true)
                .eq(BizContestWork::getValidState, 1)
                .notIn(BizContestWork::getStatus,
                        List.of(WorkStatus.VIOLATION.getValue(), WorkStatus.TAKEN_DOWN.getValue()))
                .orderByDesc(BizContestWork::getSubmitTime)
                .orderByDesc(BizContestWork::getId);

        IPage<BizContestWork> workPage = contestWorkMapper.selectPage(new Page<>(page, pageSize), wrapper);
        List<BizContestWork> records = workPage.getRecords();
        if (records.isEmpty()) {
            return PageResult.from(workPage, Collections.emptyList());
        }

        Set<Long> ugcIds = records.stream()
                .map(BizContestWork::getUserWorkId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, UgcWork> ugcMap = new HashMap<>();
        if (!ugcIds.isEmpty()) {
            List<UgcWork> ugcList = ugcWorkMapper.selectBatchIds(ugcIds);
            for (UgcWork uw : ugcList) {
                if (uw != null && (uw.getIsDeleted() == null || uw.getIsDeleted() == 0)) {
                    ugcMap.put(uw.getId(), uw);
                }
            }
        }

        Set<Long> submitterIds = records.stream()
                .map(BizContestWork::getSubmitterUserId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, SysUser> userMap = new HashMap<>();
        if (!submitterIds.isEmpty()) {
            List<SysUser> users = sysUserMapper.selectBatchIds(submitterIds);
            for (SysUser u : users) {
                userMap.put(u.getId(), u);
            }
        }

        List<Map<String, Object>> list = new ArrayList<>();
        for (BizContestWork work : records) {
            Map<String, Object> row = new LinkedHashMap<>();
            Long userWorkId = work.getUserWorkId();
            UgcWork ugc = userWorkId != null ? ugcMap.get(userWorkId) : null;

            String coverUrl = work.getPreviewUrl();
            String originalImageUrl = null;
            int likeCount = 0;
            int viewCount = 0;
            if (ugc != null) {
                if (StringUtils.hasText(ugc.getCoverUrl())) {
                    coverUrl = ugc.getCoverUrl();
                }
                originalImageUrl = ugc.getOriginalImageUrl();
                likeCount = ugc.getLikeCount() != null ? ugc.getLikeCount() : 0;
                viewCount = ugc.getViewCount() != null ? ugc.getViewCount() : 0;
            }

            // 前端与作品广场一致：id 为 UGC 作品 ID（点赞、跳转 /p/works/:id）
            row.put("id", userWorkId);
            row.put("contestWorkId", work.getId());
            row.put("title", work.getTitle());
            row.put("coverUrl", coverUrl);
            row.put("originalImageUrl", originalImageUrl);
            row.put("likeCount", likeCount);
            row.put("viewCount", viewCount);

            Map<String, Object> creator = new LinkedHashMap<>();
            SysUser su = work.getSubmitterUserId() != null ? userMap.get(work.getSubmitterUserId()) : null;
            if (su != null) {
                creator.put("nickname", StringUtils.hasText(su.getNickname()) ? su.getNickname() : su.getUsername());
                creator.put("avatar", su.getAvatar());
            } else {
                creator.put("nickname", StringUtils.hasText(work.getSubmitterAccountNo()) ? work.getSubmitterAccountNo() : "用户");
                creator.put("avatar", null);
            }
            row.put("creator", creator);

            list.add(row);
        }

        return PageResult.from(workPage, list);
    }
}
