using Jiaowu.Api.Controllers;
using Jiaowu.Api.Contracts;
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 OtherExamsControllerTests
{
    [Fact]
    public async Task Batches_AreFilteredAndReturnedByPage()
    {
        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();
        db.OtherExamBatches.AddRange(Enumerable.Range(1, 11).Select(number =>
            new OtherExamBatch
            {
                ExamCode = $"CET-{number:00}",
                Name = $"英语等级考试 {number}",
                ExamDate = new DateOnly(2026, 6, number),
                MetricKind = OtherExamMetricKind.Score,
                MaxScore = 100,
                Status = number == 11
                    ? OtherExamBatchStatus.Published
                    : OtherExamBatchStatus.Draft
            }));
        await db.SaveChangesAsync();

        var controller = new OtherExamsController(db, new TestDataScope(Guid.NewGuid()));
        var result = Assert.IsType<OkObjectResult>(await controller.GetBatches(
            keyword: "英语", status: OtherExamBatchStatus.Draft, page: 1,
            pageSize: 10, ct: CancellationToken.None));
        var page = Assert.IsType<PagedResult<object>>(result.Value);
        Assert.Equal(10, page.Total);
        Assert.Equal(10, page.Items.Count);
    }

    [Fact]
    public async Task ReplaceResults_ReplacesExistingResultsInsideTransaction()
    {
        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 college = new College { Code = "CS", Name = "计算机学院" };
        var major = new Major { Code = "SE", Name = "软件工程", CollegeId = college.Id, DegreeType = "工学" };
        var administrativeClass = new AdministrativeClass { Code = "SE202601", Name = "软件工程 2026 级 1 班", MajorId = major.Id, Grade = 2026 };
        var student = new Student
        {
            StudentNumber = "202600001",
            Name = "测试学生",
            AdministrativeClassId = administrativeClass.Id,
            EnrollmentYear = 2026,
            EnrollmentDate = new DateOnly(2026, 9, 1),
            Status = StudentStatus.Active
        };
        var batch = new OtherExamBatch
        {
            ExamCode = "CET4",
            Name = "大学英语四级",
            ExamDate = new DateOnly(2026, 6, 1),
            MetricKind = OtherExamMetricKind.Score,
            MaxScore = 710
        };
        var oldResult = new OtherExamResult
        {
            OtherExamBatchId = batch.Id,
            StudentId = student.Id,
            Score = 500,
            AttemptNumber = 1
        };
        db.AddRange(college, major, administrativeClass, student, batch, oldResult);
        await db.SaveChangesAsync();

        var controller = new OtherExamsController(db, new TestDataScope(college.Id));
        var result = await controller.ReplaceResults(batch.Id, new ReplaceOtherExamResultsRequest([
            new OtherExamResultRequest(student.StudentNumber, 610.5m, null, null, "已复核")
        ]), CancellationToken.None);

        Assert.IsType<OkObjectResult>(result);
        db.ChangeTracker.Clear();
        var saved = await db.OtherExamResults.SingleAsync(x => x.OtherExamBatchId == batch.Id);
        Assert.Equal(610.5m, saved.Score);
        Assert.Equal("已复核", saved.Notes);
        Assert.Equal(1, saved.AttemptNumber);
    }

    [Fact]
    public async Task ReplaceResults_RejectsOverlongNotesBeforeDatabaseSave()
    {
        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 college = new College { Code = "CS", Name = "计算机学院" };
        var major = new Major { Code = "SE", Name = "软件工程", CollegeId = college.Id, DegreeType = "工学" };
        var administrativeClass = new AdministrativeClass { Code = "SE202601", Name = "软件工程 2026 级 1 班", MajorId = major.Id, Grade = 2026 };
        var student = new Student { StudentNumber = "202600001", Name = "测试学生", AdministrativeClassId = administrativeClass.Id, EnrollmentYear = 2026, EnrollmentDate = new DateOnly(2026, 9, 1), Status = StudentStatus.Active };
        var batch = new OtherExamBatch { ExamCode = "CET4", Name = "大学英语四级", ExamDate = new DateOnly(2026, 6, 1), MetricKind = OtherExamMetricKind.Score, MaxScore = 710 };
        db.AddRange(college, major, administrativeClass, student, batch);
        await db.SaveChangesAsync();

        var controller = new OtherExamsController(db, new TestDataScope(college.Id));
        var result = await controller.ReplaceResults(batch.Id, new ReplaceOtherExamResultsRequest([
            new OtherExamResultRequest(student.StudentNumber, 610, null, null, new string('注', 501))
        ]), CancellationToken.None);

        var validation = Assert.IsType<ObjectResult>(result);
        Assert.Contains("备注不能超过 500 个字符", Assert.IsType<ValidationProblemDetails>(validation.Value).Detail);
        Assert.Empty(await db.OtherExamResults.ToListAsync());
    }

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