using System.Text.Json;
using Jiaowu.Api.Controllers;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;

namespace Jiaowu.Api.Tests;

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

        var user = new ApplicationUser
        {
            Id = Guid.NewGuid(),
            UserName = "student",
            NormalizedUserName = "STUDENT",
            DisplayName = "规划学生"
        };
        var college = new College { Code = "CS", Name = "计算机学院" };
        var major = new Major
        {
            Code = "SE",
            Name = "软件工程",
            CollegeId = college.Id,
            DegreeType = "工学",
            SchoolingYears = 4
        };
        var administrativeClass = new AdministrativeClass
        {
            Code = "SE2501",
            Name = "软件工程2501班",
            MajorId = major.Id,
            Grade = 2025
        };
        var student = new Student
        {
            StudentNumber = "20250001",
            Name = "规划学生",
            AdministrativeClassId = administrativeClass.Id,
            EnrollmentYear = 2025,
            EnrollmentDate = new DateOnly(2025, 9, 1),
            UserId = user.Id
        };
        var pastTerm = new AcademicTerm
        {
            Code = "2025-A",
            Name = "2025—2026 学年秋季学期",
            AcademicYear = "2025-2026",
            Season = TermSeason.Autumn,
            StartDate = new DateOnly(2025, 9, 1),
            EndDate = new DateOnly(2026, 1, 15)
        };
        var currentTerm = new AcademicTerm
        {
            Code = "2025-S",
            Name = "2025—2026 学年春季学期",
            AcademicYear = "2025-2026",
            Season = TermSeason.Spring,
            StartDate = new DateOnly(2026, 2, 20),
            EndDate = new DateOnly(2026, 7, 10),
            IsCurrent = true
        };
        var introduction = new Course
        {
            Code = "CS101",
            Name = "程序设计基础",
            CollegeId = college.Id,
            Credits = 4,
            TotalHours = 64,
            LectureHours = 48,
            PracticeHours = 16,
            Nature = CourseNature.MajorRequired,
            AssessmentMethod = AssessmentMethod.Examination
        };
        var dataStructures = new Course
        {
            Code = "CS201",
            Name = "数据结构",
            CollegeId = college.Id,
            Credits = 4,
            TotalHours = 64,
            LectureHours = 48,
            PracticeHours = 16,
            Nature = CourseNature.MajorRequired,
            AssessmentMethod = AssessmentMethod.Examination,
            Prerequisites =
            [
                new CoursePrerequisite
                {
                    PrerequisiteCourseId = introduction.Id
                }
            ]
        };
        var plan = new CurriculumPlan
        {
            MajorId = major.Id,
            Name = "软件工程本科培养方案",
            Version = "2025",
            EffectiveGrade = 2025,
            TotalCredits = 8,
            Status = CurriculumPlanStatus.Published,
            PublishedAt = DateTime.UtcNow,
            Modules =
            [
                new CurriculumModule
                {
                    Code = "M01",
                    Name = "专业基础",
                    RequiredCredits = 0,
                    Courses =
                    [
                        new CurriculumCourse
                        {
                            CourseId = introduction.Id,
                            RecommendedSemester = 1,
                            Type = CurriculumCourseType.Required
                        },
                        new CurriculumCourse
                        {
                            CourseId = dataStructures.Id,
                            RecommendedSemester = 3,
                            Type = CurriculumCourseType.Required
                        }
                    ]
                }
            ]
        };
        var teachingTask = new TeachingTask
        {
            TaskNumber = "2025-CS101-01",
            Name = "程序设计基础",
            AcademicTermId = pastTerm.Id,
            CourseId = introduction.Id,
            Capacity = 50,
            Status = TeachingTaskStatus.Closed
        };
        var gradeSheet = new GradeSheet
        {
            TeachingTaskId = teachingTask.Id,
            Status = GradeSheetStatus.Published,
            PublishedAt = DateTime.UtcNow,
            Records =
            [
                new GradeRecord
                {
                    StudentId = student.Id,
                    TotalScore = 82,
                    GradePoint = 3.2m
                }
            ]
        };
        db.AddRange(
            user,
            college,
            major,
            administrativeClass,
            student,
            pastTerm,
            currentTerm,
            introduction,
            dataStructures,
            plan,
            teachingTask,
            gradeSheet);
        await db.SaveChangesAsync();

        var controller = new AcademicPlanningController(
            db,
            new StudentDataScope(user.Id));
        var overview = Assert.IsType<OkObjectResult>(
            await controller.Get(default));
        using var overviewJson = ToJson(overview.Value);
        Assert.Equal(
            4,
            overviewJson.RootElement
                .GetProperty("baseline")
                .GetProperty("earnedCredits")
                .GetDecimal());
        Assert.Equal(
            dataStructures.Id,
            overviewJson.RootElement
                .GetProperty("nextSemesterSuggestion")[0]
                .GetProperty("courseId")
                .GetGuid());

        var result = Assert.IsType<OkObjectResult>(
            await controller.Simulate(
                new AcademicPlanningRequest(
                [
                    new AcademicPlanningTermRequest(
                        3,
                        [dataStructures.Id])
                ]),
                default));
        using var resultJson = ToJson(result.Value);
        Assert.Empty(resultJson.RootElement.GetProperty("conflicts").EnumerateArray());
        var projected = resultJson.RootElement.GetProperty("projected");
        Assert.Equal(8, projected.GetProperty("earnedCredits").GetDecimal());
        Assert.Equal(
            (int)GraduationAuditConclusion.Eligible,
            projected.GetProperty("graduationConclusion").GetInt32());
    }

    private static JsonDocument ToJson(object? value) => JsonDocument.Parse(
        JsonSerializer.Serialize(
            value,
            new JsonSerializerOptions(JsonSerializerDefaults.Web)));

    private sealed class StudentDataScope(Guid userId) : ICurrentUserDataScope
    {
        public CurrentUserScope Current { get; } = new(
            userId,
            "规划学生",
            null,
            DataScope.Self,
            new HashSet<string>([SystemRoles.Student]));
    }
}
