package org.springblade.modules.martial.controller;

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.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.tool.api.R;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.martial.pojo.dto.MiniAthleteScoreDTO;
import org.springblade.modules.martial.pojo.dto.MiniLoginDTO;
import org.springblade.modules.martial.pojo.dto.MiniScoreModifyDTO;
import org.springblade.modules.martial.pojo.entity.*;
import org.springblade.modules.martial.pojo.vo.MiniAthleteAdminVO;
import org.springblade.modules.martial.pojo.vo.MiniAthleteScoreVO;
import org.springblade.modules.martial.pojo.vo.MiniLoginVO;
import org.springblade.modules.martial.pojo.vo.MiniScoreDetailVO;
import org.springblade.modules.martial.pojo.vo.LineupGroupVO;
import org.springblade.modules.martial.pojo.vo.LineupParticipantVO;
import org.springblade.modules.martial.pojo.vo.ScheduleGroupDetailVO;
import com.alibaba.fastjson.JSON;
import org.springblade.modules.martial.service.*;
import org.springblade.modules.martial.pojo.dto.ChiefJudgeConfirmDTO;
import org.springblade.modules.martial.pojo.dto.GeneralJudgeConfirmDTO;
import org.springblade.modules.martial.pojo.entity.MtVenue;
import org.springblade.modules.martial.pojo.entity.MartialVenue;
import org.springblade.modules.martial.pojo.entity.MartialResult;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.modules.martial.mapper.MartialScheduleStatusMapper;
import org.springblade.modules.martial.mapper.MartialScheduleGroupMapper;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.Map;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 小程序专用接口 控制器
 *
 * @author BladeX
 */
@RestController
@AllArgsConstructor
@RequestMapping("/mini")
@Tag(name = "小程序接口", description = "小程序评分系统专用接口")
public class MartialMiniController extends BladeController {

	private final IMartialJudgeInviteService judgeInviteService;
	private final IMartialJudgeService judgeService;
	private final IMartialCompetitionService competitionService;
	private final IMartialVenueService venueService;
	private final IMtVenueService mtVenueService;
	private final IMartialProjectService projectService;
	private final IMartialAthleteService athleteService;
	private final IMartialScoreService scoreService;
	private final BladeRedis bladeRedis;
	private final IMartialResultService resultService;
	private final IMiniAuthService miniAuthService;
	private final IMiniScoringService miniScoringService;
	private final IMartialRegistrationOrderService registrationOrderService;
	private final MartialScheduleStatusMapper scheduleStatusMapper;
	private final MartialScheduleGroupMapper scheduleGroupMapper;

	// Redis缓存key前缀
	private static final String MINI_LOGIN_CACHE_PREFIX = "mini:login:";
	// 登录缓存过期时间（7天）
	private static final Duration LOGIN_CACHE_EXPIRE = Duration.ofDays(7);

	/**
	 * 登录验证
	 */
	@PostMapping("/login")
	@Operation(summary = "登录验证", description = "使用比赛编码和邀请码登录")
	public R<MiniLoginVO> login(@RequestBody MiniLoginDTO dto) {
		return miniAuthService.login(dto);
	}
	@PostMapping("/score/submit")
	@Operation(summary = "提交评分", description = "裁判员提交评分")
	public R submitScore(@RequestBody org.springblade.modules.martial.pojo.dto.MiniScoreSubmitDTO dto) {
		return miniScoringService.submitScore(dto);
	}
	
