using Jiaowu.Api.Controllers;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Caching;
using Jiaowu.Api.Infrastructure.Exams;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;

namespace Jiaowu.Api.Tests;

public sealed class ExamPlanPaginationTests
{
    [Fact]
    public async Task GetPlan_PaginatesSearchesAndFiltersSessionsOnServer()
    {
        await using var connection = new SqliteConnection("Data Source=:memory:");
        await connection.OpenAsync();
        await using var db = new AppDbContext(
            new DbContextOptionsBuilder<AppDbContext>()
                .UseSqlite(connection)
                .Options);
        await db.Database.EnsureCreatedAsync();

        var college = new College { Code = "CS", Name = "计算机学院" };
        var course = new Course
        {
            Code = "CS101",
            Name = "程序设计基础",
            CollegeId = college.Id,
            Credits = 4,
            TotalHours = 64,
            LectureHours = 48,
            PracticeHours = 16,
            Nature = CourseNature.MajorRequired,
            AssessmentMethod = AssessmentMethod.Examination
        };
        var term = new AcademicTerm
        {
            Code = "2026-1",
            Name = "2026—2027 学年第一学期",
            AcademicYear = "2026-2027",
            Season = TermSeason.Autumn,
            StartDate = new DateOnly(2026, 9, 1),
            EndDate = new DateOnly(2027, 1, 20)
        };
        var tasks = Enumerable.Range(1, 11)
            .Select(number => new TeachingTask
            {
                TaskNumber = $"2026-1-CS101-{number:00}",
                Name = number == 11 ? "专项课程教学班" : $"程序设计教学班 {number}",
                AcademicTermId = term.Id,
                CourseId = course.Id,
                Capacity = 60,
                Status = TeachingTaskStatus.Published
            })
            .ToList();
        var plan = new ExamPlan
        {
            AcademicTermId = term.Id,
            Name = "期末考试",
            Status = ExamPlanStatus.Draft,
            Sessions = tasks.Select((task, index) => new ExamSession
            {
                TeachingTaskId = task.Id,
                ExamDate = new DateOnly(2027, 1, 8).AddDays(index),
                StartPeriod = 1,
                PeriodCount = 2,
                StartsAt = new DateTime(2027, 1, 8, 8, 0, 0, DateTimeKind.Utc)
                    .AddDays(index),
                EndsAt = new DateTime(2027, 1, 8, 9, 50, 0, DateTimeKind.Utc)
                    .AddDays(index),
                RequiredInvigilatorCount = 1
            }).ToList()
        };
        db.AddRange(college, course, term);
        db.AddRange(tasks);
        db.Add(plan);
        await db.SaveChangesAsync();

        var controller = new ExamsController(
            db,
            new AllScope(),
            NoOpAppCache.Instance);

        var secondPage = ReadPlan(await controller.GetPlan(
            plan.Id,
            page: 2,
            pageSize: 10,
            cancellationToken: CancellationToken.None));
        Assert.Equal(11, ReadInt(secondPage, "TotalSessionCount"));
        Assert.Equal(11, ReadInt(secondPage, "FilteredSessionCount"));
        Assert.Equal(2, ReadInt(secondPage, "SessionPage"));
        Assert.Single(ReadItems(secondPage, "Sessions"));
        Assert.Equal(11, ReadItems(secondPage, "ScheduledTeachingTaskIds").Count);

        var searchPage = ReadPlan(await controller.GetPlan(
            plan.Id,
            keyword: "专项",
            cancellationToken: CancellationToken.None));
        Assert.Equal(11, ReadInt(searchPage, "TotalSessionCount"));
        Assert.Equal(1, ReadInt(searchPage, "FilteredSessionCount"));
        Assert.Single(ReadItems(searchPage, "Sessions"));

        var completedPage = ReadPlan(await controller.GetPlan(
            plan.Id,
            allocation: "complete",
            cancellationToken: CancellationToken.None));
        Assert.Equal(0, ReadInt(completedPage, "FilteredSessionCount"));
        Assert.Empty(ReadItems(completedPage, "Sessions"));
    }

    private static object ReadPlan(ActionResult result) =>
        Assert.IsType<OkObjectResult>(result).Value!;

    private static int ReadInt(object value, string property) =>
        (int)value.GetType().GetProperty(property)!.GetValue(value)!;

    private static List<object> ReadItems(object value, string property) =>
        Assert.IsAssignableFrom<System.Collections.IEnumerable>(
                value.GetType().GetProperty(property)!.GetValue(value))
            .Cast<object>()
            .ToList();

    private sealed class AllScope : ICurrentUserDataScope
    {
        public CurrentUserScope Current { get; } = new(
            Guid.NewGuid(),
            "测试管理员",
            null,
            DataScope.All,
            new HashSet<string>([SystemRoles.SuperAdmin]));
    }
}
