import {
  Injectable,
  NotFoundException,
  ConflictException,
  BadRequestException,
} from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateContestDto } from './dto/create-contest.dto';
import { UpdateContestDto } from './dto/update-contest.dto';
import { QueryContestDto } from './dto/query-contest.dto';

@Injectable()
export class ContestsService {
  constructor(private prisma: PrismaService) {}

  /**
   * 检查活动是否对租户可见
   */
  private isContestVisibleToTenant(contest: any, tenantId: number): boolean {
    // 如果活动未发布，对租户不可见
    if (contest.contestState !== 'published') {
      return false;
    }

    // 如果contestTenants为null，表示所有租户可见
    if (!contest.contestTenants) {
      return true;
    }

    // 解析JSON数组
    try {
      const tenantIds = Array.isArray(contest.contestTenants)
        ? contest.contestTenants
        : JSON.parse(contest.contestTenants as string);
      return tenantIds.includes(tenantId);
    } catch {
      return false;
    }
  }

  /**
   * 验证时间顺序
   */
  private validateTimeOrder(dto: CreateContestDto | UpdateContestDto) {
    const dtoAny = dto as any;
    // 对于 UpdateContestDto，某些字段可能是 undefined，需要检查
    if (
      !dtoAny.registerStartTime ||
      !dtoAny.registerEndTime ||
      !dtoAny.submitStartTime ||
      !dtoAny.submitEndTime ||
      !dtoAny.reviewStartTime ||
      !dtoAny.reviewEndTime
    ) {
      // 如果缺少必需的时间字段，跳过验证（UpdateContestDto 可能只更新部分字段）
      return;
    }

    const times = {
      registerStart: new Date(dtoAny.registerStartTime),
      registerEnd: new Date(dtoAny.registerEndTime),
      submitStart: new Date(dtoAny.submitStartTime),
      submitEnd: new Date(dtoAny.submitEndTime),
      reviewStart: new Date(dtoAny.reviewStartTime),
      reviewEnd: new Date(dtoAny.reviewEndTime),
    };

    if (times.registerStart >= times.registerEnd) {
      throw new BadRequestException('报名开始时间必须早于报名结束时间');
    }

    if (times.registerEnd >= times.submitStart) {
      throw new BadRequestException('报名结束时间必须早于作品提交开始时间');
    }

    if (times.submitStart >= times.submitEnd) {
      throw new BadRequestException('作品提交开始时间必须早于作品提交结束时间');
    }

    if (times.submitEnd >= times.reviewStart) {
      throw new BadRequestException('作品提交结束时间必须早于评审开始时间');
    }

    if (times.reviewStart >= times.reviewEnd) {
      throw new BadRequestException('评审开始时间必须早于评审结束时间');
    }

    if (dtoAny.resultPublishTime) {
      const resultPublish = new Date(dtoAny.resultPublishTime);
      if (times.reviewEnd >= resultPublish) {
        throw new BadRequestException('评审结束时间必须早于结果发布时间');
      }
    }

    if (dtoAny.startTime && dtoAny.endTime) {
      const startTime = new Date(dtoAny.startTime);
      const endTime = new Date(dtoAny.endTime);
      if (startTime >= endTime) {
        throw new BadRequestException('活动开始时间必须早于活动结束时间');
      }
    }
  }

