package org.springblade.modules.martial.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.modules.martial.mapper.*;
import org.springblade.modules.martial.pojo.dto.*;
import org.springblade.modules.martial.pojo.entity.*;
import org.springblade.modules.martial.pojo.vo.ScheduleGroupDetailVO;
import org.springblade.modules.martial.service.*;
import org.springframework.stereotype.Service;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ScheduleQueryServiceImpl implements IScheduleQueryService {

    private final MartialScheduleGroupMapper scheduleGroupMapper;
    private final MartialAthleteMapper athleteMapper;
    private final MartialTeamMapper teamMapper;
    private final MartialTeamMemberMapper teamMemberMapper;
    private final IMartialProjectService projectService;
    private final IMartialAthleteService athleteService;
    private final IMartialVenueService venueService;

	@Cacheable(value = "scheduleResult", key = "#competitionId", unless = "#result == null")
    @Override
	public ScheduleResultDTO getScheduleResult(Long competitionId) {
		log.info("Querying schedule result for competition: {}", competitionId);
		ScheduleResultDTO result = new ScheduleResultDTO();

		// 使用优化的一次性JOIN查询获取所有数据
		List<ScheduleGroupDetailVO> details = scheduleGroupMapper.selectScheduleGroupDetails(competitionId);

		if (details.isEmpty()) {
			// 没有编排数据时，从项目和选手表生成初始分组
			return generateInitialScheduleResult(competitionId);
		}

		// 按分组ID分组数据
		Map<Long, List<ScheduleGroupDetailVO>> groupMap = details.stream()
			.collect(Collectors.groupingBy(ScheduleGroupDetailVO::getGroupId));

		// 检查编排状态
		boolean isCompleted = details.stream()
			.anyMatch(d -> "completed".equals(d.getScheduleStatus()));
		boolean isDraft = !isCompleted;

		result.setIsDraft(isDraft);
		result.setIsCompleted(isCompleted);

		// 组装数据
		List<CompetitionGroupDTO> groupDTOs = new ArrayList<>();
		for (Map.Entry<Long, List<ScheduleGroupDetailVO>> entry : groupMap.entrySet()) {
			List<ScheduleGroupDetailVO> groupDetails = entry.getValue();
			if (groupDetails.isEmpty()) {
				continue;
			}

			// 获取第一条记录作为分组信息
			ScheduleGroupDetailVO firstDetail = groupDetails.get(0);

			CompetitionGroupDTO groupDTO = new CompetitionGroupDTO();
			groupDTO.setId(firstDetail.getGroupId());
			groupDTO.setTitle(firstDetail.getGroupName());
			groupDTO.setCode(firstDetail.getCategory());

			// 设置类型
			if (firstDetail.getProjectType() != null) {
				switch (firstDetail.getProjectType()) {
					case 1:
						groupDTO.setType("单人");
						break;
					case 2:
						groupDTO.setType("集体");
						break;
					default:
						groupDTO.setType("其他");
						break;
				}
			}

			// 设置队伍数量
			if (firstDetail.getTotalTeams() != null && firstDetail.getTotalTeams() > 0) {
				groupDTO.setCount(firstDetail.getTotalTeams() + "队");
			} else if (firstDetail.getTotalParticipants() != null) {
				groupDTO.setCount(firstDetail.getTotalParticipants() + "人");
			}

			// 设置场地和时间段信息
			groupDTO.setVenueId(firstDetail.getVenueId());
			groupDTO.setVenueName(firstDetail.getVenueName());
			groupDTO.setTimeSlot(firstDetail.getTimeSlot());
			groupDTO.setTimeSlotIndex(firstDetail.getTimeSlotIndex() != null ? firstDetail.getTimeSlotIndex() : 0);
			groupDTO.setEstimatedDuration(firstDetail.getEstimatedDuration());

			// 获取参赛者列表
			boolean isCollective = firstDetail.getProjectType() != null && firstDetail.getProjectType() == 2;
			List<ParticipantDTO> participantDTOs = new ArrayList<>();
			// Batch query optimization: collect all participant IDs first
			List<Long> participantIds = groupDetails.stream()
				.map(ScheduleGroupDetailVO::getParticipantId)
				.filter(id -> id != null)
				.collect(Collectors.toList());
			
			// Batch query all athletes
			Map<Long, MartialAthlete> athleteMap = new HashMap<>();
			if (!participantIds.isEmpty()) {
				List<MartialAthlete> athletes = athleteMapper.selectBatchIds(participantIds);
				athleteMap = athletes.stream().collect(Collectors.toMap(MartialAthlete::getId, a -> a));
			}
			
			// Batch query teams and members for collective projects
			Map<Long, List<Map<String, Object>>> teamMembersCache = new HashMap<>();
			if (isCollective && !athleteMap.isEmpty()) {
				// Collect all team names
				Set<String> teamNames = athleteMap.values().stream()
					.map(MartialAthlete::getTeamName)
					.filter(name -> name != null)
					.collect(Collectors.toSet());
				
				if (!teamNames.isEmpty()) {
					// Batch query all teams
					QueryWrapper<MartialTeam> teamWrapper = new QueryWrapper<>();
					teamWrapper.in("team_name", teamNames);
					teamWrapper.eq("is_deleted", 0);
					List<MartialTeam> teams = teamMapper.selectList(teamWrapper);
					
					if (!teams.isEmpty()) {
						// Batch query all team members
						List<Long> teamIds = teams.stream().map(MartialTeam::getId).collect(Collectors.toList());
						QueryWrapper<MartialTeamMember> memberWrapper = new QueryWrapper<>();
						memberWrapper.in("team_id", teamIds);
						memberWrapper.eq("is_deleted", 0);
						List<MartialTeamMember> allMembers = teamMemberMapper.selectList(memberWrapper);
						
						// Batch query member athletes
						List<Long> memberAthleteIds = allMembers.stream()
							.map(MartialTeamMember::getAthleteId)
							.collect(Collectors.toList());
						Map<Long, MartialAthlete> memberAthleteMap = new HashMap<>();
						if (!memberAthleteIds.isEmpty()) {
							List<MartialAthlete> memberAthletes = athleteMapper.selectBatchIds(memberAthleteIds);
							memberAthleteMap = memberAthletes.stream()
								.collect(Collectors.toMap(MartialAthlete::getId, a -> a));
						}
						
						// Build team members cache
						Map<String, Long> teamNameToId = teams.stream()
							.collect(Collectors.toMap(MartialTeam::getTeamName, MartialTeam::getId));
						Map<Long, List<MartialTeamMember>> teamMembersMap = allMembers.stream()
							.collect(Collectors.groupingBy(MartialTeamMember::getTeamId));
						
						for (MartialAthlete athlete : athleteMap.values()) {
							if (athlete.getTeamName() != null) {
								Long teamId = teamNameToId.get(athlete.getTeamName());
								if (teamId != null) {
									List<MartialTeamMember> members = teamMembersMap.getOrDefault(teamId, new ArrayList<>());
									List<Map<String, Object>> memberList = new ArrayList<>();
									for (MartialTeamMember member : members) {
										Map<String, Object> memberData = new HashMap<>();
										memberData.put("id", member.getId());
										MartialAthlete memberAthlete = memberAthleteMap.get(member.getAthleteId());
										memberData.put("name", memberAthlete != null ? memberAthlete.getPlayerName() : "");
										memberData.put("gender", memberAthlete != null ? memberAthlete.getGender() : "");
										memberList.add(memberData);
									}
									teamMembersCache.put(athlete.getId(), memberList);
								}
							}
						}
					}
				}
			}
			
			for (ScheduleGroupDetailVO d : groupDetails) {
				if (d.getParticipantId() == null) continue;
				
				ParticipantDTO dto = new ParticipantDTO();
				dto.setId(d.getParticipantId());
				dto.setSchoolUnit(d.getOrganization());
				dto.setStatus(d.getCheckInStatus() != null ? d.getCheckInStatus() : "未签到");
				dto.setSortOrder(d.getPerformanceOrder());
				dto.setPlayerName(d.getPlayerName());
				
				// Use cached team members data
				if (isCollective) {
					List<Map<String, Object>> memberList = teamMembersCache.getOrDefault(d.getParticipantId(), new ArrayList<>());
					dto.setMembers(memberList);
				}
				
				participantDTOs.add(dto);
			}
			groupDTOs.add(groupDTO);
		}

		// 按 displayOrder 排序
		groupDTOs.sort(Comparator.comparing(g -> {
			ScheduleGroupDetailVO detail = details.stream()
				.filter(d -> d.getGroupId().equals(g.getId()))
				.findFirst()
				.orElse(null);
			return detail != null ? detail.getDisplayOrder() : 999;
		}));

		result.setCompetitionGroups(groupDTOs);
		return result;
	}

	/**
	 * 没有编排数据时，从项目和选手表生成初始分组
	 */

	private ScheduleResultDTO generateInitialScheduleResult(Long competitionId) {
		ScheduleResultDTO result = new ScheduleResultDTO();
		result.setIsDraft(true);
		result.setIsCompleted(false);

		// 1. 获取该赛事的所有项目
		List<MartialProject> projects = projectService.list(
			new QueryWrapper<MartialProject>()
				.eq("competition_id", competitionId)
				.eq("is_deleted", 0)
				.orderByAsc("id")
		);

		if (projects.isEmpty()) {
			result.setCompetitionGroups(new ArrayList<>());
			return result;
		}

		// 2. 获取该赛事的所有选手
		List<MartialAthlete> athletes = athleteService.list(
			new QueryWrapper<MartialAthlete>()
				.eq("competition_id", competitionId)
				.eq("is_deleted", 0)
		);

		// 按项目ID分组选手
		Map<Long, List<MartialAthlete>> athletesByProject = athletes.stream()
			.collect(Collectors.groupingBy(MartialAthlete::getProjectId));

		// 3. 获取该赛事的第一个场地作为默认场地
		List<MartialVenue> venues = venueService.list(
			new QueryWrapper<MartialVenue>()
				.eq("competition_id", competitionId)
				.eq("is_deleted", 0)
				.orderByAsc("id")
				.last("LIMIT 1")
		);

		Long defaultVenueId = venues.isEmpty() ? null : venues.get(0).getId();
		String defaultVenueName = venues.isEmpty() ? "未分配" : venues.get(0).getVenueName();

		// 4. 为每个项目生成分组（单人项目根据maxParticipants拆分）
		List<CompetitionGroupDTO> groupDTOs = new ArrayList<>();
		int displayOrder = 0;
		long tempIdCounter = 1; // Counter for generating unique negative IDs for sub-groups

		for (MartialProject project : projects) {
			List<MartialAthlete> projectAthletes = athletesByProject.getOrDefault(project.getId(), new ArrayList<>());

			// 跳过没有选手的项目
			if (projectAthletes.isEmpty()) {
				continue;
			}

			// 判断项目类型
			Integer projectType = project.getType();
			String typeStr;
			if (projectType != null) {
				switch (projectType) {
					case 1:
						typeStr = "单人";
						break;
					case 2:
					case 3:
						typeStr = "集体";
						break;
					default:
						typeStr = "其他";
						break;
				}
			} else {
				typeStr = "单人";
			}

			// 单人项目：根据maxParticipants拆分
			if ("单人".equals(typeStr)) {
				Integer maxParticipants = project.getMaxParticipants();
				int athleteCount = projectAthletes.size();
				
				// 如果maxParticipants有效且人数超过限制，需要拆分
				if (maxParticipants != null && maxParticipants > 0 && athleteCount > maxParticipants) {
					int numGroups = (int) Math.ceil((double) athleteCount / maxParticipants);
					log.info("项目 '{}' 有{}人，maxParticipants={}，将拆分成{}组", 
						project.getProjectName(), athleteCount, maxParticipants, numGroups);
					
					for (int i = 0; i < numGroups; i++) {
						int startIdx = i * maxParticipants;
						int endIdx = Math.min(startIdx + maxParticipants, athleteCount);
						List<MartialAthlete> subGroupAthletes = projectAthletes.subList(startIdx, endIdx);
						
						CompetitionGroupDTO groupDTO = new CompetitionGroupDTO();
						// 使用唯一的负数ID
						groupDTO.setId(-project.getId() - tempIdCounter++);
						groupDTO.setProjectId(project.getId());
						
						// 如果只有一组，不加组号后缀
						if (numGroups == 1) {
							groupDTO.setTitle(project.getProjectName() + " 未分组");
						} else {
							groupDTO.setTitle(project.getProjectName() + " 第" + (i + 1) + "组");
						}
						groupDTO.setCode("C" + competitionId + "-P" + String.format("%03d", displayOrder + 1));
						groupDTO.setType(typeStr);
						groupDTO.setCount(subGroupAthletes.size() + "人");
						groupDTO.setVenueId(defaultVenueId);
						groupDTO.setVenueName(defaultVenueName);
						groupDTO.setTimeSlotIndex(0);
						groupDTO.setTimeSlot(null);
						
						// 生成参赛者列表
						List<ParticipantDTO> participantDTOs = new ArrayList<>();
						int sortOrder = 1;
						for (MartialAthlete athlete : subGroupAthletes) {
							ParticipantDTO dto = new ParticipantDTO();
							dto.setId(athlete.getId());
							dto.setPlayerName(athlete.getPlayerName());
							dto.setSchoolUnit(athlete.getOrganization());
							dto.setTeamName(athlete.getTeamName());
							dto.setStatus("未签到");
							dto.setSortOrder(sortOrder++);
							participantDTOs.add(dto);
						}
						
						groupDTO.setParticipants(participantDTOs);
			groupDTO.setStatus(calculateGroupStatus(participantDTOs));
						groupDTOs.add(groupDTO);
						displayOrder++;
					}
				} else {
					// 不需要拆分，生成单个分组
					CompetitionGroupDTO groupDTO = createSingleGroup(project, projectAthletes, typeStr, 
						defaultVenueId, defaultVenueName, competitionId, displayOrder);
					groupDTOs.add(groupDTO);
					displayOrder++;
				}
			} else {
				// 集体项目：不拆分
				CompetitionGroupDTO groupDTO = createSingleGroup(project, projectAthletes, typeStr, 
					defaultVenueId, defaultVenueName, competitionId, displayOrder);
				groupDTOs.add(groupDTO);
				displayOrder++;
			}
		}

		result.setCompetitionGroups(groupDTOs);
		return result;
	}

	/**
	 * 创建单个分组DTO
	 */

	private CompetitionGroupDTO createSingleGroup(MartialProject project, List<MartialAthlete> athletes, 
			String typeStr, Long venueId, String venueName, Long competitionId, int displayOrder) {
		CompetitionGroupDTO groupDTO = new CompetitionGroupDTO();
		groupDTO.setId(-project.getId());
		groupDTO.setProjectId(project.getId());
		groupDTO.setTitle(project.getProjectName() + " 未分组");
		groupDTO.setCode("C" + competitionId + "-P" + String.format("%03d", displayOrder + 1));
		groupDTO.setType(typeStr);
		groupDTO.setCount(athletes.size() + "人");
		groupDTO.setVenueId(venueId);
		groupDTO.setVenueName(venueName);
		groupDTO.setTimeSlotIndex(0);
		groupDTO.setTimeSlot(null);
		
		List<ParticipantDTO> participantDTOs = new ArrayList<>();
		int sortOrder = 1;
		for (MartialAthlete athlete : athletes) {
			ParticipantDTO dto = new ParticipantDTO();
			dto.setId(athlete.getId());
			dto.setPlayerName(athlete.getPlayerName());
			dto.setSchoolUnit(athlete.getOrganization());
			dto.setTeamName(athlete.getTeamName());
			dto.setStatus("未签到");
			dto.setSortOrder(sortOrder++);
			participantDTOs.add(dto);
		}
		
		groupDTO.setParticipants(participantDTOs);
			groupDTO.setStatus(calculateGroupStatus(participantDTOs));
		return groupDTO;
	}

	private int calculateGroupStatus(List<ParticipantDTO> participants) {
		if (participants == null || participants.isEmpty()) {
			return 0;
		}
		boolean allCompleted = true;
		boolean anyStarted = false;
		for (ParticipantDTO p : participants) {
			String s = p.getStatus();
			if ("已完成".equals(s) || "已签到".equals(s)) {
				anyStarted = true;
			} else {
				allCompleted = false;
			}
		}
		if (allCompleted && anyStarted) return 2;
		if (anyStarted) return 1;
		return 0;
	}

}
