# -*- coding: utf-8 -*-
"""数据包管理：列表按生成时间倒序 + 最近到期算对 + 下载/校验按该包真实文件
   （原来下载/校验都是「按会议重新导出」，同会议多个包时点哪行都拿到同一个包）
"""
import shutil, time

BE = '/data/www/files/rmsys-app/backend/src/Rmsys.Api/Endpoints/AdminEndpoints.cs'
shutil.copy2(BE, BE + '.bakP.' + time.strftime('%Y%m%d%H%M%S'))
s = open(BE, encoding='utf-8').read()

# 1) 数据包列表：最新生成的在前
old = "            var pkgs = await db.DataPackages.OrderBy(x => x.Id).ToListAsync();"
new = ("            // 最新生成的包排在最前（同时间按 Id 倒序）\n"
       "            var pkgs = await db.DataPackages.OrderByDescending(x => x.CreatedAt)\n"
       "                .ThenByDescending(x => x.Id).ToListAsync();")
assert s.count(old) == 1, "pkgs 查询未命中"
s = s.replace(old, new, 1)

# 2) 新增：按包 ID 下载该包原始文件 + 按包 ID 校验该包实际文件
anchor = '        app.MapGet("/api/admin/bootstrap", async (int? meetingId, ClaimsPrincipal p, RmsysDbContext db) =>'
assert s.count(anchor) == 1, "bootstrap 端点锚点未命中"

eps = '''        // ===== 按「包 ID」下载该包的真实文件（不再按会议重新导出，避免多包时下错）=====
        app.MapGet("/api/admin/packages/by-id/{pkgId:int}/file", async (
            int pkgId, ClaimsPrincipal p, RmsysDbContext db, IWebHostEnvironment env) =>
        {
            var pkg = await db.DataPackages.FirstOrDefaultAsync(x => x.Id == pkgId);
            if (pkg == null) return Results.NotFound(new { ok = false, message = "数据包不存在" });
            if (pkg.Status == PackageStatus.Destroyed)
                return Results.NotFound(new { ok = false, message = "该数据包已销毁，文件不可下载" });

            var full = Path.Combine(env.ContentRootPath,
                (pkg.FilePath ?? "").Replace('/', Path.DirectorySeparatorChar));
            if (string.IsNullOrWhiteSpace(pkg.FilePath) || !File.Exists(full))
                return Results.NotFound(new { ok = false, message = "包文件不在服务器上（可能已被清理），请重新生成该数据包" });

            await AuditRead(db, p, "下载数据包", pkg.PkgNo, Size(pkg.FileSize));
            return Results.File(full, "application/octet-stream", pkg.PkgNo + ".pkg");
        }).RequireAuthorization();

        // ===== 按「包 ID」校验该包实际文件：解密→校验表结构/条数/配置 =====
        app.MapGet("/api/admin/packages/by-id/{pkgId:int}/check", async (
            int pkgId, ClaimsPrincipal p, RmsysDbContext db, IWebHostEnvironment env) =>
        {
            var pkg = await db.DataPackages.FirstOrDefaultAsync(x => x.Id == pkgId);
            if (pkg == null) return Results.Ok(new { ok = false, message = "数据包不存在" });
            if (pkg.Status == PackageStatus.Destroyed)
                return Results.Ok(new { ok = false, message = "该数据包已销毁，无文件可校验" });

            var full = Path.Combine(env.ContentRootPath,
                (pkg.FilePath ?? "").Replace('/', Path.DirectorySeparatorChar));
            if (string.IsNullOrWhiteSpace(pkg.FilePath) || !File.Exists(full))
                return Results.Ok(new { ok = false, message = "包文件不在服务器上（可能已被清理），请重新生成" });

            var bytes = await File.ReadAllBytesAsync(full);
            var (hok, _, _, _) = PackageCrypto.ReadHeader(bytes);
            if (!hok)
                return Results.Ok(new { ok = false, message = "包文件头部无效，不是本系统生成的数据包" });

            var key = new PackageExporter(db).MasterKey();
            var tmp = Path.Combine(Path.GetTempPath(), $"chk-{Guid.NewGuid():N}.db");
            VerifyReport v;
            try
            {
                await File.WriteAllBytesAsync(tmp, PackageCrypto.Decode(bytes, key));
                v = PackageVerifier.Verify(tmp);
            }
            catch (Exception ex)
            {
                return Results.Ok(new { ok = false, message = "解密失败：" + ex.Message });
            }
            finally { try { if (File.Exists(tmp)) File.Delete(tmp); } catch { } }

            await AuditRead(db, p, "校验数据包", pkg.PkgNo,
                v.Ok ? "校验通过" : ("校验未通过：" + string.Join("；", v.Errors.Take(3))));
            return Results.Ok(new
            {
                ok = v.Ok,
                pkgNo = v.PkgNo,
                meeting = v.MeetingName,
                expireAt = v.ExpireAt,
                watermark = v.Watermark,
                matOrder = v.MatOrder,
                counts = new { cadres = v.Cadres, resumes = v.Resumes, families = v.Families,
                               notes = v.Notes, materials = v.Materials, docs = v.Docs, padBound = v.PadBound },
                missingTables = v.MissingTables,
                missingConfig = v.MissingConfig,
                errors = v.Errors,
                bytes = v.Bytes,
                message = v.Ok ? "校验通过：包内容完整、配置齐全" : ("校验未通过：" + string.Join("；", v.Errors.Take(3)))
            });
        }).RequireAuthorization();

''' + anchor
s = s.replace(anchor, eps, 1)

open(BE, 'w', encoding='utf-8').write(s)
print("✓ 后端：pkgs 倒序 + 按包下载文件 + 按包校验文件")