  async create(createContestDto: CreateContestDto, creatorId?: number) {
    // 检查活动名称是否已存在
    const existing = await this.prisma.contest.findUnique({
      where: { contestName: createContestDto.contestName },
    });

    if (existing) {
      throw new ConflictException('活动名称已存在');
    }

    // 验证时间顺序
    this.validateTimeOrder(createContestDto);

    const data: any = {
      contestName: createContestDto.contestName,
      contestType: createContestDto.contestType,
      startTime: new Date(createContestDto.startTime),
      endTime: new Date(createContestDto.endTime),
      address: createContestDto.address,
      content: createContestDto.content,
      contestTenants: createContestDto.contestTenants
        ? JSON.stringify(createContestDto.contestTenants)
        : null,
      coverUrl: createContestDto.coverUrl,
      posterUrl: createContestDto.posterUrl,
      contactName: createContestDto.contactName,
      contactPhone: createContestDto.contactPhone,
      contactQrcode: createContestDto.contactQrcode,
      organizers: createContestDto.organizers ? [createContestDto.organizers] : null,
      coOrganizers: createContestDto.coOrganizers ? [createContestDto.coOrganizers] : null,
      sponsors: createContestDto.sponsors ? [createContestDto.sponsors] : null,
      // 报名配置
      registerStartTime: new Date(createContestDto.registerStartTime),
      registerEndTime: new Date(createContestDto.registerEndTime),
      requireAudit: createContestDto.requireAudit ?? true,
      allowedGrades: createContestDto.allowedGrades
        ? JSON.stringify(createContestDto.allowedGrades)
        : null,
      allowedClasses: createContestDto.allowedClasses
        ? JSON.stringify(createContestDto.allowedClasses)
        : null,
      teamMinMembers: createContestDto.teamMinMembers,
      teamMaxMembers: createContestDto.teamMaxMembers,
      // 作品配置
      submitRule: createContestDto.submitRule || 'once',
      submitStartTime: new Date(createContestDto.submitStartTime),
      submitEndTime: new Date(createContestDto.submitEndTime),
      workType: createContestDto.workType,
      workRequirement: createContestDto.workRequirement,
      // 评审配置
      reviewRuleId: createContestDto.reviewRuleId || null,
      reviewStartTime: new Date(createContestDto.reviewStartTime),
      reviewEndTime: new Date(createContestDto.reviewEndTime),
      resultPublishTime: createContestDto.resultPublishTime
        ? new Date(createContestDto.resultPublishTime)
        : null,
      // 可见范围与定向推送
      visibility: createContestDto.visibility || 'designated',
      targetCities: createContestDto.targetCities || null,
      ageMin: createContestDto.ageMin || null,
      ageMax: createContestDto.ageMax || null,
      contestState: 'unpublished',
      status: 'ongoing',
      resultState: 'unpublished',
    };

    if (creatorId) {
      data.creator = creatorId;
    }

    return this.prisma.contest.create({
      data,
      include: {
        attachments: {
          where: { validState: 1 },
        },
        reviewRule: true,
        _count: {
          select: {
            registrations: true,
            works: true,
            teams: true,
            judges: true,
          },
        },
      },
    });
  }

  /**
   * 判断活动当前所处阶段
   */
  private getContestStage(contest: any): string {
    if (contest.contestState === 'unpublished') return 'unpublished';
    if (contest.status === 'finished') return 'finished';

    const now = new Date();
    const regStart = contest.registerStartTime ? new Date(contest.registerStartTime) : null;
    const regEnd = contest.registerEndTime ? new Date(contest.registerEndTime) : null;
    const subStart = contest.submitStartTime ? new Date(contest.submitStartTime) : null;
    const subEnd = contest.submitEndTime ? new Date(contest.submitEndTime) : null;
    const revStart = contest.reviewStartTime ? new Date(contest.reviewStartTime) : null;
    const revEnd = contest.reviewEndTime ? new Date(contest.reviewEndTime) : null;

    if (regStart && regEnd && now >= regStart && now <= regEnd) return 'registering';
    if (subStart && subEnd && now >= subStart && now <= subEnd) return 'submitting';
    if (revStart && revEnd && now >= revStart && now <= revEnd) return 'reviewing';

    // 所有阶段都已过
    if (revEnd && now > revEnd) return 'finished';
    return 'published'; // 已发布但尚未到报名阶段
  }

  /**
   * 活动统计（仅超管）
   */
  async getStats() {
    const contests = await this.prisma.contest.findMany({
      where: { validState: 1 },
      select: {
        id: true,
        contestState: true,
        status: true,
        registerStartTime: true,
        registerEndTime: true,
        submitStartTime: true,
        submitEndTime: true,
        reviewStartTime: true,
        reviewEndTime: true,
      },
    });

    const result = { total: contests.length, unpublished: 0, registering: 0, submitting: 0, reviewing: 0, finished: 0 };
    for (const c of contests) {
      const stage = this.getContestStage(c);
      if (stage === 'unpublished') result.unpublished++;
      else if (stage === 'registering') result.registering++;
      else if (stage === 'submitting') result.submitting++;
      else if (stage === 'reviewing') result.reviewing++;
      else if (stage === 'finished') result.finished++;
    }
    return result;
  }

