using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Infrastructure.Caching;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;

namespace Jiaowu.Api.Tests;

public sealed class DemoDataSeederTests
{
    [Fact]
    public async Task SeedAsync_populates_an_empty_business_database()
    {
        await using var connection = new SqliteConnection("Data Source=:memory:");
        await connection.OpenAsync();
        await using var db = await CreateDatabaseAsync(connection);
        await SeedCourseCategoriesAsync(db);

        var seeder = new DemoDataSeeder(
            db,
            NoOpAppCache.Instance,
            NullLogger<DemoDataSeeder>.Instance);

        await seeder.SeedAsync();

        Assert.Equal(1, await db.Campuses.CountAsync());
        Assert.Equal(16, await db.Colleges.CountAsync());
        Assert.Equal(52, await db.Majors.CountAsync());
        Assert.Equal(104, await db.AdministrativeClasses.CountAsync());
        Assert.Equal(128, await db.Teachers.CountAsync());
        Assert.Equal(3640, await db.Students.CountAsync());
        Assert.Equal(148, await db.Courses.CountAsync());
        Assert.Equal(736, await db.TeacherCourseApplications.CountAsync());
        Assert.Equal(1, await db.AcademicTerms.CountAsync(x => x.IsCurrent));
        Assert.Equal(30, await db.Classrooms.CountAsync());
    }

    [Fact]
    public async Task SeedAsync_rejects_a_database_with_business_data()
    {
        await using var connection = new SqliteConnection("Data Source=:memory:");
        await connection.OpenAsync();
        await using var db = await CreateDatabaseAsync(connection);
        await SeedCourseCategoriesAsync(db);
        db.Campuses.Add(new Campus { Code = "EXISTING", Name = "现有校区" });
        await db.SaveChangesAsync();

        var seeder = new DemoDataSeeder(
            db,
            NoOpAppCache.Instance,
            NullLogger<DemoDataSeeder>.Instance);

        var exception = await Assert.ThrowsAsync<InvalidOperationException>(
            () => seeder.SeedAsync());

        Assert.Contains("只允许写入没有业务数据的新数据库", exception.Message);
        Assert.Contains("校区", exception.Message);
        Assert.Equal(1, await db.Campuses.CountAsync());
    }

    [Fact]
    public async Task SeedAsync_rolls_back_all_demo_rows_when_insertion_fails()
    {
        await using var connection = new SqliteConnection("Data Source=:memory:");
        await connection.OpenAsync();
        await using var db = await CreateDatabaseAsync(connection);
        await SeedCourseCategoriesAsync(db);
        var practiceCategory = await db.CourseCategories
            .SingleAsync(x => x.Code == "PRACTICE");
        db.CourseCategories.Remove(practiceCategory);
        await db.SaveChangesAsync();

        var seeder = new DemoDataSeeder(
            db,
            NoOpAppCache.Instance,
            NullLogger<DemoDataSeeder>.Instance);

        await Assert.ThrowsAsync<KeyNotFoundException>(() => seeder.SeedAsync());

        db.ChangeTracker.Clear();
        Assert.Equal(0, await db.Campuses.CountAsync());
        Assert.Equal(0, await db.Colleges.CountAsync());
        Assert.Equal(0, await db.Students.CountAsync());
        Assert.Equal(0, await db.Courses.CountAsync());
        Assert.Equal(0, await db.AcademicTerms.CountAsync());
        Assert.Equal(0, await db.Classrooms.CountAsync());
    }

    private static async Task<AppDbContext> CreateDatabaseAsync(
        SqliteConnection connection)
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseSqlite(connection)
            .Options;
        var db = new AppDbContext(options);
        await db.Database.EnsureCreatedAsync();
        return db;
    }

    private static async Task SeedCourseCategoriesAsync(AppDbContext db)
    {
        db.CourseCategories.AddRange(
            CreateCategory("BASIC", "基础课程", 10),
            CreateCategory("MORAL", "德育课程", 20),
            CreateCategory("AESTHETIC", "美育课程", 30),
            CreateCategory("LABOR", "劳动教育", 40),
            CreateCategory("INNOVATION", "创新创业", 50),
            CreateCategory("ENGLISH", "大学英语", 60),
            CreateCategory("SPORTS", "大学体育", 70),
            CreateCategory("MILITARY", "国防教育", 80),
            CreateCategory("MAJOR", "专业教育", 90),
            CreateCategory("PRACTICE", "实践教学", 100));
        await db.SaveChangesAsync();
    }

    private static CourseCategory CreateCategory(
        string code,
        string name,
        int sortOrder) =>
        new()
        {
            Code = code,
            Name = name,
            SortOrder = sortOrder
        };
}
