using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;

namespace Jiaowu.Api.Infrastructure.Timetables;

public sealed class PublishedTimetableProjectionService(AppDbContext db)
{
    private const int WriteBatchSize = 2_000;
    public async Task RebuildPublishedPlansForTaskAsync(Guid teachingTaskId, CancellationToken cancellationToken)
    {
        var plans = await db.SchedulePlans
            .Where(plan => plan.Status == SchedulePlanStatus.Published &&
                plan.Entries.Any(entry => entry.TeachingTaskId == teachingTaskId))
            .Include(plan => plan.Entries)
            .ToListAsync(cancellationToken);
        foreach (var plan in plans)
            await RebuildAsync(plan, cancellationToken);
    }

    public async Task RebuildAsync(SchedulePlan plan, CancellationToken cancellationToken)
    {
        await db.PublishedScheduleOccurrences
            .Where(x => x.SchedulePlanId == plan.Id)
            .ExecuteDeleteAsync(cancellationToken);

        var rows = new List<PublishedScheduleOccurrence>(WriteBatchSize);
        foreach (var entry in plan.Entries)
        for (var week = entry.StartWeek; week <= entry.EndWeek; week++)
        {
            if (entry.WeekPattern == WeekPattern.Odd && week % 2 == 0 ||
                entry.WeekPattern == WeekPattern.Even && week % 2 != 0) continue;
            rows.Add(new PublishedScheduleOccurrence
            {
                SchedulePlanId = plan.Id,
                AcademicTermId = plan.AcademicTermId,
                ScheduleEntryId = entry.Id,
                TeachingTaskId = entry.TeachingTaskId,
                ClassroomId = entry.ClassroomId,
                Week = week,
                DayOfWeek = entry.DayOfWeek,
                StartPeriod = entry.StartPeriod,
                PeriodCount = entry.PeriodCount,
                Kind = entry.Kind
            });
            if (rows.Count == WriteBatchSize)
                await WriteBatchAsync(rows, cancellationToken);
        }
        if (rows.Count > 0)
            await WriteBatchAsync(rows, cancellationToken);
    }

    private async Task WriteBatchAsync(
        List<PublishedScheduleOccurrence> rows,
        CancellationToken cancellationToken)
    {
        db.PublishedScheduleOccurrences.AddRange(rows);
        await db.SaveChangesAsync(cancellationToken);
        foreach (var row in rows)
            db.Entry(row).State = EntityState.Detached;
        rows.Clear();
    }
}