  async findAll(queryDto: QueryContestDto, tenantId?: number) {
    const {
      page = 1,
      pageSize = 10,
      contestName,
      contestState,
      contestType,
      visibility,
      status,
      stage,
      creatorTenantId,
    } = queryDto;
    const skip = (page - 1) * pageSize;

    const where: any = {
      validState: 1,
    };

    if (contestName) {
      where.contestName = {
        contains: contestName,
      };
    }

    // stage 筛选中 unpublished 映射到 contestState
    if (stage === 'unpublished') {
      where.contestState = 'unpublished';
    } else if (stage === 'finished') {
      where.status = 'finished';
      where.contestState = 'published';
    } else if (stage) {
      // registering / submitting / reviewing — 需要应用层过滤，但先限定已发布
      where.contestState = 'published';
    } else {
      // 无 stage 时保留原有逻辑
      if (contestState) {
        where.contestState = contestState;
      }
      if (status) {
        where.status = status;
      }
    }

    if (contestType) {
      where.contestType = contestType;
    }

    if (visibility) {
      where.visibility = visibility;
    }

    if (creatorTenantId) {
      // 通过创建者的 tenantId 筛选：查出该租户下的用户 ID
      where.creatorUser = { is: { tenantId: creatorTenantId } };
    }

    // 查询时多取数据，用于 stage 应用层过滤
    const needStageFilter = stage && !['unpublished', 'finished'].includes(stage);
    const queryTake = needStageFilter ? pageSize * 5 : pageSize;

    // 先查询所有符合条件的活动
    const [allList, allTotal] = await Promise.all([
      this.prisma.contest.findMany({
        where,
        skip: needStageFilter ? 0 : skip, // stage 过滤时从头查
        take: needStageFilter ? 500 : queryTake,
        orderBy: {
          createTime: 'desc',
        },
        include: {
          attachments: {
            where: { validState: 1 },
            take: 5,
          },
          reviewRule: true,
          creatorUser: {
            select: { tenantId: true, tenant: { select: { id: true, name: true, code: true } } },
          },
          _count: {
            select: {
              registrations: true,
              works: true,
              teams: true,
              judges: true,
            },
          },
        },
      }),
      this.prisma.contest.count({ where }),
    ]);

    // 应用层过滤：stage 和租户可见性
    let filteredList = allList;
    let filteredTotal = allTotal;

    // stage 应用层过滤（registering / submitting / reviewing）
    if (needStageFilter) {
      filteredList = filteredList.filter((c) => this.getContestStage(c) === stage);
      filteredTotal = filteredList.length;
      filteredList = filteredList.slice(skip, skip + pageSize);
    }

    if (tenantId) {
      // 检查是否为超级租户
      const tenant = await this.prisma.tenant.findUnique({
        where: { id: tenantId },
        select: { isSuper: true },
      });
      const isSuperTenant = tenant?.isSuper === 1;

      // 超级租户可以看到所有活动，普通租户只能看到已发布且在公开范围内的活动
      if (!isSuperTenant) {
        filteredList = allList.filter((contest) =>
          this.isContestVisibleToTenant(contest, tenantId),
        );
        // 重新计算总数（简化处理，实际应该用原生SQL）
        filteredTotal = filteredList.length;
        // 限制返回数量
        filteredList = filteredList.slice(0, pageSize);
      }
    }

    // 解析 contestTenants JSON 字符串为数组，并计算评审统计数据
    const parsedList = await Promise.all(
      filteredList.map(async (contest) => {
        // 计算总作品数（已提交或评审中的作品）
        const totalWorksCount = await this.prisma.contestWork.count({
          where: {
            contestId: contest.id,
            status: { in: ['submitted', 'reviewing'] },
            isLatest: true,
            validState: 1,
          },
        });

        // 计算已完成评审的作品数（有评分记录的作品）
        const reviewedCount = await this.prisma.contestWork.count({
          where: {
            contestId: contest.id,
            status: { in: ['submitted', 'reviewing'] },
            isLatest: true,
            validState: 1,
            scores: {
              some: {
                validState: 1,
              },
            },
          },
        });

        // 提取创建者所属租户信息
        const creatorTenant = (contest as any).creatorUser?.tenant || null;

        return {
          ...contest,
          creatorUser: undefined, // 不暴露 creatorUser 详情
          creatorTenant,
          stage: this.getContestStage(contest),
          contestTenants: this.parseContestTenants(contest.contestTenants),
          totalWorksCount,
          reviewedCount,
          // 覆盖 _count.works 为正确的统计数据（只统计 isLatest=true 且 validState=1 的作品）
          _count: {
            ...contest._count,
            works: totalWorksCount,
          },
        };
      }),
    );

    return {
      list: parsedList,
      total: filteredTotal,
      page,
      pageSize,
    };
  }

