using System.Reflection;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using SolutionErp.Domain.Identity;
using SolutionErp.Infrastructure.Persistence;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Application;

// [S92 2026-06-29] Call grant này (SeedAllRolesHrmProfileReadPermissionsAsync, S65) ĐÃ TẮT khỏi
// SeedAsync — anh chốt nhóm "Nhân sự" CHỈ Admin thấy. Test giữ guard method-CÔ-LẬP cho reversibility
// (method vẫn private static); hành vi prod THẬT = AdminOnlyModulesRevokeTests (revoke là bước cuối).
// Assertion dưới KHÔNG đổi — vẫn pass vì invoke trực tiếp method cô lập.
// S66 test-after (UAT mode — "public Hồ sơ NS nhưng ẩn Dashboard/Config" shipped S65).
// DbInitializer permission-seed chain (SeedAsync order, line 2033-2046):
//   1. SeedAllRolesReviewReadPermissionsAsync   (grant Master/Catalogs/Pe_* read)
//   2. RevokeTemporarilyHiddenModulesAsync (S58) → set MỌI Hrm*/Off*/Personal = false
//      cho non-Admin (StartsWith("Hrm") bắt cả Hrm, Hrm_HoSo, Hrm_Dashboard, Hrm_Config*).
//   3. SeedAllRolesHrmProfileReadPermissionsAsync (S65) → CHẠY SAU để THẮNG revoke,
//      nâng RIÊNG 2 key { Hrm, Hrm_HoSo } = CanRead=true (upgrade-only).
//
// Kết quả mong đợi (anh chốt public module Nhân sự, ẩn Dashboard + 6 catalog config):
//   - non-Admin role: Hrm=true, Hrm_HoSo=true  (mở lại để tra cứu hồ sơ NS)
//   - non-Admin role: Hrm_Dashboard=false, Hrm_Config*=false  (revoke vẫn che)
//   - Admin role: KHÔNG bị revoke (giữ nguyên — quản trị/chuẩn bị dữ liệu)
//
// 2 method này `private static` trong DbInitializer → invoke qua REFLECTION
// (BindingFlags.NonPublic|Static). Test ĐÚNG behavior code thật, KHÔNG re-implement.
// Signature cả 2: (ApplicationDbContext db, RoleManager<Role> roleManager, ILogger logger).
//
// FK lưu ý (PermissionConfiguration): Permission.MenuKey → MenuItem.Key (Cascade) +
// Permission.RoleId → Role (Cascade). → PHẢI seed MenuItem rows + Role trước khi seed
// Permission rows (nếu không SQLite FK Error 19).
public class HrmProfilePermissionSeedTests
{
    // 6 leaf config (đại diện Hrm_Config*) — tất cả phải GIỮ ẩn sau chain.
    private static readonly string[] ConfigKeys =
    {
        MenuKeys.HrmConfig,
        MenuKeys.HrmConfigLeaveTypes,
        MenuKeys.HrmConfigHolidays,
        MenuKeys.HrmConfigShifts,
        MenuKeys.HrmConfigOtPolicies,
        MenuKeys.HrmConfigVehicles,
        MenuKeys.HrmConfigDrivers,
    };

    // Mọi key Hrm* dùng trong test (cần MenuItem row vì FK).
    private static readonly string[] AllHrmKeys =
        new[] { MenuKeys.Hrm, MenuKeys.HrmHoSo, MenuKeys.HrmDashboard }
            .Concat(ConfigKeys).ToArray();

    private static async Task InvokeRevokeAsync(IdentityFixture fix)
        => await InvokePrivateSeedAsync(fix, "RevokeTemporarilyHiddenModulesAsync");

    private static async Task InvokeHrmProfileSeedAsync(IdentityFixture fix)
        => await InvokePrivateSeedAsync(fix, "SeedAllRolesHrmProfileReadPermissionsAsync");

    // Reflection invoke private static (ApplicationDbContext, RoleManager<Role>, ILogger).
    private static async Task InvokePrivateSeedAsync(IdentityFixture fix, string methodName)
    {
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var rm = fix.Services.GetRequiredService<RoleManager<Role>>();
        var mi = typeof(DbInitializer).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
        mi.Should().NotBeNull($"DbInitializer.{methodName} phải tồn tại (private static) — đổi signature thì cập nhật test");
        var task = (Task)mi!.Invoke(null, new object[] { db, rm, NullLogger.Instance })!;
        await task;
    }

