using Core.Entities;

namespace Core.Calculation;

/// <summary>
/// Represents a solution to the team scheduling problem.
/// Contains team assignments to time slots and information about student scheduling conflicts.
/// </summary>
public class TeamSchedulerSolution(
    Team[][] timeSlots,
    Student[] students,
    string status)
{
    /// <summary>
    /// Gets the solver status (e.g., "Optimal", "Feasible", "Infeasible").
    /// </summary>
    public string Status { get; } = status;

    /// <summary>
    /// Gets the scheduled time slots with team assignments and conflict information.
    /// </summary>
    public TeamScheduleTimeSlot[] TimeSlots { get; set; }
        = timeSlots.Select( (teams,i) =>
            new TeamScheduleTimeSlot{
                Name = "Time Slot " + (i + 1),
                Teams = teams,
                StudentOverlaps = GetStudentTeamOverlaps(teams),
                UnscheduledStudents = GetStudentsNotInTimSlot(teams, students)
            }
        ).ToArray();

    /// <summary>
    /// Calculates the total number of student conflicts across all time slots.
    /// </summary>
    /// <param name="timeSlots">The scheduled time slots</param>
    /// <returns>Total count of students with overlapping team meetings</returns>
    public static int GetStudentTeamOverlapCount(Team[][] timeSlots)
    {
        return timeSlots.Sum(GetStudentTeamOverlapCount);
    }

    /// <summary>
    /// Calculates the number of student conflicts in a single time slot.
    /// </summary>
    /// <param name="timeSlot">The time slot to analyze</param>
    /// <returns>Count of students with multiple team meetings in this slot</returns>
    private static int GetStudentTeamOverlapCount(Team[] timeSlot)
    {
        return GetStudentTeamOverlaps(timeSlot).Count();
    }

    /// <summary>
    /// Identifies students who have multiple team meetings in the same time slot.
    /// </summary>
    /// <param name="timeSlot">The time slot to analyze</param>
    /// <returns>Students and their conflicting teams in this time slot</returns>
    public static IEnumerable<(Student student, IEnumerable<Team> teams)> GetStudentTeamOverlaps(Team[] timeSlot)
    {
        return
            from s in timeSlot.SelectMany(ts => ts.Students).Distinct()
            group s by timeSlot.Where(t => t.Students.Contains(s))
            into gs
            where gs.Key.Count() > 1
            select (gs.First(), gs.Key);
    }

    /// <summary>
    /// Identifies students who have no team meetings in the given time slot.
    /// </summary>
    /// <param name="timeSlot">The time slot to analyze</param>
    /// <param name="students">All students</param>
    /// <returns>Students not scheduled in this time slot</returns>
    public static Student[] GetStudentsNotInTimSlot(Team[] timeSlot, Student[] students)
    {
        var studentsInTimeSlot = timeSlot.SelectMany(ts => ts.Students).Distinct().ToHashSet();
        return students.Where(s => !studentsInTimeSlot.Contains(s)).ToArray();
    }

    /// <summary>
    /// Gets the teams a student is on that weren't assigned to any time slot.
    /// </summary>
    /// <param name="student">The student to check</param>
    /// <returns>Teams the student is on that have no scheduled meeting</returns>
    public Team[] StudentUnassignedTeams(Student student)
    {
        var meetingTeams = TimeSlots.SelectMany(t => t.Teams);
        return student.Teams.Where(e => !meetingTeams.Contains(e)).ToArray();
    }
}