  /**
   * 解析 contestTenants JSON 字符串为数组
   */
  private parseContestTenants(contestTenants: any): number[] | null {
    if (!contestTenants) {
      return null;
    }
    try {
      return Array.isArray(contestTenants)
        ? contestTenants
        : JSON.parse(contestTenants as string);
    } catch {
      return null;
    }
  }

  /**
   * 获取我参与的活动列表
   * @param role 角色：student-学生报名的活动, teacher-教师指导的活动, judge-评委评审的活动
   */
  async findMyContests(queryDto: QueryContestDto, userId: number, tenantId?: number, role?: string) {
    const {
      page = 1,
      pageSize = 10,
      contestName,
      contestState,
      contestType,
      status,
    } = queryDto;
    const skip = (page - 1) * pageSize;

    let contestIds: number[] = [];

    // 根据角色查询不同的活动
    if (role === 'judge') {
      // 评委：查询作为评委参与的活动
      const judgeRecords = await this.prisma.contestJudge.findMany({
        where: {
          judgeId: userId,
          validState: 1,
        },
        select: {
          contestId: true,
        },
        distinct: ['contestId'],
      });
      contestIds = judgeRecords.map((r) => r.contestId);
    } else if (role === 'teacher') {
      // 教师：查询作为指导老师参与的活动
      // 1. 从报名指导老师关联表查询（个人参与）
      const teacherRecords = await this.prisma.contestRegistrationTeacher.findMany({
        where: {
          userId,
        },
        select: {
          registration: {
            select: {
              contestId: true,
            },
          },
        },
      });
      const contestIdsFromRegistration = teacherRecords.map((r) => r.registration.contestId as number);

      // 2. 从团队成员表查询（团队参与，role='mentor'）
      const mentorRecords = await this.prisma.contestTeamMember.findMany({
        where: {
          userId,
          role: 'mentor',
        },
        select: {
          team: {
            select: {
              contestId: true,
            },
          },
        },
      });
      const contestIdsFromTeam = mentorRecords.map((r) => r.team.contestId);

      // 合并去重
      contestIds = Array.from(new Set([...contestIdsFromRegistration, ...contestIdsFromTeam]));
    } else {
      // 学生/默认：查询报名的活动
      // 1. 从报名记录查询（个人参与报名或团队参与队长）
      const registrationWhere: any = {
        userId,
      };

      if (tenantId) {
        registrationWhere.tenantId = tenantId;
      }

      const registrations = await this.prisma.contestRegistration.findMany({
        where: registrationWhere,
        select: {
          contestId: true,
        },
        distinct: ['contestId'],
      });
      const contestIdsFromRegistration = registrations.map((r) => r.contestId);

      // 2. 从团队成员表查询（团队参与成员，role='leader' 或 'member'）
      const teamMemberWhere: any = {
        userId,
        role: { in: ['leader', 'member'] },
      };

      if (tenantId) {
        teamMemberWhere.tenantId = tenantId;
      }

      const teamMembers = await this.prisma.contestTeamMember.findMany({
        where: teamMemberWhere,
        select: {
          team: {
            select: {
              contestId: true,
            },
          },
        },
      });
      const contestIdsFromTeam = teamMembers.map((r) => r.team.contestId);

      // 合并去重
      contestIds = Array.from(new Set([...contestIdsFromRegistration, ...contestIdsFromTeam]));
    }

    if (contestIds.length === 0) {
      return {
        list: [],
        total: 0,
        page,
        pageSize,
      };
    }

    const where: any = {
      id: { in: contestIds },
      validState: 1,
    };

    if (contestName) {
      where.contestName = {
        contains: contestName,
      };
    }

    if (contestState) {
      where.contestState = contestState;
    }

    if (status) {
      where.status = status;
    }

    if (contestType) {
      where.contestType = contestType;
    }

    const [list, total] = await Promise.all([
      this.prisma.contest.findMany({
        where,
        skip,
        take: pageSize,
        orderBy: {
          createTime: 'desc',
        },
        include: {
          attachments: {
            where: { validState: 1 },
            take: 5,
          },
          reviewRule: true,
          registrations: {
            where: {
              userId,
              tenantId: tenantId || undefined,
            },
            select: {
              id: true,
              registrationState: true,
              registrationTime: true,
            },
          },
          _count: {
            select: {
              registrations: true,
              works: true,
              teams: true,
              judges: true,
            },
          },
        },
      }),
      this.prisma.contest.count({ where }),
    ]);

    return {
      list,
      total,
      page,
      pageSize,
    };
  }

