/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.martial.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.martial.mapper.*;
import org.springblade.modules.martial.pojo.entity.*;
import org.springblade.modules.martial.config.ScheduleConfig;
import org.springblade.modules.martial.service.IMartialScheduleArrangeService;
import org.springblade.modules.martial.service.schedule.generator.TimeSlotGenerator;
import org.springblade.modules.martial.service.schedule.grouping.ParticipantGroupingService;
import org.springblade.modules.martial.service.schedule.allocation.VenueAllocationService;
import org.springblade.modules.martial.service.schedule.query.ScheduleQueryService;
import org.springframework.stereotype.Service;
import org.springblade.modules.martial.service.schedule.model.ScheduleGroupData;
import org.springblade.modules.martial.service.schedule.model.TimeSlot;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 赛程自动编排服务实现类
 *
 * @author BladeX
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrangeService {

	private final MartialScheduleStatusMapper scheduleStatusMapper;
	private final MartialScheduleGroupMapper scheduleGroupMapper;
	private final MartialScheduleDetailMapper scheduleDetailMapper;
	private final MartialScheduleParticipantMapper scheduleParticipantMapper;
	private final MartialAthleteMapper athleteMapper;
	private final MartialTeamMapper teamMapper;
	private final MartialTeamMemberMapper teamMemberMapper;
	private final MartialCompetitionMapper competitionMapper;
	private final MartialVenueMapper venueMapper;
	private final MartialProjectMapper projectMapper;
	private final ScheduleConfig scheduleConfig;
	private final TimeSlotGenerator timeSlotGenerator;

	private final ParticipantGroupingService participantGroupingService;
	private final VenueAllocationService venueAllocationService;

	private final ScheduleQueryService scheduleQueryService;
	@Override
	public List<Long> getUnlockedCompetitions() {
		return scheduleQueryService.getUnlockedCompetitions();
	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public void autoArrange(Long competitionId) {
		log.info("开始自动编排赛程, competitionId: {}", competitionId);

		try {
			// 1. 检查赛事状态
			MartialScheduleStatus status = getOrCreateScheduleStatus(competitionId);
			if (status.getScheduleStatus() == 2) {
				log.info("赛事已锁定,跳过自动编排, competitionId: {}", competitionId);
				return;
			}

			// 2. 加载赛事信息
			MartialCompetition competition = competitionMapper.selectById(competitionId);
			if (competition == null) {
				log.error("赛事不存在, competitionId: {}", competitionId);
				return;
			}

			// 3. 加载场地列表
			List<MartialVenue> venues = loadVenues(competitionId);
			if (venues.isEmpty()) {
				log.warn("赛事没有配置场地, competitionId: {}", competitionId);
				return;
			}

			// 4. 加载参赛者列表
			List<MartialAthlete> athletes = loadAthletes(competitionId);
			if (athletes.isEmpty()) {
				log.warn("赛事没有参赛者, competitionId: {}", competitionId);
				return;
			}

			// 5. 生成时间段网格
			List<TimeSlot> timeSlots = timeSlotGenerator.generate(competition);

			// 6. 自动分组
			List<ScheduleGroupData> groups = participantGroupingService.autoGroup(athletes);

			// 7. 验证容量是否足够
			venueAllocationService.validateCapacity(groups, venues, timeSlots);

			// 8. 分配场地和时间段(轮询均匀分配)
			venueAllocationService.allocate(groups, venues, timeSlots);

			// 8. 清空旧的编排数据
			clearOldScheduleData(competitionId);

			// 9. 保存新的编排结果
			saveScheduleData(competitionId, groups);

			// 10. 更新编排状态
			status.setScheduleStatus(1);
			status.setLastAutoScheduleTime(LocalDateTime.now());
			status.setTotalGroups(groups.size());
			status.setTotalParticipants(athletes.size());
			status.setUpdateTime(new Date());
			scheduleStatusMapper.updateById(status);

			log.info("自动编排完成, competitionId: {}, 分组数: {}, 参赛人数: {}",
				competitionId, groups.size(), athletes.size());

		} catch (Exception e) {
			log.error("自动编排失败, competitionId: {}", competitionId, e);
			throw new RuntimeException("自动编排失败: " + e.getMessage(), e);
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveAndLock(Long competitionId, String userId) {
		log.info("保存并锁定编排, competitionId: {}, userId: {}", competitionId, userId);

		MartialScheduleStatus status = getOrCreateScheduleStatus(competitionId);
		status.setScheduleStatus(2);
		status.setLockedTime(LocalDateTime.now());
		status.setLockedBy(userId);
		status.setUpdateTime(new Date());

		scheduleStatusMapper.updateById(status);

		log.info("编排已保存并锁定, competitionId: {}", competitionId);
	}

	@Override
	public Map<String, Object> getScheduleResult(Long competitionId) {
		return scheduleQueryService.getScheduleResult(competitionId);
	}


	// ==================== 私有辅助方法 ====================

	private MartialScheduleStatus getOrCreateScheduleStatus(Long competitionId) {
		LambdaQueryWrapper<MartialScheduleStatus> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(MartialScheduleStatus::getCompetitionId, competitionId)
			.eq(MartialScheduleStatus::getIsDeleted, 0)
			.last("LIMIT 1");

		MartialScheduleStatus status = scheduleStatusMapper.selectOne(wrapper);

		if (status == null) {
			status = new MartialScheduleStatus();
			status.setCompetitionId(competitionId);
			status.setScheduleStatus(0);
			status.setTotalGroups(0);
			status.setTotalParticipants(0);
			status.setCreateTime(new Date());
			scheduleStatusMapper.insert(status);
		}

		return status;
	}

	private List<MartialVenue> loadVenues(Long competitionId) {
		LambdaQueryWrapper<MartialVenue> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(MartialVenue::getCompetitionId, competitionId)
			.eq(MartialVenue::getIsDeleted, 0);
		return venueMapper.selectList(wrapper);
	}

	private List<MartialAthlete> loadAthletes(Long competitionId) {
		LambdaQueryWrapper<MartialAthlete> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(MartialAthlete::getCompetitionId, competitionId)
			.eq(MartialAthlete::getIsDeleted, 0);
		return athleteMapper.selectList(wrapper);
	}


	private void clearOldScheduleData(Long competitionId) {
		// 删除旧的编排数据
		LambdaQueryWrapper<MartialScheduleGroup> groupWrapper = new LambdaQueryWrapper<>();
		groupWrapper.eq(MartialScheduleGroup::getCompetitionId, competitionId);

		// 先查询出所有分组ID,然后再删除
		List<Long> groupIds = scheduleGroupMapper.selectList(groupWrapper).stream()
			.map(MartialScheduleGroup::getId)
			.collect(Collectors.toList());

		// 删除参赛者关联(必须在删除分组之前)
		if (groupIds != null && !groupIds.isEmpty()) {
			LambdaQueryWrapper<MartialScheduleParticipant> participantWrapper = new LambdaQueryWrapper<>();
			participantWrapper.in(MartialScheduleParticipant::getScheduleGroupId, groupIds);
			scheduleParticipantMapper.delete(participantWrapper);
		}

		// 删除场地时间段详情
		LambdaQueryWrapper<MartialScheduleDetail> detailWrapper = new LambdaQueryWrapper<>();
		detailWrapper.eq(MartialScheduleDetail::getCompetitionId, competitionId);
		scheduleDetailMapper.delete(detailWrapper);

		// 最后删除分组
		scheduleGroupMapper.delete(groupWrapper);
	}

	private void saveScheduleData(Long competitionId, List<ScheduleGroupData> groups) {
		for (ScheduleGroupData groupData : groups) {
			// 保存分组
			MartialScheduleGroup group = new MartialScheduleGroup();
			group.setCompetitionId(competitionId);
			group.setGroupName(groupData.getGroupName());
			group.setProjectId(groupData.getProjectId());
			group.setProjectName(groupData.getGroupName());
			group.setProjectType(groupData.getProjectType());
			group.setDisplayOrder(groupData.getDisplayOrder());
			group.setTotalParticipants(groupData.getTotalParticipants());
			group.setTotalTeams(groupData.getTotalTeams());
			group.setEstimatedDuration(groupData.getEstimatedDuration());
			group.setCategory(groupData.getCategory()); // 保存组别
			group.setCreateTime(new Date());
			scheduleGroupMapper.insert(group);

			Long groupId = group.getId();

			// 保存场地时间段详情
			if (groupData.getAssignedVenueId() != null) {
				MartialScheduleDetail detail = new MartialScheduleDetail();
				detail.setScheduleGroupId(groupId);
				detail.setCompetitionId(competitionId);
				detail.setVenueId(groupData.getAssignedVenueId());
				detail.setVenueName(groupData.getAssignedVenueName());
				detail.setScheduleDate(groupData.getAssignedDate());
				detail.setTimePeriod(groupData.getAssignedTimePeriod());
				detail.setTimeSlot(groupData.getAssignedTimeSlot());
				detail.setTimeSlotIndex(groupData.getAssignedTimeSlotIndex()); // 保存时间段索引
				detail.setEstimatedDuration(groupData.getEstimatedDuration());
				detail.setParticipantCount(groupData.getTotalParticipants());
				detail.setCreateTime(new Date());
				scheduleDetailMapper.insert(detail);

				Long detailId = detail.getId();

				// 保存参赛者关联
				int order = 1;
				for (MartialAthlete athlete : groupData.getAthletes()) {
					MartialScheduleParticipant participant = new MartialScheduleParticipant();
					participant.setScheduleDetailId(detailId);
					participant.setScheduleGroupId(groupId);
					participant.setParticipantId(athlete.getId());
					participant.setOrganization(athlete.getTeamName() != null && !athlete.getTeamName().isEmpty() ? athlete.getTeamName() : athlete.getOrganization());
					participant.setPlayerName(athlete.getPlayerName());
					participant.setProjectName(groupData.getGroupName());
					participant.setCategory(athlete.getCategory());
					participant.setPerformanceOrder(order++);
					participant.setScheduleStatus("draft"); // 初始状态为草稿
					participant.setCreateTime(new Date());
					scheduleParticipantMapper.insert(participant);
				}
			}
		}
	}

}
