package org.springblade.modules.martial.service.schedule.grouping;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.modules.martial.config.ScheduleConfig;
import org.springblade.modules.martial.mapper.MartialProjectMapper;
import org.springblade.modules.martial.mapper.MartialTeamMapper;
import org.springblade.modules.martial.mapper.MartialTeamMemberMapper;
import org.springblade.modules.martial.pojo.entity.MartialAthlete;
import org.springblade.modules.martial.pojo.entity.MartialProject;
import org.springblade.modules.martial.pojo.entity.MartialTeam;
import org.springblade.modules.martial.pojo.entity.MartialTeamMember;
import org.springblade.modules.martial.service.schedule.model.ScheduleGroupData;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

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

/**
 * Participant grouping service
 * Groups athletes by project and category for schedule arrangement
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ParticipantGroupingService {

    private final MartialProjectMapper projectMapper;
    private final MartialTeamMapper teamMapper;
    private final MartialTeamMemberMapper teamMemberMapper;
    private final ScheduleConfig scheduleConfig;

    public List<ScheduleGroupData> autoGroup(List<MartialAthlete> athletes) {
        List<ScheduleGroupData> groups = new ArrayList<>();
        int displayOrder = 1;

        Map<Long, MartialProject> projectMap = loadProjectMap(athletes);
        List<MartialAthlete> teamAthletes = filterTeamAthletes(athletes, projectMap);
        List<MartialAthlete> individualAthletes = filterIndividualAthletes(athletes, projectMap);

        displayOrder = groupTeamAthletes(teamAthletes, projectMap, groups, displayOrder);
        groupIndividualAthletes(individualAthletes, projectMap, groups, displayOrder);

        return groups;
    }

    public String getProjectCategoryName(MartialProject project) {
        if (project == null || project.getCategory() == null) {
            return "";
        }
        switch (project.getCategory()) {
            case "1": return "男子";
            case "2": return "女子";
            case "3": return "混合";
            case "4": return "团体";
            default: return project.getCategory();
        }
    }

    public List<MartialAthlete> filterByCategory(List<MartialAthlete> athletes, MartialProject project) {
        if (project == null || project.getCategory() == null) {
            return athletes;
        }
        switch (project.getCategory()) {
            case "1":
                return athletes.stream()
                    .filter(a -> a.getGender() == null || a.getGender() == 1)
                    .collect(Collectors.toList());
            case "2":
                return athletes.stream()
                    .filter(a -> a.getGender() != null && a.getGender() == 2)
                    .collect(Collectors.toList());
            default:
                return athletes;
        }
    }

    private Map<Long, MartialProject> loadProjectMap(List<MartialAthlete> athletes) {
        Set<Long> projectIds = athletes.stream()
            .map(MartialAthlete::getProjectId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        Map<Long, MartialProject> projectMap = new HashMap<>();
        for (Long projectId : projectIds) {
            MartialProject project = projectMapper.selectById(projectId);
            if (project != null) {
                projectMap.put(projectId, project);
            }
        }
        return projectMap;
    }

    private List<MartialAthlete> filterTeamAthletes(List<MartialAthlete> athletes, Map<Long, MartialProject> projectMap) {
        return athletes.stream()
            .filter(a -> {
                MartialProject project = projectMap.get(a.getProjectId());
                return project != null && (project.getType() == 2 || project.getType() == 3);
            })
            .collect(Collectors.toList());
    }

    private List<MartialAthlete> filterIndividualAthletes(List<MartialAthlete> athletes, Map<Long, MartialProject> projectMap) {
        return athletes.stream()
            .filter(a -> {
                MartialProject project = projectMap.get(a.getProjectId());
                return project != null && project.getType() == 1;
            })
            .collect(Collectors.toList());
    }

    /**
     * Get member count for a team by team name
     */
    private int getTeamMemberCount(String teamName) {
        if (teamName == null || teamName.isEmpty()) {
            return 1;
        }
        MartialTeam team = teamMapper.selectOne(
            new LambdaQueryWrapper<MartialTeam>()
                .eq(MartialTeam::getTeamName, teamName)
                .eq(MartialTeam::getIsDeleted, 0)
                .last("LIMIT 1")
        );
        if (team == null) {
            return 1;
        }
        Long count = teamMemberMapper.selectCount(
            new LambdaQueryWrapper<MartialTeamMember>()
                .eq(MartialTeamMember::getTeamId, team.getId())
                .eq(MartialTeamMember::getIsDeleted, 0)
        );
        return count != null && count > 0 ? count.intValue() : 1;
    }

    /**
     * Calculate total members across all teams
     */
    private int calculateTotalMembers(List<MartialAthlete> teams) {
        int total = 0;
        for (MartialAthlete team : teams) {
            total += getTeamMemberCount(team.getPlayerName());
        }
        return total;
    }

    private int groupTeamAthletes(List<MartialAthlete> teamAthletes, Map<Long, MartialProject> projectMap,
                                   List<ScheduleGroupData> groups, int displayOrder) {
        Map<String, List<MartialAthlete>> teamGroupMap = teamAthletes.stream()
            .collect(Collectors.groupingBy(a -> String.valueOf(a.getProjectId())));

        for (Map.Entry<String, List<MartialAthlete>> entry : teamGroupMap.entrySet()) {
            List<MartialAthlete> teams = entry.getValue();
            if (teams.isEmpty()) continue;

            MartialAthlete first = teams.get(0);
            MartialProject project = projectMap.get(first.getProjectId());
            if (project == null) {
                log.warn("Project not found, projectId: {}, skipping", first.getProjectId());
                continue;
            }

            String projectName = project.getProjectName();
            String categoryName = getProjectCategoryName(project);
            int maxParticipants = (project.getMaxParticipants() != null && project.getMaxParticipants() > 0)
                ? project.getMaxParticipants() : Integer.MAX_VALUE;
            int durationPerTeam = (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0)
                ? project.getEstimatedDuration() : 5;

            int totalMembers = calculateTotalMembers(teams);
            int teamCount = teams.size();

            log.info("Team project '{}': {} teams, {} total members, venue capacity: {}",
                projectName + " " + categoryName, teamCount, totalMembers, maxParticipants);

            if (totalMembers <= maxParticipants) {
                ScheduleGroupData group = ScheduleGroupData.builder()
                    .groupName(projectName + (categoryName.isEmpty() ? "" : " " + categoryName))
                    .projectId(first.getProjectId())
                    .projectType(2)
                    .maxParticipants(maxParticipants)
                    .category(categoryName)
                    .displayOrder(displayOrder++)
                    .totalParticipants(totalMembers)
                    .totalTeams(teamCount)
                    .athletes(teams)
                    .estimatedDuration(teamCount * durationPerTeam)
                    .build();
                groups.add(group);
            } else {
                displayOrder = splitTeamsByMemberCount(teams, project, projectName, categoryName,
                    maxParticipants, durationPerTeam, groups, displayOrder);
            }
        }
        return displayOrder;
    }

    /**
     * Split teams into sub-groups based on total member count and venue capacity
     */
    private int splitTeamsByMemberCount(List<MartialAthlete> teams, MartialProject project,
                                         String projectName, String categoryName, int maxParticipants,
                                         int durationPerTeam, List<ScheduleGroupData> groups, int displayOrder) {
        List<List<MartialAthlete>> subGroups = new ArrayList<>();
        List<MartialAthlete> currentGroup = new ArrayList<>();
        int currentMemberCount = 0;

        for (MartialAthlete team : teams) {
            int teamMemberCount = getTeamMemberCount(team.getPlayerName());
            
            if (currentMemberCount + teamMemberCount > maxParticipants && !currentGroup.isEmpty()) {
                subGroups.add(new ArrayList<>(currentGroup));
                currentGroup.clear();
                currentMemberCount = 0;
            }
            
            currentGroup.add(team);
            currentMemberCount += teamMemberCount;
        }
        
        if (!currentGroup.isEmpty()) {
            subGroups.add(currentGroup);
        }

        log.info("Team project '{}' split into {} groups based on venue capacity {}",
            projectName + " " + categoryName, subGroups.size(), maxParticipants);

        for (int i = 0; i < subGroups.size(); i++) {
            List<MartialAthlete> subGroupTeams = subGroups.get(i);
            int subGroupMemberCount = calculateTotalMembers(subGroupTeams);
            int subGroupTeamCount = subGroupTeams.size();

            ScheduleGroupData group = ScheduleGroupData.builder()
                .groupName(projectName + " " + categoryName + " 第" + (i + 1) + "组")
                .projectId(project.getId())
                .projectType(2)
                .maxParticipants(maxParticipants)
                .displayOrder(displayOrder++)
                .category(categoryName)
                .totalParticipants(subGroupMemberCount)
                .totalTeams(subGroupTeamCount)
                .athletes(subGroupTeams)
                .estimatedDuration(subGroupTeamCount * durationPerTeam)
                .build();
            groups.add(group);
        }
        return displayOrder;
    }

    private void groupIndividualAthletes(List<MartialAthlete> individualAthletes, Map<Long, MartialProject> projectMap,
                                          List<ScheduleGroupData> groups, int displayOrder) {
        Map<String, List<MartialAthlete>> individualGroupMap = individualAthletes.stream()
            .collect(Collectors.groupingBy(a -> String.valueOf(a.getProjectId())));

        for (Map.Entry<String, List<MartialAthlete>> entry : individualGroupMap.entrySet()) {
            List<MartialAthlete> members = entry.getValue();
            if (members.isEmpty()) continue;

            MartialAthlete first = members.get(0);
            MartialProject project = projectMap.get(first.getProjectId());
            if (project == null) {
                log.warn("Project not found, projectId: {}, skipping", first.getProjectId());
                continue;
            }

            members = filterByCategory(members, project);
            if (members.isEmpty()) continue;

            String projectName = project.getProjectName();
            String categoryName = getProjectCategoryName(project);
            int durationPerPerson = (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0)
                ? project.getEstimatedDuration() : scheduleConfig.getDefaultDurationPerPerson();

            int maxPeoplePerGroup = calculateMaxPeoplePerGroup(project, members.size());

            if (members.size() <= maxPeoplePerGroup) {
                ScheduleGroupData group = ScheduleGroupData.builder()
                    .groupName(projectName + " " + categoryName)
                    .projectId(first.getProjectId())
                    .projectType(1)
                    .maxParticipants(maxPeoplePerGroup)
                    .displayOrder(displayOrder++)
                    .category(categoryName)
                    .totalParticipants(members.size())
                    .athletes(members)
                    .estimatedDuration(members.size() * durationPerPerson)
                    .build();
                groups.add(group);
            } else {
                displayOrder = splitIntoSubGroups(members, project, projectName, categoryName,
                    maxPeoplePerGroup, durationPerPerson, groups, displayOrder);
            }
        }
    }

    private int calculateMaxPeoplePerGroup(MartialProject project, int memberCount) {
        Integer projectMax = project.getMaxParticipants();
        if (projectMax != null && projectMax > 0) {
            return projectMax;
        } else if (memberCount <= scheduleConfig.getMaxPeoplePerGroup()) {
            return memberCount;
        } else {
            return scheduleConfig.getTargetPeoplePerGroup();
        }
    }

    private int splitIntoSubGroups(List<MartialAthlete> members, MartialProject project,
                                    String projectName, String categoryName, int maxPeoplePerGroup,
                                    int durationPerPerson, List<ScheduleGroupData> groups, int displayOrder) {
        int totalPeople = members.size();
        int numSubGroups = (int) Math.ceil((double) totalPeople / maxPeoplePerGroup);

        log.info("Individual project '{}' has {} people, splitting into {} groups (max {} per group)",
            projectName + " " + categoryName, totalPeople, numSubGroups, maxPeoplePerGroup);

        for (int i = 0; i < numSubGroups; i++) {
            int startIdx = i * maxPeoplePerGroup;
            int endIdx = Math.min(startIdx + maxPeoplePerGroup, totalPeople);
            List<MartialAthlete> subGroupMembers = new ArrayList<>(members.subList(startIdx, endIdx));

            ScheduleGroupData group = ScheduleGroupData.builder()
                .groupName(projectName + " " + categoryName + " 第" + (i + 1) + "组")
                .projectId(project.getId())
                .projectType(1)
                .maxParticipants(maxPeoplePerGroup)
                .displayOrder(displayOrder++)
                .category(categoryName)
                .totalParticipants(subGroupMembers.size())
                .athletes(subGroupMembers)
                .estimatedDuration(subGroupMembers.size() * durationPerPerson)
                .build();
            groups.add(group);
        }
        return displayOrder;
    }
}