  async findOne(id: number, tenantId?: number) {
    const where: any = {
      id,
      validState: 1,
    };

    const contest = await this.prisma.contest.findFirst({
      where,
      include: {
        attachments: {
          where: { validState: 1 },
        },
        reviewRule: true,
        notices: {
          where: { validState: 1 },
          orderBy: [{ priority: 'desc' }, { publishTime: 'desc' }],
          take: 10,
        },
        _count: {
          select: {
            registrations: true,
            works: true,
            teams: true,
            judges: true,
          },
        },
      },
    });

    if (!contest) {
      throw new NotFoundException('活动不存在');
    }

    // 租户过滤：检查活动是否对租户可见（超级租户可以查看所有活动）
    if (tenantId) {
      const tenant = await this.prisma.tenant.findUnique({
        where: { id: tenantId },
        select: { isSuper: true },
      });
      const isSuperTenant = tenant?.isSuper === 1;

      if (!isSuperTenant && !this.isContestVisibleToTenant(contest, tenantId)) {
        throw new NotFoundException('活动不存在或无权访问');
      }
    }

    // 解析 contestTenants JSON 字符串为数组
    return {
      ...contest,
      contestTenants: this.parseContestTenants(contest.contestTenants),
    };
  }

  async update(
    id: number,
    updateContestDto: UpdateContestDto,
    modifierId?: number,
  ) {
    const contest = await this.prisma.contest.findUnique({
      where: { id },
    });

    if (!contest) {
      throw new NotFoundException('活动不存在');
    }

    // 允许取消发布，即使有报名记录也可以（保留报名和作品数据）

    // 如果更新了活动名称，检查是否重复
    if (
      (updateContestDto as any).contestName &&
      (updateContestDto as any).contestName !== contest.contestName
    ) {
      const existing = await this.prisma.contest.findUnique({
        where: { contestName: (updateContestDto as any).contestName },
      });

      if (existing) {
        throw new ConflictException('活动名称已存在');
      }
    }

    // 特殊处理：开始评审时的友好提示
    const dto = updateContestDto as any;
    if (dto.reviewStartTime && Object.keys(dto).length === 1) {
      const newReviewStartTime = new Date(dto.reviewStartTime);
      if (newReviewStartTime < contest.submitEndTime) {
        const submitEndTimeStr = contest.submitEndTime.toLocaleString('zh-CN', {
          year: 'numeric',
          month: '2-digit',
          day: '2-digit',
          hour: '2-digit',
          minute: '2-digit',
        });
        throw new BadRequestException(
          `还未到评审时间，作品提交将于 ${submitEndTimeStr} 结束`,
        );
      }
    }

    // 验证时间顺序（如果提供了时间字段）
    if (
      dto.registerStartTime ||
      dto.registerEndTime ||
      dto.submitStartTime ||
      dto.submitEndTime ||
      dto.reviewStartTime ||
      dto.reviewEndTime
    ) {
      // 合并现有数据和更新数据，确保所有必需字段都存在
      const mergedDto = {
        registerStartTime:
          (updateContestDto as any).registerStartTime ||
          contest.registerStartTime.toISOString(),
        registerEndTime:
          (updateContestDto as any).registerEndTime ||
          contest.registerEndTime.toISOString(),
        submitStartTime:
          (updateContestDto as any).submitStartTime ||
          contest.submitStartTime.toISOString(),
        submitEndTime:
          (updateContestDto as any).submitEndTime ||
          contest.submitEndTime.toISOString(),
        reviewStartTime:
          (updateContestDto as any).reviewStartTime ||
          contest.reviewStartTime.toISOString(),
        reviewEndTime:
          (updateContestDto as any).reviewEndTime ||
          contest.reviewEndTime.toISOString(),
        resultPublishTime:
          (updateContestDto as any).resultPublishTime ||
          contest.resultPublishTime?.toISOString(),
        startTime:
          (updateContestDto as any).startTime ||
          contest.startTime.toISOString(),
        endTime:
          (updateContestDto as any).endTime || contest.endTime.toISOString(),
      } as CreateContestDto;
      this.validateTimeOrder(mergedDto);
    }

    const data: any = {};

    if (dto.contestName !== undefined) {
      data.contestName = dto.contestName;
    }
    if (dto.contestType !== undefined) {
      data.contestType = dto.contestType;
    }
    if (dto.startTime !== undefined) {
      data.startTime = new Date(dto.startTime);
    }
    if (dto.endTime !== undefined) {
      data.endTime = new Date(dto.endTime);
    }
    if (dto.address !== undefined) {
      data.address = dto.address;
    }
    if (dto.content !== undefined) {
      data.content = dto.content;
    }
    if (dto.contestTenants !== undefined) {
      data.contestTenants = dto.contestTenants
        ? JSON.stringify(dto.contestTenants)
        : null;
    }
    if (dto.coverUrl !== undefined) {
      data.coverUrl = dto.coverUrl;
    }
    if (dto.posterUrl !== undefined) {
      data.posterUrl = dto.posterUrl;
    }
    if (dto.contactName !== undefined) {
      data.contactName = dto.contactName;
    }
    if (dto.contactPhone !== undefined) {
      data.contactPhone = dto.contactPhone;
    }
    if (dto.contactQrcode !== undefined) {
      data.contactQrcode = dto.contactQrcode;
    }
    if (dto.organizers !== undefined) {
      data.organizers = dto.organizers ? [dto.organizers] : null;
    }
    if (dto.coOrganizers !== undefined) {
      data.coOrganizers = dto.coOrganizers ? [dto.coOrganizers] : null;
    }
    if (dto.sponsors !== undefined) {
      data.sponsors = dto.sponsors ? [dto.sponsors] : null;
    }
    // 报名配置
    if (dto.registerStartTime !== undefined) {
      data.registerStartTime = new Date(dto.registerStartTime);
    }
    if (dto.registerEndTime !== undefined) {
      data.registerEndTime = new Date(dto.registerEndTime);
    }
    if (dto.requireAudit !== undefined) {
      data.requireAudit = dto.requireAudit;
    }
    if (dto.allowedGrades !== undefined) {
      data.allowedGrades = dto.allowedGrades
        ? JSON.stringify(dto.allowedGrades)
        : null;
    }
    if (dto.allowedClasses !== undefined) {
      data.allowedClasses = dto.allowedClasses
        ? JSON.stringify(dto.allowedClasses)
        : null;
    }
    if (dto.teamMinMembers !== undefined) {
      data.teamMinMembers = dto.teamMinMembers;
    }
    if (dto.teamMaxMembers !== undefined) {
      data.teamMaxMembers = dto.teamMaxMembers;
    }
    // 作品配置
    if (dto.submitRule !== undefined) {
      data.submitRule = dto.submitRule;
    }
    if (dto.submitStartTime !== undefined) {
      data.submitStartTime = new Date(dto.submitStartTime);
    }
    if (dto.submitEndTime !== undefined) {
      data.submitEndTime = new Date(dto.submitEndTime);
    }
    if (dto.workType !== undefined) {
      data.workType = dto.workType;
    }
    if (dto.workRequirement !== undefined) {
      data.workRequirement = dto.workRequirement;
    }
    // 评审配置
    if (dto.reviewRuleId !== undefined) {
      data.reviewRuleId = dto.reviewRuleId || null;
    }
    if (dto.reviewStartTime !== undefined) {
      data.reviewStartTime = new Date(dto.reviewStartTime);
    }
    if (dto.reviewEndTime !== undefined) {
      data.reviewEndTime = new Date(dto.reviewEndTime);
    }
    if (dto.resultPublishTime !== undefined) {
      data.resultPublishTime = dto.resultPublishTime
        ? new Date(dto.resultPublishTime)
        : null;
    }
    if (dto.contestState !== undefined) {
      data.contestState = dto.contestState;
    }
    // 可见范围与定向推送
    if (dto.visibility !== undefined) {
      data.visibility = dto.visibility;
    }
    if (dto.targetCities !== undefined) {
      data.targetCities = dto.targetCities;
    }
    if (dto.ageMin !== undefined) {
      data.ageMin = dto.ageMin;
    }
    if (dto.ageMax !== undefined) {
      data.ageMax = dto.ageMax;
    }

    if (modifierId) {
      data.modifier = modifierId;
    }

    return this.prisma.contest.update({
      where: { id },
      data,
      include: {
        attachments: {
          where: { validState: 1 },
        },
        reviewRule: true,
        _count: {
          select: {
            registrations: true,
            works: true,
            teams: true,
            judges: true,
          },
        },
      },
    });
  }

