using System.Globalization;
using System.Security.Cryptography;
using System.Text.Json.Nodes;
using Eis.Application.Administration;
using Eis.Infrastructure.Authentication;
using Eis.Infrastructure.Candidate;
using Eis.Infrastructure.Spreadsheets;

namespace Eis.Infrastructure.Administration;

internal sealed class AdminExcelService(
    IAuthenticationStateStore authenticationState,
    AuthenticationRepository authenticationRepository,
    AdminAccountBatchSnapshotLoader directoryLoader,
    AdminOperationalSnapshotLoader operationalLoader,
    AdminCenterSnapshotLoader centerLoader,
    AdminWriteRepository writeRepository,
    CandidateWriteRepository candidateWriteRepository,
    RegionCatalog regionCatalog,
    IAdminOrganizationService organizationService,
    IAdminCenterService centerService,
    IAdminResultService resultService) : IAdminExcelService
{
    private static readonly IReadOnlyDictionary<string, string> ResourceNames =
        new Dictionary<string, string>(StringComparer.Ordinal)
        {
            ["classes"] = "班级台账",
            ["class_admins"] = "班级管理员",
            ["account_quotas"] = "报名号班级配额",
            ["account_results"] = "报名号下发结果",
            ["candidates"] = "考生资料",
            ["payments"] = "考试缴费名单",
            ["centers"] = "考点考场档案",
            ["results"] = "成绩台账"
        };

    public async Task<AdminDocumentResult> ExportAsync(
        string sessionToken,
        string resource,
        bool template,
        string examId,
        string batchId,
        CancellationToken cancellationToken)
    {
        var context = await ResolveAsync(sessionToken, cancellationToken);
        if (context.Error is not null) return DocumentError(context.Error);
        if (!ResourceNames.ContainsKey(resource) || !SystemWorkbook.Supports(resource))
            return DocumentError(Error(404, "Excel 数据类型不存在"));
        var user = context.User!;
        if (!CanRead(user, resource)) return DocumentError(Error(403, "当前账号不能导出该数据"));
        if (resource == "results")
            return await resultService.ExportAsync(sessionToken, examId, cancellationToken);
        var rows = template ? Array.Empty<JsonObject>() :
            await ExportRowsAsync(user, resource, batchId, cancellationToken);
        if (rows is null) return DocumentError(Error(404, "批次不存在或不在当前学校范围内"));
        var content = SystemWorkbook.Build(resource, rows, template);
        var name = $"{ResourceNames[resource]}-{(template ? "导入模板" : "导出")}-{DateTime.UtcNow:yyyy-MM-dd}.xlsx";
        return new(200, content,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", name, null);
    }

    public async Task<AdminEndpointResult> ImportAsync(
        string sessionToken,
        string resource,
        byte[] content,
        CancellationToken cancellationToken)
    {
        var context = await ResolveAsync(sessionToken, cancellationToken);
        if (context.Error is not null) return context.Error;
        if (!ResourceNames.ContainsKey(resource) || !SystemWorkbook.Supports(resource))
            return Error(404, "Excel 数据类型不存在");
        if (resource is "account_results" or "payments")
            return Error(400, "该清单只支持导出");
        IReadOnlyList<JsonObject> rows;
        try
        {
            rows = SystemWorkbook.Parse(resource, content);
        }
        catch (WorkbookException error)
        {
            return Error(error.StatusCode, error.Message);
        }
        if (resource == "results")
            return await resultService.PreviewImportAsync(
                sessionToken,
                new JsonArray(rows.Select(item => (JsonNode)item).ToArray()),
                cancellationToken);
        var user = context.User!;
        return resource switch
        {
            "classes" => await ImportClassesAsync(user, rows, cancellationToken),
            "class_admins" => await ImportClassAdminsAsync(sessionToken, user, rows, cancellationToken),
            "account_quotas" => await ImportQuotasAsync(user, rows, cancellationToken),
            "candidates" => await ImportCandidatesAsync(user, rows, cancellationToken),
            "centers" => await ImportCentersAsync(sessionToken, user, rows, cancellationToken),
            _ => Error(400, "该 Excel 类型仅支持导出")
        };
    }

    private async Task<IReadOnlyList<JsonObject>?> ExportRowsAsync(
        AuthenticationUser user,
        string resource,
        string batchId,
        CancellationToken cancellationToken)
    {
        var directory = await directoryLoader.LoadAsync(cancellationToken);
        var schoolIds = Level(user) == "super"
            ? directory.Schools.Select(item => item.Id).ToHashSet(StringComparer.Ordinal)
            : new HashSet<string>(user.SchoolId is null ? [] : [user.SchoolId], StringComparer.Ordinal);
        if (resource == "classes")
            return directory.Classes.Where(item => schoolIds.Contains(item.SchoolId)).Select(item =>
                new JsonObject
                {
                    ["schoolCode"] = directory.Schools.FirstOrDefault(school => school.Id == item.SchoolId)?.Code ?? "",
                    ["grade"] = item.Grade, ["name"] = item.Name, ["status"] = item.Active ? "启用" : "停用"
                }).ToArray();
        if (resource == "class_admins")
            return directory.Users.Where(item => item.Role == "admin" && item.AdminLevel == "class" &&
                                                  item.SchoolId is not null && schoolIds.Contains(item.SchoolId))
                .Select(item => new JsonObject
                {
                    ["schoolCode"] = directory.Schools.FirstOrDefault(school => school.Id == item.SchoolId)?.Code ?? "",
                    ["className"] = directory.Classes.FirstOrDefault(entry => entry.Id == item.ClassId)?.Name ?? "",
                    ["displayName"] = item.DisplayName, ["username"] = item.Username, ["initialPassword"] = "",
                    ["status"] = item.Active ? "启用" : "停用"
                }).ToArray();
        if (resource == "account_quotas")
            return directory.Classes.Where(item => item.Active && schoolIds.Contains(item.SchoolId))
                .Select(item => new JsonObject { ["className"] = item.Name, ["count"] = 0 }).ToArray();
        if (resource == "account_results")
        {
            var batch = directory.Batches.FirstOrDefault(item =>
                item.Id == batchId && schoolIds.Contains(item.SchoolId));
            if (batch is null) return null;
            return directory.Items.Where(item => item.BatchId == batch.Id)
                .OrderBy(item => item.Position).Select(item => new JsonObject
                {
                    ["batchId"] = batch.Id,
                    ["className"] = directory.Classes.FirstOrDefault(entry => entry.Id == item.ClassId)?.Name ?? "",
                    ["candidateNumber"] = item.CandidateNumber,
                    ["initialPassword"] = item.InitialPassword
                }).ToArray();
        }
        var operational = await operationalLoader.LoadAsync(cancellationToken);
        if (resource == "candidates")
            return operational.Profiles.Where(item => InScope(user, item)).Select(profile =>
                new JsonObject
                {
                    ["candidateNumber"] = operational.Users.FirstOrDefault(item => item.Id == profile.UserId)?.CandidateNumber ?? "",
                    ["name"] = Text(profile.Data["name"]), ["gender"] = Text(profile.Data["gender"]),
                    ["idNumber"] = Text(profile.Data["idNumber"]).StartsWith("PENDING-", StringComparison.Ordinal) ? "" : Text(profile.Data["idNumber"]),
                    ["phone"] = Text(profile.Data["phone"]), ["email"] = Text(profile.Data["email"]),
                    ["nativePlace"] = Text(profile.Data["nativePlace"]), ["provinceCode"] = Text(profile.Data["provinceCode"]),
                    ["provinceName"] = Text(profile.Data["provinceName"]), ["cityCode"] = Text(profile.Data["cityCode"]),
                    ["cityName"] = Text(profile.Data["cityName"]), ["districtCode"] = Text(profile.Data["districtCode"]),
                    ["districtName"] = Text(profile.Data["districtName"]), ["address"] = Text(profile.Data["address"]),
                    ["className"] = directory.Classes.FirstOrDefault(item => item.Id == profile.ClassId)?.Name ?? Text(profile.Data["grade"]),
                    ["ethnicity"] = Text(profile.Data["ethnicity"]), ["birthDate"] = Text(profile.Data["birthDate"]),
                    ["postalCode"] = Text(profile.Data["postalCode"]), ["guardianName"] = Text(profile.Data["guardianName"]),
                    ["guardianPhone"] = Text(profile.Data["guardianPhone"])
                }).ToArray();
        if (resource == "payments")
            return operational.Registrations.Where(item => item.Status == "approved")
                .Select(registration => (Registration: registration,
                    Profile: operational.Profiles.FirstOrDefault(profile => profile.UserId == registration.UserId)))
                .Where(item => item.Profile is not null && InScope(user, item.Profile))
                .Select(item =>
                {
                    var profile = item.Profile!;
                    var exam = operational.Exams.FirstOrDefault(exam => exam.Id == item.Registration.ExamId);
                    var account = operational.Users.FirstOrDefault(account => account.Id == item.Registration.UserId);
                    var subjectIds = (item.Registration.Data["subjectIds"] as JsonArray ?? []).Select(Text).ToHashSet(StringComparer.Ordinal);
                    var subjects = (exam?.Subjects ?? []).Where(subject => subjectIds.Contains(Text(subject["id"]))).ToArray();
                    return new JsonObject
                    {
                        ["examCode"] = Text(exam?.Data["code"]), ["examName"] = Text(exam?.Data["name"]),
                        ["schoolName"] = directory.Schools.FirstOrDefault(school => school.Id == profile.SchoolId)?.Name ?? Text(profile.Data["school"]),
                        ["className"] = directory.Classes.FirstOrDefault(entry => entry.Id == profile.ClassId)?.Name ?? Text(profile.Data["grade"]),
                        ["candidateNumber"] = account?.CandidateNumber ?? Text(item.Registration.Data["registrationNumber"]),
                        ["candidateName"] = Text(profile.Data["name"]) is { Length: > 0 } name ? name : account?.DisplayName ?? "",
                        ["subjectNames"] = string.Join('、', subjects.Select(subject => Text(subject["name"]))),
                        ["amountDue"] = Math.Round(subjects.Sum(subject => Number(subject["fee"])), 2),
                        ["paymentStatus"] = item.Registration.PaymentStatus == "paid" ? "已缴费" : "待缴费",
                        ["paidAt"] = Text(item.Registration.Data["paidAt"]),
                        ["paidByName"] = directory.Users.FirstOrDefault(entry => entry.Id == item.Registration.PaidBy)?.DisplayName ?? ""
                    };
                }).ToArray();
        if (resource == "centers")
        {
            var centers = await centerLoader.LoadAsync(cancellationToken);
            return centers.Centers.Where(item => schoolIds.Contains(item.SchoolId)).SelectMany(center =>
            {
                var rooms = centers.Rooms.Where(item => item.CenterId == center.Id).ToArray();
                if (rooms.Length == 0) rooms = [new AdminCenterRoom("", center.Id, "", "", "", "", 0, "", 1, 0, "", "active", "")];
                return rooms.Select(room => new JsonObject
                {
                    ["schoolCode"] = directory.Schools.FirstOrDefault(item => item.Id == center.SchoolId)?.Code ?? "",
                    ["centerCode"] = center.Code, ["centerName"] = center.Name,
                    ["provinceCode"] = center.ProvinceCode, ["provinceName"] = center.ProvinceName,
                    ["cityCode"] = center.CityCode, ["cityName"] = center.CityName,
                    ["districtCode"] = center.DistrictCode, ["districtName"] = center.DistrictName,
                    ["address"] = center.Address, ["managerName"] = center.ManagerName, ["managerPhone"] = center.ManagerPhone,
                    ["contact"] = center.Contact, ["emergencyPhone"] = center.EmergencyPhone,
                    ["gateOpenTime"] = center.GateOpenTime, ["transport"] = center.Transport,
                    ["centerStatus"] = center.Status == "inactive" ? "停用" : "启用", ["centerNotes"] = center.Notes,
                    ["roomCode"] = room.Code, ["roomName"] = room.Name, ["building"] = room.Building, ["floor"] = room.Floor,
                    ["capacity"] = room.Id.Length == 0 ? "" : room.Capacity, ["seatPlan"] = room.SeatPlan,
                    ["roomType"] = RoomTypeName(room.RoomType),
                    ["roomStatus"] = room.Status == "inactive" ? "停用" : "启用", ["roomNotes"] = room.Notes
                });
            }).ToArray();
        }
        return [];
    }

    private async Task<AdminEndpointResult> ImportClassesAsync(
        AuthenticationUser user,
        IReadOnlyList<JsonObject> rows,
        CancellationToken cancellationToken)
    {
        if (Level(user) is not ("school" or "super")) return Error(403, "当前账号不能导入班级");
        var snapshot = await directoryLoader.LoadAsync(cancellationToken);
        var classes = snapshot.Classes.ToList();
        foreach (var row in rows)
        {
            var school = snapshot.Schools.FirstOrDefault(item =>
                item.Code.Equals(Text(row["schoolCode"]), StringComparison.OrdinalIgnoreCase));
            if (school is null || Level(user) == "school" && school.Id != user.SchoolId)
                return RowError(row, "学校代码无效或不在管理范围内");
            var name = Clean(Text(row["name"]), 100);
            var grade = Clean(Text(row["grade"]), 60);
            if (name.Length == 0 || grade.Length == 0) return RowError(row, "年级和班级名称不能为空");
            var existing = classes.FirstOrDefault(item => item.SchoolId == school.Id && item.Name == name);
            var entry = existing is null
                ? new AdminClass(Uid("class"), school.Id, name, grade, Text(row["status"]) != "停用")
                : existing with { Grade = grade, Active = Text(row["status"]) != "停用" };
            await writeRepository.SaveClassAsync(
                entry, existing is null,
                Audit(user, existing is null ? "Excel 新增班级" : "Excel 更新班级", $"{school.Name} · {name}"),
                cancellationToken);
            if (existing is null) classes.Add(entry);
        }
        return Success(new JsonObject { ["ok"] = true, ["count"] = rows.Count });
    }

    private async Task<AdminEndpointResult> ImportClassAdminsAsync(
        string token,
        AuthenticationUser user,
        IReadOnlyList<JsonObject> rows,
        CancellationToken cancellationToken)
    {
        if (Level(user) != "school") return Error(403, "班级管理员 Excel 导入由校级管理员执行");
        foreach (var row in rows)
        {
            var snapshot = await directoryLoader.LoadAsync(cancellationToken);
            var school = snapshot.Schools.FirstOrDefault(item =>
                item.Id == user.SchoolId && item.Code.Equals(Text(row["schoolCode"]), StringComparison.OrdinalIgnoreCase));
            var schoolClass = snapshot.Classes.FirstOrDefault(item =>
                item.SchoolId == user.SchoolId && item.Name == Clean(Text(row["className"]), 100));
            if (school is null || schoolClass is null) return RowError(row, "学校代码或班级名称无效");
            var username = Clean(Text(row["username"]), 50);
            var displayName = Clean(Text(row["displayName"]), 50);
            var password = Text(row["initialPassword"]);
            if (username.Length == 0 || displayName.Length == 0) return RowError(row, "管理员姓名和登录账号不能为空");
            var existing = snapshot.Users.FirstOrDefault(item =>
                item.Username.Equals(username, StringComparison.OrdinalIgnoreCase));
            if (existing is not null && (existing.AdminLevel != "class" || existing.SchoolId != user.SchoolId))
                return RowError(row, "登录账号已被其他用户占用");
            if (existing is null && password.Length < 8) return RowError(row, "新建管理员的初始密码至少 8 位");
            var body = new JsonObject
            {
                ["username"] = username, ["displayName"] = displayName, ["password"] = password,
                ["classId"] = schoolClass.Id, ["active"] = Text(row["status"]) != "停用"
            };
            var result = existing is null
                ? await organizationService.CreateAdminAsync(token, body, cancellationToken)
                : await organizationService.UpdateAdminAsync(token, existing.Id, body, cancellationToken);
            if (result.StatusCode is < 200 or >= 300) return RowError(row, Text(result.Body["message"]));
        }
        return Success(new JsonObject { ["ok"] = true, ["count"] = rows.Count });
    }

    private async Task<AdminEndpointResult> ImportQuotasAsync(
        AuthenticationUser user,
        IReadOnlyList<JsonObject> rows,
        CancellationToken cancellationToken)
    {
        if (Level(user) != "school") return Error(403, "班级配额模板仅供校级管理员使用");
        var snapshot = await directoryLoader.LoadAsync(cancellationToken);
        var quotas = new JsonArray();
        foreach (var row in rows.Where(item => Number(item["count"]) > 0))
        {
            var count = Number(row["count"]);
            var schoolClass = snapshot.Classes.FirstOrDefault(item =>
                item.SchoolId == user.SchoolId && item.Name == Clean(Text(row["className"]), 100) && item.Active);
            if (schoolClass is null || count % 1 != 0 || count is < 1 or > 200)
                return RowError(row, "班级不存在，或申领数量不在 1—200 之间");
            quotas.Add(new JsonObject
            {
                ["classId"] = schoolClass.Id, ["className"] = schoolClass.Name, ["count"] = (int)count
            });
        }
        if (quotas.Count == 0) return Error(400, "模板中没有大于 0 的申领数量");
        return Success(new JsonObject { ["ok"] = true, ["count"] = quotas.Count, ["quotas"] = quotas });
    }

    private async Task<AdminEndpointResult> ImportCandidatesAsync(
        AuthenticationUser user,
        IReadOnlyList<JsonObject> rows,
        CancellationToken cancellationToken)
    {
        var operational = await operationalLoader.LoadAsync(cancellationToken);
        var directory = await directoryLoader.LoadAsync(cancellationToken);
        foreach (var row in rows)
        {
            var account = operational.Users.FirstOrDefault(item =>
                item.CandidateNumber == Clean(Text(row["candidateNumber"]), 120));
            var profile = operational.Profiles.FirstOrDefault(item => item.UserId == account?.Id);
            if (account is null || profile is null || !InScope(user, profile))
                return RowError(row, "报名号不存在或不在数据范围内");
            if (directory.Instances.Any(item =>
                    item.BusinessType == "profile_change" && item.BusinessId == profile.Id && item.Status == "pending"))
                return RowError(row, "该考生已有待审批资料流程");
            var schoolClass = directory.Classes.FirstOrDefault(item =>
                item.SchoolId == profile.SchoolId && item.Name == Clean(Text(row["className"]), 100));
            if (schoolClass is null) return RowError(row, "班级名称无效");
            if (new[] { "name", "gender", "idNumber", "phone" }.Any(key => Clean(Text(row[key]), 200).Length == 0))
                return RowError(row, "姓名、性别、证件号码和手机号必填");
            var region = regionCatalog.Resolve(Text(row["provinceCode"]), Text(row["cityCode"]), Text(row["districtCode"]));
            if (region is null) return RowError(row, "省、市或区县代码无效，或上下级不匹配");
            var data = profile.Data.DeepClone().AsObject();
            data["name"] = Clean(Text(row["name"]), 50);
            data["gender"] = Clean(Text(row["gender"]), 10);
            data["idNumber"] = Clean(Text(row["idNumber"]), 40);
            data["phone"] = Clean(Text(row["phone"]), 30);
            data["email"] = Clean(Text(row["email"]), 100);
            data["nativePlace"] = Clean(Text(row["nativePlace"]), 100);
            data["provinceCode"] = region.ProvinceCode; data["provinceName"] = region.ProvinceName;
            data["cityCode"] = region.CityCode; data["cityName"] = region.CityName;
            data["districtCode"] = region.DistrictCode; data["districtName"] = region.DistrictName;
            data["address"] = Clean(Text(row["address"]), 200);
            data["classId"] = schoolClass.Id; data["grade"] = schoolClass.Name;
            data["ethnicity"] = Clean(Text(row["ethnicity"]), 30);
            data["birthDate"] = Clean(Text(row["birthDate"]), 20);
            data["postalCode"] = Clean(Text(row["postalCode"]), 20);
            data["guardianName"] = Clean(Text(row["guardianName"]), 50);
            data["guardianPhone"] = Clean(Text(row["guardianPhone"]), 30);
            data["profileCompleted"] = true; data["status"] = "pending"; data["reviewNote"] = "";
            data["reviewedAt"] = null; data["reviewerId"] = null; data["updatedAt"] = NowIso();
            var workflow = directory.Workflows.FirstOrDefault(item => item.BusinessType == "profile_change" && item.Active);
            var step = workflow?.Steps.OrderBy(item => item.Position).FirstOrDefault();
            if (workflow is null || step is null) return Error(409, "该业务尚未配置审批流程");
            var assignee = directory.Users.Where(item =>
                    item.Role == "admin" && item.Active && (item.AdminLevel ?? "super") == step.AdminLevel &&
                    (step.AdminLevel == "super" ||
                     step.AdminLevel == "school" && item.SchoolId == profile.SchoolId ||
                     step.AdminLevel == "class" && item.SchoolId == profile.SchoolId && item.ClassId == schoolClass.Id))
                .OrderBy(item => directory.Instances.Count(instance => instance.Status == "pending" && instance.AssigneeId == item.Id))
                .ThenBy(item => directory.Actions.Count(action => action.ToAssigneeId == item.Id))
                .ThenBy(item => item.CreatedAt, StringComparer.Ordinal).ThenBy(item => item.Id, StringComparer.Ordinal)
                .FirstOrDefault();
            if (assignee is null) return Error(409, $"没有可承接“{step.Name}”的管理员");
            var now = NowIso();
            var instance = new CandidateWorkflowInstance(
                Uid("flow"), workflow.Id, "profile_change", profile.Id, "pending", 1, assignee.Id, now, null);
            var action = new CandidateWorkflowAction(
                Uid("flow_action"), instance.Id, user.Id, "submit", "提交审批", null, assignee.Id, now);
            await candidateWriteRepository.UpdateProfileAsync(data, Text(data["name"]), instance, action, cancellationToken);
        }
        return Success(new JsonObject { ["ok"] = true, ["count"] = rows.Count });
    }

    private async Task<AdminEndpointResult> ImportCentersAsync(
        string token,
        AuthenticationUser user,
        IReadOnlyList<JsonObject> rows,
        CancellationToken cancellationToken)
    {
        if (Level(user) is not ("super" or "school")) return Error(403, "当前账号不能导入考点考场");
        var directory = await directoryLoader.LoadAsync(cancellationToken);
        var centers = await centerLoader.LoadAsync(cancellationToken);
        var groups = rows.GroupBy(item => Clean(Text(item["centerCode"]), 30).ToUpperInvariant());
        var count = 0;
        foreach (var group in groups)
        {
            var first = group.First();
            var school = directory.Schools.FirstOrDefault(item =>
                item.Code.Equals(Text(first["schoolCode"]), StringComparison.OrdinalIgnoreCase) &&
                (Level(user) == "super" || item.Id == user.SchoolId));
            if (school is null || group.Key.Length == 0) return RowError(first, "学校代码或考点代码无效");
            var existing = centers.Centers.FirstOrDefault(item =>
                item.Code.Equals(group.Key, StringComparison.OrdinalIgnoreCase));
            if (existing is not null && existing.SchoolId != school.Id) return RowError(first, "考点代码已属于其他学校");
            var rooms = new JsonArray(group.Select(row => (JsonNode)new JsonObject
            {
                ["code"] = Text(row["roomCode"]), ["name"] = Text(row["roomName"]),
                ["building"] = Text(row["building"]), ["floor"] = Text(row["floor"]),
                ["capacity"] = Number(row["capacity"]), ["seatPlan"] = Text(row["seatPlan"]),
                ["roomType"] = RoomTypeCode(Text(row["roomType"])),
                ["status"] = Text(row["roomStatus"]) == "停用" ? "inactive" : "active",
                ["notes"] = Text(row["roomNotes"])
            }).ToArray());
            var body = new JsonObject
            {
                ["schoolId"] = school.Id, ["code"] = group.Key, ["name"] = Text(first["centerName"]),
                ["provinceCode"] = Text(first["provinceCode"]), ["cityCode"] = Text(first["cityCode"]),
                ["districtCode"] = Text(first["districtCode"]), ["address"] = Text(first["address"]),
                ["managerName"] = Text(first["managerName"]), ["managerPhone"] = Text(first["managerPhone"]),
                ["contact"] = Text(first["contact"]), ["emergencyPhone"] = Text(first["emergencyPhone"]),
                ["gateOpenTime"] = Text(first["gateOpenTime"]), ["transport"] = Text(first["transport"]),
                ["status"] = Text(first["centerStatus"]) == "停用" ? "inactive" : "active",
                ["notes"] = Text(first["centerNotes"]), ["rooms"] = rooms
            };
            var result = existing is null
                ? await centerService.CreateAsync(token, body, cancellationToken)
                : await centerService.UpdateAsync(token, existing.Id, body, cancellationToken);
            if (result.StatusCode is < 200 or >= 300) return RowError(first, Text(result.Body["message"]));
            count++;
        }
        return Success(new JsonObject { ["ok"] = true, ["count"] = count });
    }

    private async Task<ResolvedAdmin> ResolveAsync(string token, CancellationToken cancellationToken)
    {
        if (token.Length == 0) return ResolvedAdmin.Failed(Error(401, "请先登录"));
        var userId = await authenticationState.GetSessionUserIdAsync(token);
        if (userId is null) return ResolvedAdmin.Failed(Error(401, "请先登录"));
        var user = await authenticationRepository.FindUserByIdAsync(userId, cancellationToken);
        return user is { Role: "admin", Active: true, ArchivedAt: null }
            ? new(user, null)
            : ResolvedAdmin.Failed(Error(user is null ? 401 : 403, user is null ? "请先登录" : "当前账号无权执行此操作"));
    }

    private static bool CanRead(AuthenticationUser user, string resource) => resource switch
    {
        "classes" or "class_admins" or "account_quotas" or "account_results" => Level(user) is "school" or "super",
        "centers" => Level(user) is "school" or "super",
        _ => true
    };
    private static bool InScope(AuthenticationUser user, OperationalProfile profile) => Level(user) switch
    {
        "super" => true,
        "school" => user.SchoolId is not null && profile.SchoolId == user.SchoolId,
        _ => user.ClassId is not null && profile.ClassId == user.ClassId
    };
    private static string Level(AuthenticationUser user) => user.AdminLevel ?? "super";
    private static string RoomTypeName(string value) => value switch
    {
        "standard" => "标准考场", "computer" => "机考考场", "accessible" => "无障碍考场",
        "spare" => "备用考场", _ => value
    };
    private static string RoomTypeCode(string value) => value switch
    {
        "标准考场" => "standard", "机考考场" => "computer", "无障碍考场" => "accessible",
        "备用考场" => "spare", _ => value
    };
    private static AdminEndpointResult RowError(JsonObject row, string message) =>
        Error(400, $"Excel 第 {Integer(row["__row"], 0)} 行：{message}");
    private static AdminEndpointResult Success(JsonObject body) => new(200, body);
    private static AdminEndpointResult Error(int status, string message) =>
        new(status, new JsonObject { ["ok"] = false, ["message"] = message });
    private static AdminDocumentResult DocumentError(AdminEndpointResult error) =>
        new(error.StatusCode, null, null, null, error.Body);
    private static string Text(JsonNode? node) =>
        node is JsonValue value && value.TryGetValue<string>(out var text) ? text : node?.ToString() ?? "";
    private static double Number(JsonNode? node) =>
        double.TryParse(Text(node), NumberStyles.Float, CultureInfo.InvariantCulture, out var value) ? value : 0;
    private static int Integer(JsonNode? node, int fallback = 0) =>
        int.TryParse(Text(node), NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) ? value : fallback;
    private static string Clean(string value, int maximum)
    {
        var cleaned = value.Trim();
        return cleaned[..Math.Min(cleaned.Length, maximum)];
    }
    private static string NowIso() =>
        DateTimeOffset.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture);
    private static string Uid(string prefix) =>
        $"{prefix}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds():x}_{Convert.ToHexStringLower(RandomNumberGenerator.GetBytes(4))}";
    private static AdminAuditEntry Audit(AuthenticationUser user, string action, string detail) =>
        new(Uid("log"), user.Id, action, detail, NowIso());
    private sealed record ResolvedAdmin(AuthenticationUser? User, AdminEndpointResult? Error)
    {
        public static ResolvedAdmin Failed(AdminEndpointResult error) => new(null, error);
    }
}
