using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace Jiaowu.Api.Infrastructure.Persistence;

public sealed class DatabaseInitializer(
    AppDbContext db,
    RoleManager<ApplicationRole> roleManager,
    UserManager<ApplicationUser> userManager,
    DevelopmentSqliteMigrator sqliteMigrator,
    DatabaseOptions databaseOptions,
    IConfiguration configuration,
    IHostEnvironment environment,
    ILogger<DatabaseInitializer> logger)
{
    public async Task InitializeAsync(bool migrateOnly = false)
    {
        if (environment.IsDevelopment())
        {
            if (migrateOnly)
            {
                throw new InvalidOperationException(
                    "--migrate-only 仅用于非 Development 环境的 MySQL 数据库。");
            }

            await db.Database.EnsureCreatedAsync();
            await sqliteMigrator.MigrateAsync();
        }
        else if (migrateOnly || databaseOptions.ApplyMigrationsOnStartup)
        {
            await db.Database.MigrateAsync();
        }
        else
        {
            var pendingMigrations = (await db.Database.GetPendingMigrationsAsync()).ToArray();
            if (pendingMigrations.Length > 0)
            {
                throw new InvalidOperationException(
                    $"数据库还有 {pendingMigrations.Length} 个待执行迁移。请在发布前运行 " +
                    "'Jiaowu.Api --migrate-only'，或显式配置 " +
                    "Database:ApplyMigrationsOnStartup=true。");
            }
        }

        if (migrateOnly)
        {
            logger.LogInformation("数据库迁移已完成。");
            return;
        }

        await SeedRolesAsync();
        await SeedAdministratorAsync();
        await SeedCourseCategoriesAsync();
    }

    private async Task SeedRolesAsync()
    {
        var roleDefinitions = new Dictionary<string, (string Description, DataScope Scope)>
        {
            [SystemRoles.SuperAdmin] = ("系统配置与全部数据管理", DataScope.All),
            [SystemRoles.AcademicAdmin] = ("校级教务管理", DataScope.All),
            [SystemRoles.CollegeAdmin] = ("院系教务管理", DataScope.College),
            [SystemRoles.Teacher] = ("教师教学工作台", DataScope.Self),
            [SystemRoles.Counselor] = ("辅导员与班级管理", DataScope.Class),
            [SystemRoles.Student] = ("学生自助服务", DataScope.Self),
            [SystemRoles.Leader] = ("校级统计查看", DataScope.All)
        };

        foreach (var (name, definition) in roleDefinitions)
        {
            if (await roleManager.RoleExistsAsync(name))
            {
                continue;
            }

            var result = await roleManager.CreateAsync(new ApplicationRole
            {
                Name = name,
                Description = definition.Description,
                DataScope = definition.Scope
            });

            EnsureSucceeded(result, $"创建角色 {name}");
        }
    }

    private async Task SeedAdministratorAsync()
    {
        var userName = configuration["SeedAdmin:UserName"];
        var password = configuration["SeedAdmin:Password"];
        if (string.IsNullOrWhiteSpace(userName) || string.IsNullOrWhiteSpace(password))
        {
            if (!environment.IsDevelopment())
            {
                logger.LogWarning("未配置 SeedAdmin，生产环境不会创建默认管理员。");
            }

            return;
        }

        var user = await userManager.FindByNameAsync(userName);
        if (user is null)
        {
            user = new ApplicationUser
            {
                UserName = userName,
                DisplayName = configuration["SeedAdmin:DisplayName"] ?? "系统管理员",
                LockoutEnabled = true,
                IsEnabled = true
            };

            EnsureSucceeded(await userManager.CreateAsync(user, password), "创建初始管理员");
        }

        if (!user.LockoutEnabled)
        {
            user.LockoutEnabled = true;
            EnsureSucceeded(await userManager.UpdateAsync(user), "启用管理员登录保护");
        }

        if (!await userManager.IsInRoleAsync(user, SystemRoles.SuperAdmin))
        {
            EnsureSucceeded(
                await userManager.AddToRoleAsync(user, SystemRoles.SuperAdmin),
                "授予超级管理员角色");
        }
    }

    private async Task SeedCourseCategoriesAsync()
    {
        var defaults = new[]
        {
            ("BASIC", "基础课程", 10),
            ("MORAL", "德育课程", 20),
            ("AESTHETIC", "美育课程", 30),
            ("LABOR", "劳动教育", 40),
            ("INNOVATION", "创新创业", 50),
            ("ENGLISH", "大学英语", 60),
            ("SPORTS", "大学体育", 70),
            ("MILITARY", "国防教育", 80),
            ("MAJOR", "专业教育", 90),
            ("PRACTICE", "实践教学", 100)
        };
        var existingCodes = (await db.CourseCategories
            .Select(x => x.Code)
            .ToListAsync())
            .ToHashSet(StringComparer.OrdinalIgnoreCase);
        foreach (var (code, name, sortOrder) in defaults)
        {
            if (existingCodes.Contains(code))
            {
                continue;
            }

            db.CourseCategories.Add(new CourseCategory
            {
                Code = code,
                Name = name,
                SortOrder = sortOrder
            });
        }
        await db.SaveChangesAsync();

        var categories = await db.CourseCategories
            .ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase);
        var uncategorizedCourses = await db.Courses
            .Where(x => x.CourseCategoryId == null)
            .ToListAsync();
        foreach (var course in uncategorizedCourses)
        {
            var categoryCode = course.Nature switch
            {
                CourseNature.Practice => "PRACTICE",
                CourseNature.MajorRequired or CourseNature.MajorElective => "MAJOR",
                _ => "BASIC"
            };
            course.CourseCategoryId = categories[categoryCode].Id;
        }
        await db.SaveChangesAsync();
    }

    private static void EnsureSucceeded(IdentityResult result, string action)
    {
        if (result.Succeeded)
        {
            return;
        }

        throw new InvalidOperationException(
            $"{action}失败：{string.Join("；", result.Errors.Select(x => x.Description))}");
    }
}