    // Ensure roles tồn tại (Admin + non-Admin). Trả (adminId, nonAdminId).
    private static async Task<(Guid adminId, Guid nonAdminId)> SeedRolesAsync(IdentityFixture fix)
    {
        var rm = fix.Services.GetRequiredService<RoleManager<Role>>();
        var admin = new Role { Id = Guid.NewGuid(), Name = AppRoles.Admin };
        var drafter = new Role { Id = Guid.NewGuid(), Name = AppRoles.Drafter };
        await rm.CreateAsync(admin);
        await rm.CreateAsync(drafter);
        return (admin.Id, drafter.Id);
    }

    // Seed MenuItem rows (FK target cho Permission.MenuKey).
    private static async Task SeedMenuItemsAsync(TestApplicationDbContext db)
    {
        foreach (var key in AllHrmKeys)
            db.MenuItems.Add(new MenuItem { Key = key, Label = key });
        await db.SaveChangesAsync(CancellationToken.None);
    }

    // Seed 1 Permission row.
    private static void AddPerm(TestApplicationDbContext db, Guid roleId, string menuKey, bool canRead)
        => db.Permissions.Add(new Permission
        {
            RoleId = roleId,
            MenuKey = menuKey,
            CanRead = canRead,
            CanCreate = false,
            CanUpdate = false,
            CanDelete = false,
        });

    private static async Task<bool> CanReadAsync(TestApplicationDbContext db, Guid roleId, string menuKey)
    {
        var row = await db.Permissions.AsNoTracking()
            .FirstOrDefaultAsync(p => p.RoleId == roleId && p.MenuKey == menuKey);
        return row?.CanRead ?? false;
    }

    // ============================================================
    // Full chain: pre-grant Hrm* → Revoke → HrmProfileSeed
    // ============================================================

    [Fact]
    public async Task Chain_NonAdmin_HrmAndHoSo_ReadTrue_DashboardAndConfig_StayHidden()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var (adminId, nonAdminId) = await SeedRolesAsync(fix);
        await SeedMenuItemsAsync(db);

        // PRE-STATE: non-Admin từng được grant CanRead=true trên MỌI Hrm* (mô phỏng
        // seed cũ) → để revoke có gì đó để thu hồi. Admin cũng có (sẽ KHÔNG bị revoke).
        foreach (var key in AllHrmKeys)
        {
            AddPerm(db, nonAdminId, key, canRead: true);
            AddPerm(db, adminId, key, canRead: true);
        }
        await db.SaveChangesAsync(CancellationToken.None);

        // CHAIN đúng thứ tự SeedAsync.
        await InvokeRevokeAsync(fix);
        await InvokeHrmProfileSeedAsync(fix);

        // non-Admin: Hrm + Hrm_HoSo mở lại (public Hồ sơ NS).
        (await CanReadAsync(db, nonAdminId, MenuKeys.Hrm)).Should().BeTrue("root Nhân sự mở lại cho user thường");
        (await CanReadAsync(db, nonAdminId, MenuKeys.HrmHoSo)).Should().BeTrue("Hồ sơ NS public read-only");