  async publish(id: number, contestState: string, modifierId?: number) {
    const contest = await this.prisma.contest.findUnique({
      where: { id },
    });

    if (!contest) {
      throw new NotFoundException('活动不存在');
    }

    // 允许取消发布，即使有报名记录也可以（保留报名和作品数据）

    const data: any = {
      contestState,
    };

    if (modifierId) {
      data.modifier = modifierId;
    }

    return this.prisma.contest.update({
      where: { id },
      data,
      include: {
        attachments: {
          where: { validState: 1 },
        },
        reviewRule: true,
      },
    });
  }

  async remove(id: number) {
    const contest = await this.prisma.contest.findUnique({
      where: { id },
    });

    if (!contest) {
      throw new NotFoundException('活动不存在');
    }

    // 使用事务删除活动及相关数据
    // 注意：作品和报名记录保留（用户仍可在自己的数据中看到）
    return this.prisma.$transaction(async (tx) => {
      // 1. 删除团队成员记录
      await tx.contestTeamMember.deleteMany({
        where: {
          team: {
            contestId: id,
          },
        },
      });

      // 2. 删除团队记录
      await tx.contestTeam.deleteMany({
        where: { contestId: id },
      });

      // 3. 删除评委分配记录
      await tx.contestJudge.deleteMany({
        where: { contestId: id },
      });

      // 4. 删除公告
      await tx.contestNotice.deleteMany({
        where: { contestId: id },
      });

      // 5. 删除作品评分记录
      await tx.contestWorkScore.deleteMany({
        where: { contestId: id },
      });

      // 6. 删除作品评委分配记录
      await tx.contestWorkJudgeAssignment.deleteMany({
        where: { contestId: id },
      });

      // 7. 软删除活动（报名记录和作品保留，指向此软删除的活动）
      return tx.contest.update({
        where: { id },
        data: {
          validState: 2,
        },
      });
    });
  }

