using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Infrastructure.Scheduling;

namespace Jiaowu.Api.Infrastructure.CourseSelection;

public static class CourseSelectionRules
{
    public static bool IsSelectionOpen(CourseSelectionRound round, DateTime nowUtc) =>
        round.Status == CourseSelectionRoundStatus.Open &&
        nowUtc >= round.StartsAt &&
        nowUtc <= round.EndsAt;

    public static bool CanWithdraw(CourseSelectionRound round, DateTime nowUtc) =>
        round.Status == CourseSelectionRoundStatus.Open &&
        nowUtc <= round.WithdrawalEndsAt;

    public static bool CanPromoteWaitlist(CourseSelectionRound round, DateTime nowUtc) =>
        round.Status == CourseSelectionRoundStatus.Open &&
        nowUtc <= round.WithdrawalEndsAt;

    public static bool SupportsProxyEnrollment(CourseNature nature) =>
        nature == CourseNature.GeneralRequired;

    public static bool IsGradeEligible(
        IEnumerable<int> eligibleGrades,
        int studentGrade) =>
        !eligibleGrades.Any() || eligibleGrades.Contains(studentGrade);

    public static bool HasReachedCourseLimit(
        int? maxCourseCount,
        int selectedCourseCount) =>
        maxCourseCount.HasValue && selectedCourseCount >= maxCourseCount.Value;

    public static bool RequiresPublishedSchedule(TeachingTaskSchedulingMode schedulingMode) =>
        schedulingMode == TeachingTaskSchedulingMode.Standard;

    public static bool HasScheduleConflict(
        IEnumerable<ScheduleEntry> candidateEntries,
        IEnumerable<ScheduleEntry> selectedEntries) =>
        candidateEntries.Any(candidate =>
            selectedEntries.Any(selected =>
                ScheduleConflictDetector.TimeOverlaps(candidate, selected)));

    /// <summary>
    /// Returns the overlap percentage of candidate schedule entries with selected entries.
    /// 0 = no conflict, 100 = fully overlapping.
    /// Used for retake enrollment where ≤50% overlap is allowed.
    /// </summary>
    public static double CalculateScheduleOverlap(
        IReadOnlyCollection<ScheduleEntry> candidateEntries,
        IReadOnlyCollection<ScheduleEntry> selectedEntries)
    {
        if (candidateEntries.Count == 0 || selectedEntries.Count == 0)
            return 0;

        int totalCandidatePeriods = 0;
        int overlappedPeriods = 0;

        foreach (var candidate in candidateEntries)
        {
            totalCandidatePeriods += candidate.PeriodCount;
            foreach (var selected in selectedEntries)
            {
                if (!ScheduleConflictDetector.TimeOverlaps(candidate, selected))
                    continue;
                // Calculate overlapping periods
                var overlapStart = Math.Max(
                    candidate.StartPeriod, selected.StartPeriod);
                var overlapEnd = Math.Min(
                    candidate.StartPeriod + candidate.PeriodCount,
                    selected.StartPeriod + selected.PeriodCount);
                if (overlapEnd > overlapStart)
                    overlappedPeriods += overlapEnd - overlapStart;
            }
        }

        return totalCandidatePeriods == 0
            ? 0
            : (double)overlappedPeriods / totalCandidatePeriods * 100;
    }

    /// <summary>Retake expanded capacity: ceiling(original * 1.15).</summary>
    public static int RetakeCapacity(int originalCapacity) =>
        (int)Math.Ceiling(originalCapacity * 1.15);

    public static int EffectiveCapacity(int originalCapacity, bool isRetake) =>
        isRetake ? RetakeCapacity(originalCapacity) : originalCapacity;
}