        // non-Admin: Dashboard + 6 config VẪN ẩn (revoke che, seed không nâng).
        (await CanReadAsync(db, nonAdminId, MenuKeys.HrmDashboard)).Should().BeFalse("Dashboard NS giữ ẩn");
        foreach (var key in ConfigKeys)
            (await CanReadAsync(db, nonAdminId, key)).Should().BeFalse($"{key} giữ ẩn (chỉ Admin)");
    }

    [Fact]
    public async Task Chain_Admin_NotRevoked_KeepsAllHrmReadAfterChain()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var (adminId, nonAdminId) = await SeedRolesAsync(fix);
        await SeedMenuItemsAsync(db);

        foreach (var key in AllHrmKeys)
        {
            AddPerm(db, adminId, key, canRead: true);
            AddPerm(db, nonAdminId, key, canRead: true);
        }
        await db.SaveChangesAsync(CancellationToken.None);

        await InvokeRevokeAsync(fix);
        await InvokeHrmProfileSeedAsync(fix);

        // Admin GIỮ nguyên CanRead=true trên mọi Hrm* (kể cả Dashboard + Config).
        foreach (var key in AllHrmKeys)
            (await CanReadAsync(db, adminId, key)).Should().BeTrue($"Admin KHÔNG bị revoke trên {key}");
    }

    // ============================================================
    // SeedAllRolesHrmProfileReadPermissionsAsync — isolated behavior
    // ============================================================

    [Fact]
    public async Task ProfileSeed_UpgradesExistingFalseRow_RaisesCanReadTrue()
    {
        // Upgrade-only path (line 2224): row đã tồn tại CanRead=false (revoke vừa set)
        // → nâng lên true. Mô phỏng: chỉ seed Hrm/Hrm_HoSo = false rồi gọi profile-seed.
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var (_, nonAdminId) = await SeedRolesAsync(fix);
        await SeedMenuItemsAsync(db);

        AddPerm(db, nonAdminId, MenuKeys.Hrm, canRead: false);
        AddPerm(db, nonAdminId, MenuKeys.HrmHoSo, canRead: false);
        await db.SaveChangesAsync(CancellationToken.None);

        await InvokeHrmProfileSeedAsync(fix);

        (await CanReadAsync(db, nonAdminId, MenuKeys.Hrm)).Should().BeTrue();
        (await CanReadAsync(db, nonAdminId, MenuKeys.HrmHoSo)).Should().BeTrue();
    }

    [Fact]
    public async Task ProfileSeed_CreatesMissingRow_ReadTrueOtherFlagsFalse()
    {
        // Row chưa có (DB mới) → tạo CanRead=true, Create/Update/Delete=false (line 2229).
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var (_, nonAdminId) = await SeedRolesAsync(fix);
        await SeedMenuItemsAsync(db);
        // KHÔNG seed Permission row nào cho Hrm/Hrm_HoSo.

        await InvokeHrmProfileSeedAsync(fix);

        var hrm = await db.Permissions.AsNoTracking()
            .FirstOrDefaultAsync(p => p.RoleId == nonAdminId && p.MenuKey == MenuKeys.Hrm);
        hrm.Should().NotBeNull("profile-seed tạo row mới khi chưa tồn tại");
        hrm!.CanRead.Should().BeTrue();
        hrm.CanCreate.Should().BeFalse();
        hrm.CanUpdate.Should().BeFalse();
        hrm.CanDelete.Should().BeFalse();
    }

    [Fact]
    public async Task ProfileSeed_DoesNotTouchDashboardOrConfigKeys()
    {
        // Profile-seed CHỈ đụng { Hrm, Hrm_HoSo } — KHÔNG tạo/nâng Dashboard hay Config*.
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var (_, nonAdminId) = await SeedRolesAsync(fix);
        await SeedMenuItemsAsync(db);
        // KHÔNG seed Permission rows nào.

        await InvokeHrmProfileSeedAsync(fix);

        // Dashboard + Config* không có row nào được tạo (CanRead mặc định false / no row).
        (await CanReadAsync(db, nonAdminId, MenuKeys.HrmDashboard)).Should().BeFalse();
        foreach (var key in ConfigKeys)
            (await CanReadAsync(db, nonAdminId, key)).Should().BeFalse();

        var dashRow = await db.Permissions.AsNoTracking()
            .FirstOrDefaultAsync(p => p.RoleId == nonAdminId && p.MenuKey == MenuKeys.HrmDashboard);
        dashRow.Should().BeNull("profile-seed KHÔNG tạo row cho Hrm_Dashboard");
    }

    // ============================================================
    // RevokeTemporarilyHiddenModulesAsync — isolated behavior
    // ============================================================

    [Fact]
    public async Task Revoke_NonAdmin_ClearsAllHrmReadFlags()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var (_, nonAdminId) = await SeedRolesAsync(fix);
        await SeedMenuItemsAsync(db);

        foreach (var key in AllHrmKeys)
            AddPerm(db, nonAdminId, key, canRead: true);
        await db.SaveChangesAsync(CancellationToken.None);

        await InvokeRevokeAsync(fix);

        // MỌI Hrm* về false cho non-Admin (gồm cả Hrm + Hrm_HoSo TRƯỚC khi profile-seed nâng lại).
        foreach (var key in AllHrmKeys)
            (await CanReadAsync(db, nonAdminId, key)).Should().BeFalse($"revoke thu hồi {key} khỏi non-Admin");
    }
}
