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 DashboardControllerTests
{
    [Fact]
    public async Task College_dashboard_is_scoped_and_only_counts_actionable_stage()
    {
        await using var database = await DashboardDatabase.CreateAsync();
        var controller = new DashboardController(
            database.Db,
            new TestDataScope(
                database.FirstCollegeId,
                DataScope.College,
                SystemRoles.CollegeAdmin));

        var result = await controller.Get(CancellationToken.None);

        var response = Assert.IsType<DashboardResponse>(
            Assert.IsType<OkObjectResult>(result.Result).Value);
        Assert.Equal("College", response.Audience.Level);
        Assert.Equal("第一学院", response.Audience.ScopeName);
        Assert.Equal(1, response.Counts.Students);
        Assert.Equal(1, response.Counts.Teachers);
        Assert.Equal(1, response.Counts.TeachingTasks);
        Assert.Equal(1, response.Pending.TeacherApplications);
        Assert.Equal(1, response.Pending.StudentStatusChanges);
    }

    [Fact]
    public async Task School_dashboard_uses_school_stage_and_all_colleges()
    {
        await using var database = await DashboardDatabase.CreateAsync();
        var controller = new DashboardController(
            database.Db,
            new TestDataScope(
                null,
                DataScope.All,
                SystemRoles.AcademicAdmin));

        var result = await controller.Get(CancellationToken.None);

        var response = Assert.IsType<DashboardResponse>(
            Assert.IsType<OkObjectResult>(result.Result).Value);
        Assert.Equal("School", response.Audience.Level);
        Assert.Equal(2, response.Counts.Students);
        Assert.Equal(2, response.Counts.Teachers);
        Assert.Equal(2, response.Counts.TeachingTasks);
        Assert.Equal(2, response.Pending.TeacherApplications);
        Assert.Equal(1, response.Pending.StudentStatusChanges);
    }

    private sealed class DashboardDatabase : IAsyncDisposable
    {
        private readonly SqliteConnection connection;

        private DashboardDatabase(
            SqliteConnection connection,
            AppDbContext db,
            Guid firstCollegeId)
        {
            this.connection = connection;
            Db = db;
            FirstCollegeId = firstCollegeId;
        }

        public AppDbContext Db { get; }
        public Guid FirstCollegeId { get; }

        public static async Task<DashboardDatabase> CreateAsync()
        {
            var connection = new SqliteConnection("Data Source=:memory:");
            await connection.OpenAsync();
            var options = new DbContextOptionsBuilder<AppDbContext>()
                .UseSqlite(connection)
                .Options;
            var db = new AppDbContext(options);
            await db.Database.EnsureCreatedAsync();

            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, 15),
                IsCurrent = true
            };
            var firstCollege = new College
            {
                Code = "C01",
                Name = "第一学院"
            };
            var secondCollege = new College
            {
                Code = "C02",
                Name = "第二学院"
            };
            var firstMajor = CreateMajor("M01", firstCollege);
            var secondMajor = CreateMajor("M02", secondCollege);
            var firstClass = CreateClass("CL01", firstMajor);
            var secondClass = CreateClass("CL02", secondMajor);
            var firstStudent = CreateStudent("S01", firstClass);
            var secondStudent = CreateStudent("S02", secondClass);
            var firstTeacher = CreateTeacher("T01", firstCollege);
            var secondTeacher = CreateTeacher("T02", secondCollege);
            var firstCourse = CreateCourse("COURSE01", firstCollege);
            var secondCourse = CreateCourse("COURSE02", secondCollege);
            var firstTask = CreateTask("TASK01", term, firstCourse);
            var secondTask = CreateTask("TASK02", term, secondCourse);

            db.AddRange(
                term,
                firstCollege,
                secondCollege,
                firstMajor,
                secondMajor,
                firstClass,
                secondClass,
                firstStudent,
                secondStudent,
                firstTeacher,
                secondTeacher,
                firstCourse,
                secondCourse,
                firstTask,
                secondTask,
                new TeacherCourseApplication
                {
                    AcademicTerm = term,
                    Teacher = firstTeacher,
                    Course = firstCourse,
                    Status = TeacherCourseApplicationStatus.Pending
                },
                new TeacherCourseApplication
                {
                    AcademicTerm = term,
                    Teacher = secondTeacher,
                    Course = secondCourse,
                    Status = TeacherCourseApplicationStatus.Pending
                },
                CreateStatusChange(
                    firstStudent,
                    StudentStatusChangeState.CounselorApproved),
                CreateStatusChange(
                    firstStudent,
                    StudentStatusChangeState.CollegeApproved),
                CreateStatusChange(
                    secondStudent,
                    StudentStatusChangeState.Submitted));
            await db.SaveChangesAsync();

            return new DashboardDatabase(connection, db, firstCollege.Id);
        }

        public async ValueTask DisposeAsync()
        {
            await Db.DisposeAsync();
            await connection.DisposeAsync();
        }

        private static Major CreateMajor(string code, College college) => new()
        {
            Code = code,
            Name = $"专业 {code}",
            College = college,
            DegreeType = "本科"
        };

        private static AdministrativeClass CreateClass(
            string code,
            Major major) => new()
        {
            Code = code,
            Name = $"班级 {code}",
            Major = major,
            Grade = 2026
        };

        private static Student CreateStudent(
            string number,
            AdministrativeClass administrativeClass) => new()
        {
            StudentNumber = number,
            Name = $"学生 {number}",
            AdministrativeClass = administrativeClass,
            EnrollmentYear = 2026,
            EnrollmentDate = new DateOnly(2026, 9, 1),
            Status = StudentStatus.Active
        };

        private static Teacher CreateTeacher(
            string number,
            College college) => new()
        {
            TeacherNumber = number,
            Name = $"教师 {number}",
            College = college,
            Status = TeacherStatus.Active
        };

        private static Course CreateCourse(string code, College college) => new()
        {
            Code = code,
            Name = $"课程 {code}",
            College = college,
            Nature = CourseNature.MajorRequired,
            Credits = 2,
            TotalHours = 32,
            LectureHours = 32,
            AssessmentMethod = AssessmentMethod.Examination
        };

        private static TeachingTask CreateTask(
            string number,
            AcademicTerm term,
            Course course) => new()
        {
            TaskNumber = number,
            Name = $"教学班 {number}",
            AcademicTerm = term,
            Course = course,
            Capacity = 40,
            Status = TeachingTaskStatus.Published
        };

        private static StudentStatusChange CreateStatusChange(
            Student student,
            StudentStatusChangeState state) => new()
        {
            Student = student,
            Type = StudentStatusChangeType.Suspension,
            OriginalStatus = StudentStatus.Active,
            TargetStatus = StudentStatus.Suspended,
            Reason = "测试学籍异动流程",
            State = state
        };
    }

    private sealed class TestDataScope(
        Guid? collegeId,
        DataScope dataScope,
        string role) : ICurrentUserDataScope
    {
        public CurrentUserScope Current { get; } = new(
            Guid.NewGuid(),
            "测试管理员",
            collegeId,
            dataScope,
            new HashSet<string> { role });
    }
}