  /**
   * 标记活动为已完结
   */
  async finish(id: number, modifierId?: number) {
    const contest = await this.prisma.contest.findUnique({
      where: { id },
    });

    if (!contest) {
      throw new NotFoundException('活动不存在');
    }

    // 只有已发布的活动才能标记为完结
    if (contest.contestState !== 'published') {
      throw new BadRequestException('只有已发布的活动才能标记为完结');
    }

    // 如果已经完结，不能重复操作
    if (contest.status === 'finished') {
      throw new BadRequestException('活动已经完结');
    }

    const data: any = {
      status: 'finished',
    };

    if (modifierId) {
      data.modifier = modifierId;
    }

    return this.prisma.contest.update({
      where: { id },
      data,
      include: {
        attachments: {
          where: { validState: 1 },
        },
        reviewRule: true,
        _count: {
          select: {
            registrations: true,
            works: true,
            teams: true,
            judges: true,
          },
        },
      },
    });
  }

  /**
   * 重新开启已完结的活动
   */
  async reopen(id: number, modifierId?: number) {
    const contest = await this.prisma.contest.findUnique({
      where: { id },
    });

    if (!contest) {
      throw new NotFoundException('活动不存在');
    }

    if (contest.status !== 'finished') {
      throw new BadRequestException('只有已完结的活动才能重新开启');
    }

    const data: any = {
      status: 'ongoing',
    };

    if (modifierId) {
      data.modifier = modifierId;
    }

    return this.prisma.contest.update({
      where: { id },
      data,
      include: {
        attachments: {
          where: { validState: 1 },
        },
        reviewRule: true,
        _count: {
          select: {
            registrations: true,
            works: true,
            teams: true,
            judges: true,
          },
        },
      },
    });
  }
}