	/**
	 * 计算并更新选手总分
	 * 总分算法：去掉一个最高分和一个最低分，取剩余分数的平均值
	 * 特殊情况：裁判数量<3时，直接取平均分
	 * 只有所有裁判都评分完成后才更新总分
	 */
	private void updateAthleteTotalScore(Long athleteId, Long projectId, Long venueId) {
		try {
			// 1. 查询该场地的裁判员数量
			int requiredJudgeCount = getRequiredJudgeCount(venueId);

			// 2. 获取主裁判ID列表
			List<Long> chiefJudgeIds = getChiefJudgeIds(venueId);

			// 3. 查询该选手在该项目的所有评分（排除主裁判的评分）
			LambdaQueryWrapper<MartialScore> scoreQuery = new LambdaQueryWrapper<>();
			scoreQuery.eq(MartialScore::getAthleteId, athleteId);
			scoreQuery.eq(MartialScore::getProjectId, projectId);
			scoreQuery.eq(MartialScore::getIsDeleted, 0);
			// 排除主裁判的所有评分（包括普通评分和修改记录）
			if (!chiefJudgeIds.isEmpty()) {
				scoreQuery.notIn(MartialScore::getJudgeId, chiefJudgeIds);
			}
			List<MartialScore> scores = scoreService.list(scoreQuery);

			// 4. 判断是否所有裁判都已评分
			if (scores == null || scores.isEmpty()) {
				return;
			}
			
			// 如果配置了裁判数量，检查是否评分完成
			if (requiredJudgeCount > 0 && scores.size() < requiredJudgeCount) {
				// 未完成评分，清空总分
				MartialAthlete athlete = athleteService.getById(athleteId);
				if (athlete != null && athlete.getTotalScore() != null) {
					athlete.setTotalScore(null);
					athleteService.updateById(athlete);
				}
				return;
			}
			
			// 4. 计算总分（去掉最高最低分取平均）
			BigDecimal totalScore = calculateTotalScore(scores);
			
			// 5. 更新选手总分
			if (totalScore != null) {
				MartialAthlete athlete = athleteService.getById(athleteId);
				if (athlete != null) {
					athlete.setTotalScore(totalScore);
					athleteService.updateById(athlete);
				}
			}
		} catch (Exception e) {
			// 记录错误但不影响评分提交
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取项目应评分的裁判数量（裁判员，不包括主裁判）
	 * 按项目过滤：检查 projects JSON 字段是否包含该项目ID
	 */
	private int getRequiredJudgeCount(Long venueId) {
		if (venueId == null || venueId <= 0) {
			return 0;
		}
		LambdaQueryWrapper<MartialJudgeInvite> judgeQuery = new LambdaQueryWrapper<>();
		judgeQuery.eq(MartialJudgeInvite::getIsDeleted, 0);
		judgeQuery.eq(MartialJudgeInvite::getVenueId, venueId);
		judgeQuery.eq(MartialJudgeInvite::getRefereeType, 2); // Only count referees (type=2), exclude chief judge (type=1) and general judge (type=3)
		List<MartialJudgeInvite> judges = judgeInviteService.list(judgeQuery);
		// Use distinct judge_id to count unique judges
		return (int) judges.stream()
			.map(MartialJudgeInvite::getJudgeId)
			.filter(Objects::nonNull)
			.distinct()
			.count();
	}
	
	/**
	 * 计算总分
	 * 算法：去掉一个最高分和一个最低分，取剩余分数的平均值
	 * 特殊情况：裁判数量<3时，直接取平均分
	 */
	private BigDecimal calculateTotalScore(List<MartialScore> scores) {
		if (scores == null || scores.isEmpty()) {
			return null;
		}
		
		// 提取所有分数并排序
		List<BigDecimal> scoreValues = scores.stream()
			.map(MartialScore::getScore)
			.filter(Objects::nonNull)
			.sorted()
			.collect(Collectors.toList());
		
		int count = scoreValues.size();
		if (count == 0) {
			return null;
		}
		
		if (count < 3) {
			// 裁判数量<3，直接取平均分
			BigDecimal sum = scoreValues.stream()
				.reduce(BigDecimal.ZERO, BigDecimal::add);
			return sum.divide(new BigDecimal(count), 3, RoundingMode.HALF_UP);
		}
		
		// 去掉最高分和最低分（已排序，去掉第一个和最后一个）
		List<BigDecimal> middleScores = scoreValues.subList(1, count - 1);
		
		// 计算平均分
		BigDecimal sum = middleScores.stream()
			.reduce(BigDecimal.ZERO, BigDecimal::add);
		return sum.divide(new BigDecimal(middleScores.size()), 3, RoundingMode.HALF_UP);
	}

	/**
	 * 安全地将String转换为Long
	 */
	private Long parseLong(String value) {
		if (value == null || value.trim().isEmpty()) {
			return null;
		}
		try {
			return Long.parseLong(value.trim());
		} catch (NumberFormatException e) {
			return null;
		}
	}

	/**
	 * 获取选手列表（支持分页）
	 * - 裁判员：获取所有选手，标记是否已评分
	 * - 主裁判：获取所有裁判员都评分完成的选手列表
	 */
	@GetMapping("/score/athletes")
	@Operation(summary = "获取选手列表", description = "根据裁判类型获取选手列表（支持分页）")
	public R<IPage<org.springblade.modules.martial.pojo.vo.MiniAthleteListVO>> getAthletes(
		@RequestParam Long judgeId,
		@RequestParam Integer refereeType,
		@RequestParam(required = false) Long projectId,
		@RequestParam(required = false) Long orderId,
		@RequestParam(required = false) Long venueId,
		@RequestParam(required = false) Long competitionId,
		@RequestParam(defaultValue = "1") Integer current,
		@RequestParam(defaultValue = "10") Integer size
	) {
		// 1. 构建选手查询条件
		LambdaQueryWrapper<MartialAthlete> athleteQuery = new LambdaQueryWrapper<>();
		athleteQuery.eq(MartialAthlete::getIsDeleted, 0);

		// 按比赛ID过滤（重要：确保只显示当前比赛的选手）
		if (competitionId != null) {
			athleteQuery.eq(MartialAthlete::getCompetitionId, competitionId);
		}

		if (projectId != null) {
			athleteQuery.eq(MartialAthlete::getProjectId, projectId);
		}

		athleteQuery.orderByAsc(MartialAthlete::getOrderNum);

		List<MartialAthlete> athletes = athleteService.list(athleteQuery);

		// 2. 获取该场地所有主裁判的judge_id列表
		List<Long> chiefJudgeIds = getChiefJudgeIds(venueId);

		// 3. 获取所有评分记录（排除主裁判的评分）
		LambdaQueryWrapper<MartialScore> scoreQuery = new LambdaQueryWrapper<>();
		scoreQuery.eq(MartialScore::getIsDeleted, 0);
		if (projectId != null) {
			scoreQuery.eq(MartialScore::getProjectId, projectId);
		}
		// 添加场地过滤
		if (venueId != null && venueId > 0) {
			scoreQuery.eq(MartialScore::getVenueId, venueId);
		}
		// 排除主裁判的评分
		if (!chiefJudgeIds.isEmpty()) {
			scoreQuery.notIn(MartialScore::getJudgeId, chiefJudgeIds);
		}
		List<MartialScore> allScores = scoreService.list(scoreQuery);

		// 按选手ID分组统计评分
		java.util.Map<Long, List<MartialScore>> scoresByAthlete = allScores.stream()
			.collect(java.util.stream.Collectors.groupingBy(MartialScore::getAthleteId));

		// 4. 获取该场地的应评裁判数量
		int requiredJudgeCount = getRequiredJudgeCount(venueId);

		// 5. 根据裁判类型处理选手列表
		List<org.springblade.modules.martial.pojo.vo.MiniAthleteListVO> filteredList;

		if (refereeType == 1) {
			// 主裁判：返回所有选手，前端根据totalScore判断是否显示修改按钮
			filteredList = athletes.stream()
				.map(athlete -> convertToAthleteListVO(athlete, scoresByAthlete.get(athlete.getId()), judgeId, requiredJudgeCount))
				.collect(java.util.stream.Collectors.toList());
		} else {
			// 裁判员：返回所有选手，标记是否已评分
			filteredList = athletes.stream()
				.map(athlete -> convertToAthleteListVO(athlete, scoresByAthlete.get(athlete.getId()), judgeId, requiredJudgeCount))
				.collect(java.util.stream.Collectors.toList());
		}

		// 6. 手动分页
		int total = filteredList.size();
		int fromIndex = (current - 1) * size;
		int toIndex = Math.min(fromIndex + size, total);

		List<org.springblade.modules.martial.pojo.vo.MiniAthleteListVO> pageRecords;
		if (fromIndex >= total) {
			pageRecords = new ArrayList<>();
		} else {
			pageRecords = filteredList.subList(fromIndex, toIndex);
		}

		// 7. 构建分页结果
		IPage<org.springblade.modules.martial.pojo.vo.MiniAthleteListVO> page = new Page<>(current, size, total);
		page.setRecords(pageRecords);

		return R.data(page);
	}

	/**
	 * 获取场地所有主裁判的judge_id列表
	 */
	private List<Long> getChiefJudgeIds(Long venueId) {
		if (venueId == null) {
			return new ArrayList<>();
		}
		LambdaQueryWrapper<MartialJudgeInvite> judgeQuery = new LambdaQueryWrapper<>();
		judgeQuery.eq(MartialJudgeInvite::getVenueId, venueId);
		judgeQuery.eq(MartialJudgeInvite::getIsDeleted, 0);
		judgeQuery.eq(MartialJudgeInvite::getRole, "chief_judge");
		List<MartialJudgeInvite> chiefJudges = judgeInviteService.list(judgeQuery);
		return chiefJudges.stream()
			.map(MartialJudgeInvite::getJudgeId)
			.filter(Objects::nonNull)
			.collect(Collectors.toList());
	}

	/**
	 * 获取评分详情
	 */
	@GetMapping("/score/detail/{athleteId}")
	@Operation(summary = "评分详情", description = "查看选手的所有评委评分")
	public R<MiniScoreDetailVO> getScoreDetail(@PathVariable Long athleteId) {
		MiniScoreDetailVO detail = scoreService.getScoreDetailForMini(athleteId);
		return R.data(detail);
	}

	/**
	 * 修改评分（主裁判）
	 */
	@PutMapping("/score/modify")
	@Operation(summary = "修改评分", description = "主裁判修改选手总分")
	public R modifyScore(@RequestBody MiniScoreModifyDTO dto) {
		boolean success = scoreService.modifyScoreByAdmin(dto);
		return success ? R.success("修改成功") : R.fail("修改失败");
	}

	/**
	 * 退出登录
	 */
	@PostMapping("/logout")
	@Operation(summary = "退出登录", description = "清除登录状态")
	public R logout(@RequestHeader(value = "Authorization", required = false) String token) {
		// 从Redis删除登录缓存
		if (token != null && !token.isEmpty()) {
			String cacheKey = MINI_LOGIN_CACHE_PREFIX + token;
			bladeRedis.del(cacheKey);
		}
		return R.success("退出成功");
	}

	/**
	 * Token验证（从Redis恢复登录状态）
	 */
	@GetMapping("/verify")
	@Operation(summary = "Token验证", description = "验证token并返回登录信息，支持服务重启后恢复登录状态")
	public R<MiniLoginVO> verify(@RequestHeader(value = "Authorization", required = false) String token) {
		if (token == null || token.isEmpty()) {
			return R.fail("Token不能为空");
		}

		// 从Redis获取登录信息
		String cacheKey = MINI_LOGIN_CACHE_PREFIX + token;
		MiniLoginVO loginInfo = bladeRedis.get(cacheKey);

		if (loginInfo != null) {
			// 刷新缓存过期时间
			bladeRedis.setEx(cacheKey, loginInfo, LOGIN_CACHE_EXPIRE);
			return R.data(loginInfo);
		}

		// Redis中没有，尝试从数据库恢复
		LambdaQueryWrapper<MartialJudgeInvite> inviteQuery = new LambdaQueryWrapper<>();
		inviteQuery.eq(MartialJudgeInvite::getAccessToken, token);
		inviteQuery.eq(MartialJudgeInvite::getIsDeleted, 0);
		MartialJudgeInvite invite = judgeInviteService.getOne(inviteQuery);

		if (invite == null) {
			return R.fail("Token无效");
		}

		if (invite.getTokenExpireTime() != null && invite.getTokenExpireTime().isBefore(LocalDateTime.now())) {
			return R.fail("Token已过期");
		}

		// 重建登录信息
		MartialCompetition competition = competitionService.getById(invite.getCompetitionId());
		MartialJudge judge = judgeService.getById(invite.getJudgeId());
		MartialVenue martialVenue = invite.getVenueId() != null ? venueService.getById(invite.getVenueId()) : null;
		// 获取项目列表：总裁判看所有项目，其他裁判根据场地获取项目
		List<MiniLoginVO.ProjectInfo> projects = new ArrayList<>();
		Integer refereeTypeVal = invite.getRefereeType();
		String roleVal = invite.getRole();
		boolean isGeneralJudge = (refereeTypeVal != null && refereeTypeVal == 3) 
			|| "general_judge".equals(roleVal) || "general".equals(roleVal);
		
		if (isGeneralJudge) {
			// 总裁判看所有项目
			projects = getAllProjectsByCompetition(competition.getId());
		} else if (Func.isNotEmpty(invite.getProjects())) {
			projects = parseProjects(invite.getProjects());
		} else if (invite.getVenueId() != null) {
			// 未指定项目，根据场地获取项目；如果场地没有项目则返回空列表
			projects = getProjectsByVenue(invite.getVenueId());
		}
		// 如果没有场地，projects保持为空列表

		MiniLoginVO vo = new MiniLoginVO();
		vo.setToken(token);
		String role = invite.getRole();
		Integer refereeType = invite.getRefereeType();
		if ("general_judge".equals(role) || "general".equals(role) || (refereeType != null && refereeType == 3)) {
			vo.setUserRole("general");
		} else if ("chief_judge".equals(role) || (refereeType != null && refereeType == 1)) {
			vo.setUserRole("admin");
		} else {
			vo.setUserRole("pub");
		}
		vo.setMatchId(competition != null ? competition.getId() : null);
		vo.setMatchName(competition != null ? competition.getCompetitionName() : null);
		vo.setMatchTime(competition != null && competition.getCompetitionStartTime() != null ?
			competition.getCompetitionStartTime().toString() : "");
		vo.setJudgeId(judge != null ? judge.getId() : null);
		vo.setJudgeName(judge != null ? judge.getName() : null);
		vo.setVenueId(martialVenue != null ? martialVenue.getId() : null);
		vo.setVenueName(martialVenue != null ? martialVenue.getVenueName() : null);
		vo.setProjects(projects);

		// 重新缓存到Redis
		bladeRedis.setEx(cacheKey, vo, LOGIN_CACHE_EXPIRE);

		return R.data(vo);
	}

	/**
	 * 转换选手实体为VO
	 * 新增：只有评分完成时才显示总分
	 */
	private org.springblade.modules.martial.pojo.vo.MiniAthleteListVO convertToAthleteListVO(
			MartialAthlete athlete, 
			List<MartialScore> scores, 
			Long currentJudgeId,
			int requiredJudgeCount) {
		org.springblade.modules.martial.pojo.vo.MiniAthleteListVO vo = new org.springblade.modules.martial.pojo.vo.MiniAthleteListVO();
		vo.setAthleteId(athlete.getId());
		vo.setName(athlete.getPlayerName());
		vo.setIdCard(athlete.getIdCard());
		vo.setNumber(athlete.getPlayerNo());
		vo.setTeam(athlete.getTeamName());
		vo.setOrderNum(athlete.getOrderNum());
		vo.setCompetitionStatus(athlete.getCompetitionStatus());
		
		// 设置应评分裁判数量
		vo.setRequiredJudgeCount(requiredJudgeCount);

		// 设置项目名称
		if (athlete.getProjectId() != null) {
			MartialProject project = projectService.getById(athlete.getProjectId());
			if (project != null) {
				vo.setProjectName(project.getProjectName());
			}
		}

		// 设置评分状态
		int scoredCount = 0;
		if (scores != null && !scores.isEmpty()) {
			scoredCount = scores.size();
			vo.setScoredJudgeCount(scoredCount);
			
			// 查找当前裁判的评分
			MartialScore myScore = scores.stream()
				.filter(s -> s.getJudgeId().equals(currentJudgeId))
				.findFirst()
				.orElse(null);
			
			if (myScore != null) {
				vo.setScored(true);
				vo.setMyScore(myScore.getScore());
			} else {
				vo.setScored(false);
			}
		} else {
			vo.setScored(false);
			vo.setScoredJudgeCount(0);
		}
		
		// 判断评分是否完成（所有裁判都已评分）
		boolean scoringComplete = false;
		if (requiredJudgeCount > 0) {
			scoringComplete = scoredCount >= requiredJudgeCount;
		} else {
			// 如果没有配置裁判数量，只要有评分就算完成
			scoringComplete = scoredCount > 0;
		}
		vo.setScoringComplete(scoringComplete);
		
		// 只有评分完成时才显示总分
		if (scoringComplete) {
			vo.setTotalScore(athlete.getTotalScore());
		} else {
			vo.setTotalScore(null);
		}

		return vo;
	}

	/**
	 * 解析项目JSON字符串
	 */
	private List<MiniLoginVO.ProjectInfo> parseProjects(String projectsJson) {
		List<MiniLoginVO.ProjectInfo> projects = new ArrayList<>();

		if (Func.isEmpty(projectsJson)) {
			return projects;
		}

		try {
			ObjectMapper mapper = new ObjectMapper();
			List<Long> projectIds = mapper.readValue(projectsJson, new TypeReference<List<Long>>() {});

			if (Func.isNotEmpty(projectIds)) {
				List<MartialProject> projectList = projectService.listByIds(projectIds);
				projects = projectList.stream().map(project -> {
					MiniLoginVO.ProjectInfo info = new MiniLoginVO.ProjectInfo();
					info.setProjectId(project.getId());
					info.setProjectName(project.getProjectName());
					return info;
				}).collect(Collectors.toList());
			}
		} catch (Exception e) {
			try {
				String[] ids = projectsJson.split(",");
				List<Long> projectIds = new ArrayList<>();
				for (String id : ids) {
					projectIds.add(Long.parseLong(id.trim()));
				}

				if (Func.isNotEmpty(projectIds)) {
					List<MartialProject> projectList = projectService.listByIds(projectIds);
					projects = projectList.stream().map(project -> {
						MiniLoginVO.ProjectInfo info = new MiniLoginVO.ProjectInfo();
						info.setProjectId(project.getId());
						info.setProjectName(project.getProjectName());
						return info;
					}).collect(Collectors.toList());
				}
			} catch (Exception ex) {
				// 解析失败，返回空列表
			}
		}

		return projects;
	}


	/**
	 * 获取比赛的所有项目
	 */
	private List<MiniLoginVO.ProjectInfo> getAllProjectsByCompetition(Long competitionId) {
		List<MiniLoginVO.ProjectInfo> projects = new ArrayList<>();
		
		LambdaQueryWrapper<MartialProject> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(MartialProject::getCompetitionId, competitionId);
		wrapper.eq(MartialProject::getIsDeleted, 0);
		
		List<MartialProject> projectList = projectService.list(wrapper);
		
		if (Func.isNotEmpty(projectList)) {
			projects = projectList.stream().map(project -> {
				MiniLoginVO.ProjectInfo info = new MiniLoginVO.ProjectInfo();
				info.setProjectId(project.getId());
				info.setProjectName(project.getProjectName());
				return info;
			}).collect(Collectors.toList());
		}
		
		return projects;
	}

	/**
	 * 根据场地获取项目列表
	 */
	private List<MiniLoginVO.ProjectInfo> getProjectsByVenue(Long venueId) {
		List<MiniLoginVO.ProjectInfo> projects = new ArrayList<>();
		
		LambdaQueryWrapper<MartialProject> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(MartialProject::getVenueId, venueId);
		wrapper.eq(MartialProject::getIsDeleted, 0);
		
		List<MartialProject> projectList = projectService.list(wrapper);
		
		if (Func.isNotEmpty(projectList)) {
			projects = projectList.stream().map(project -> {
				MiniLoginVO.ProjectInfo info = new MiniLoginVO.ProjectInfo();
				info.setProjectId(project.getId());
				info.setProjectName(project.getProjectName());
				return info;
			}).collect(Collectors.toList());
		}
		
		return projects;
	}


	// ========== 三级裁判评分流程 API ==========

	/**
	 * 主裁判确认/修改分数
	 */
	@PostMapping("/chief/confirm")
	@Operation(summary = "主裁判确认分数", description = "主裁判确认或修改选手分数")
	public R confirmByChiefJudge(@RequestBody ChiefJudgeConfirmDTO dto) {
		Long resultId = parseLong(dto.getResultId());
		Long chiefJudgeId = parseLong(dto.getChiefJudgeId());
		if (resultId == null || chiefJudgeId == null) {
			return R.fail("参数错误");
		}
		boolean success = resultService.confirmByChiefJudge(resultId, chiefJudgeId, dto.getScore(), dto.getNote());
		return success ? R.success("确认成功") : R.fail("确认失败");
	}

	/**
	 * 总裁确认/修改分数
	 */
	@PostMapping("/general/confirm")
	@Operation(summary = "总裁确认分数", description = "总裁确认或修改选手分数")
	public R confirmByGeneralJudge(@RequestBody GeneralJudgeConfirmDTO dto) {
		Long resultId = parseLong(dto.getResultId());
		Long generalJudgeId = parseLong(dto.getGeneralJudgeId());
		if (resultId == null || generalJudgeId == null) {
			return R.fail("参数错误");
		}
		boolean success = resultService.confirmByGeneralJudge(resultId, generalJudgeId, dto.getScore(), dto.getNote());
		return success ? R.success("确认成功") : R.fail("确认失败");
	}

	/**
	 * 获取待主裁判确认的成绩列表
	 */
	@GetMapping("/chief/pending")
	@Operation(summary = "待主裁判确认列表", description = "获取待主裁判确认的成绩列表")
	public R<List<MartialResult>> getPendingChiefConfirmList(@RequestParam Long venueId) {
		List<MartialResult> list = resultService.getPendingChiefConfirmList(venueId);
		return R.data(list);
	}

	/**
	 * 获取待总裁确认的成绩列表
	 */
	@GetMapping("/general/pending")
	@Operation(summary = "待总裁确认列表", description = "获取待总裁确认的成绩列表（所有场地）")
	public R<List<MartialResult>> getPendingGeneralConfirmList(@RequestParam Long competitionId) {
		List<MartialResult> list = resultService.getPendingGeneralConfirmList(competitionId);
		return R.data(list);
	}

	/**
	 * 获取所有场地列表（总裁用）
	 */
	@GetMapping("/general/venues")
	@Operation(summary = "获取所有场地", description = "总裁获取比赛的所有场地列表")
	public R<List<MartialVenue>> getAllVenues(@RequestParam Long competitionId) {
		LambdaQueryWrapper<MartialVenue> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(MartialVenue::getCompetitionId, competitionId);
		wrapper.eq(MartialVenue::getIsDeleted, 0);
		wrapper.orderByAsc(MartialVenue::getVenueName);
		List<MartialVenue> venues = venueService.list(wrapper);
		return R.data(venues);
	}

	/**
	 * 获取已总裁确认的成绩列表
	 */
	@GetMapping("/general/confirmed")
	@Operation(summary = "已总裁确认列表", description = "获取已总裁确认的成绩列表")
	public R<List<MartialResult>> getConfirmedGeneralList(@RequestParam Long competitionId) {
		List<MartialResult> list = resultService.getConfirmedGeneralList(competitionId);
		return R.data(list);
	}

	// ========== 出场顺序相关 API ==========

	/**
	 * 获取编排状态
	 */
	@GetMapping("/schedule/status")
	@Operation(summary = "获取编排状态", description = "检查赛事编排是否完成")
	public R<Map<String, Object>> getScheduleStatus(@RequestParam Long competitionId) {
		Map<String, Object> result = new HashMap<>();

		LambdaQueryWrapper<MartialScheduleStatus> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(MartialScheduleStatus::getCompetitionId, competitionId);
		wrapper.eq(MartialScheduleStatus::getIsDeleted, 0);
		wrapper.last("LIMIT 1");
		MartialScheduleStatus status = scheduleStatusMapper.selectOne(wrapper);

		if (status == null) {
			result.put("isCompleted", false);
			result.put("scheduleStatus", 0);
			result.put("statusText", "未编排");
			return R.data(result);
		}

		boolean isCompleted = status.getScheduleStatus() != null && status.getScheduleStatus() == 2;
		result.put("isCompleted", isCompleted);
		result.put("scheduleStatus", status.getScheduleStatus());
		result.put("statusText", getScheduleStatusText(status.getScheduleStatus()));
		result.put("totalGroups", status.getTotalGroups());
		result.put("totalParticipants", status.getTotalParticipants());
		result.put("lockedTime", status.getLockedTime());

		return R.data(result);
	}

	private String getScheduleStatusText(Integer status) {
		if (status == null) return "未编排";
		switch (status) {
			case 0: return "未编排";
			case 1: return "编排中";
			case 2: return "已锁定";
			default: return "未知";
		}
	}

	/**
	 * 获取出场顺序
	/**
	 * 获取出场顺序（只返回当前用户报名的项目）
	 */
	@GetMapping("/schedule/lineup")
	@Operation(summary = "获取出场顺序", description = "获取当前用户报名项目的出场顺序")
	public R<Map<String, Object>> getLineup(
		@RequestParam Long competitionId,
		@RequestParam(required = false) Long projectId,
		@RequestParam(required = false) Long orderId
	) {
		Map<String, Object> result = new HashMap<>();

		// 获取当前登录用户
		BladeUser user = AuthUtil.getUser();
		System.out.println("user: " + (user != null ? user.getUserId() : "null"));
		if (user == null) {
			result.put("groups", new ArrayList<>());
			return R.data(result);
		}

		// 查询当前用户在该赛事的订单
		LambdaQueryWrapper<MartialRegistrationOrder> orderWrapper = new LambdaQueryWrapper<>();
		orderWrapper.eq(MartialRegistrationOrder::getCompetitionId, competitionId)
			.eq(orderId != null, MartialRegistrationOrder::getId, orderId)
			.eq(MartialRegistrationOrder::getUserId, user.getUserId())
			.eq(MartialRegistrationOrder::getIsDeleted, 0);
		List<MartialRegistrationOrder> myOrders = registrationOrderService.list(orderWrapper);

		System.out.println("myOrders count: " + myOrders.size());
		if (myOrders.isEmpty()) {
			result.put("groups", new ArrayList<>());
			return R.data(result);
		}

		// 获取用户的所有订单ID
		Set<Long> myOrderIds = myOrders.stream()
			.map(MartialRegistrationOrder::getId)
			.collect(Collectors.toSet());

		// 查询当前用户在该赛事的运动员记录（通过订单ID关联）
		LambdaQueryWrapper<MartialAthlete> athleteWrapper = new LambdaQueryWrapper<>();
		athleteWrapper.eq(MartialAthlete::getCompetitionId, competitionId)
			.in(MartialAthlete::getOrderId, myOrderIds)
			.eq(MartialAthlete::getIsDeleted, 0);
		List<MartialAthlete> myAthletes = athleteService.list(athleteWrapper);

		if (myAthletes.isEmpty()) {
			result.put("groups", new ArrayList<>());
			return R.data(result);
		}

		// 获取用户的所有athlete ID
		System.out.println("myAthletes count: " + myAthletes.size());
		System.out.println("myAthletes: " + myAthletes.stream().map(a -> a.getId() + ":" + a.getPlayerName()).collect(Collectors.joining(", ")));
		Set<Long> myAthleteIds = myAthletes.stream()
			.map(MartialAthlete::getId)
			.collect(Collectors.toSet());

		// 使用现有mapper查询编排详情
		List<ScheduleGroupDetailVO> details = scheduleGroupMapper.selectScheduleGroupDetails(competitionId);
		// 查询已完成评分的选手ID（主裁判已确认）
		LambdaQueryWrapper<MartialResult> resultWrapper = new LambdaQueryWrapper<>();
		resultWrapper.eq(MartialResult::getCompetitionId, competitionId)
			.eq(MartialResult::getScoreStatus, 1) // 主裁判已确认
			.eq(MartialResult::getIsDeleted, 0);
		List<MartialResult> completedResults = resultService.list(resultWrapper);
		Set<Long> completedAthleteIds = completedResults.stream()
			.map(MartialResult::getAthleteId)
			.collect(Collectors.toSet());
		System.out.println("details count: " + (details != null ? details.size() : 0));
		if (details != null && !details.isEmpty()) {
			System.out.println("first detail participantId: " + details.get(0).getParticipantId());
		}

		if (details == null || details.isEmpty()) {
			result.put("groups", new ArrayList<>());
			return R.data(result);
		}

		// 过滤出包含当前用户的分组
		Set<Long> myGroupIds = details.stream()
			.filter(d -> d.getParticipantId() != null && myAthleteIds.contains(d.getParticipantId()))
			.map(ScheduleGroupDetailVO::getGroupId)
			.collect(Collectors.toSet());

		// 只保留用户参与的分组数据
		details = details.stream()
			.filter(d -> myGroupIds.contains(d.getGroupId()))
			.collect(Collectors.toList());

		// 转换为LineupGroupVO格式
		Map<Long, LineupGroupVO> groupMap = new LinkedHashMap<>();
		for (ScheduleGroupDetailVO detail : details) {
			Long groupId = detail.getGroupId();
			LineupGroupVO group = groupMap.get(groupId);
			if (group == null) {
				group = new LineupGroupVO();
				group.setGroupId(groupId);
				group.setGroupName(detail.getGroupName());
				group.setCategory(detail.getCategory());
				group.setVenueName(detail.getVenueName());
				group.setTimeSlot(detail.getTimeSlot());
				group.setTableNo(generateTableNo(detail));
				group.setParticipants(new ArrayList<>());
				groupMap.put(groupId, group);
			}

			// 添加参赛者
			if (detail.getParticipantId() != null) {
				LineupParticipantVO participant = new LineupParticipantVO();
				participant.setId(detail.getParticipantId());
				participant.setOrder(detail.getPerformanceOrder() != null ? detail.getPerformanceOrder() : group.getParticipants().size() + 1);
				participant.setPlayerName(detail.getPlayerName());
				participant.setOrganization(detail.getOrganization());
				participant.setIsMe(myAthleteIds.contains(detail.getParticipantId()));
				// 根据评分状态判断：已完成 > 进行中 > 待出场
				if (completedAthleteIds.contains(detail.getParticipantId())) {
					participant.setStatus("已完成");
				} else {
					participant.setStatus("待出场");
				}
				// 计算预计出场时间
				String baseTime = detail.getTimeSlot() != null ? detail.getTimeSlot() : "08:00";
				int orderNum = participant.getOrder() != null ? participant.getOrder() : 1;
				int minutesOffset = (orderNum - 1) * 5; // 每人5分钟
				try {
					String[] parts = baseTime.split(":");
					int hours = Integer.parseInt(parts[0]);
					int minutes = Integer.parseInt(parts[1]) + minutesOffset;
					hours += minutes / 60;
					minutes = minutes % 60;
					participant.setEstimatedTime(String.format("%02d:%02d", hours, minutes));
				} catch (Exception e) {
					participant.setEstimatedTime(baseTime);
				}
				group.getParticipants().add(participant);
			}
		}

		result.put("groups", new ArrayList<>(groupMap.values()));
		return R.data(result);
	}

	private String generateTableNo(ScheduleGroupDetailVO detail) {
		// 场地编号（简单取第一个数字或默认1）
		int venueNo = 1;
		if (detail.getVenueName() != null) {
			String venueName = detail.getVenueName();
			for (char c : venueName.toCharArray()) {
				if (Character.isDigit(c)) {
					venueNo = Character.getNumericValue(c);
					break;
				}
			}
		}

		// 时段：上午=1, 下午=2
		int period = 1;
		if (detail.getTimeSlot() != null) {
			try {
				int hour = Integer.parseInt(detail.getTimeSlot().split(":")[0]);
				period = hour < 12 ? 1 : 2;
			} catch (Exception e) {
				// ignore
			}
		}

		// 序号：使用displayOrder或默认1
		int orderNo = detail.getDisplayOrder() != null ? detail.getDisplayOrder() : 1;

		return String.format("%d%d%02d", venueNo, period, orderNo);
	}

}
