using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using SolutionErp.Api.Controllers;

namespace SolutionErp.Infrastructure.Tests.Api;

// Regression tests for gotcha #44 — Silent 403 từ class-level [Authorize(Policy = ...)]
// quá strict (Session 18, fix `f77ea38`).
//
// Bug ngày 2026-05-08: ApprovalWorkflowsV2Controller class-level
// `[Authorize(Policy = "Workflows.Read")]` → Drafter `nv.test` (chỉ có
// `PurchaseEvaluations.Read`) bị 403 silent khi GET /api/approval-workflows-v2.
// FE TanStack Query catch silent → Workspace dropdown empty không có error toast.
//
// Fix: split policy per action — class-level [Authorize] (any authenticated)
// cho list-pick GET, action-level [Authorize(Policy = "Workflows.Create")]
// cho POST/DELETE/PATCH admin-only.
//
// Regression coverage: nếu future ai đó add Policy="Workflows.Read" lên
// class-level OR add Policy lên GET action → test FAIL ngay, không cần UAT
// reproduce lại bug silent 403 mà FE catch không hiển thị.
public class AuthorizePolicyRegressionTests
{
    private static AuthorizeAttribute? GetClassLevelAuthorize(Type controllerType)
        => controllerType.GetCustomAttributes<AuthorizeAttribute>(inherit: false).FirstOrDefault();

    private static AuthorizeAttribute? GetActionAuthorize(Type controllerType, string methodName)
        => controllerType
            .GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
            .FirstOrDefault(m => m.Name == methodName)
            ?.GetCustomAttributes<AuthorizeAttribute>(inherit: false)
            .FirstOrDefault();

    [Fact]
    public void ApprovalWorkflowsV2Controller_ClassLevel_AuthorizeOnly_NoPolicy()
    {
        var attr = GetClassLevelAuthorize(typeof(ApprovalWorkflowsV2Controller));

        attr.Should().NotBeNull("controller phải có [Authorize] class-level để chặn anonymous");
        attr!.Policy.Should().BeNull(
            "class-level KHÔNG được hardcode Policy — sẽ block Drafter 403 silent khi GET. " +
            "Gotcha #44: ràng buộc per-action thay vì class-level uniform.");
        attr.Roles.Should().BeNull("class-level KHÔNG được hardcode Roles");
    }

    [Fact]
    public void ApprovalWorkflowsV2Controller_Overview_GET_InheritsClassLevel_NoActionPolicy()
    {
        // GET Overview KHÔNG được override với [Authorize(Policy=...)] — Drafter
        // cần list workflow để pick lúc create PE/HĐ (read-only, không expose
        // business data nhạy cảm).
        var attr = GetActionAuthorize(typeof(ApprovalWorkflowsV2Controller), nameof(ApprovalWorkflowsV2Controller.Overview));

        attr.Should().BeNull(
            "GET Overview phải inherit class-level [Authorize] (any authenticated). " +
            "Nếu add [Authorize(Policy=...)] action-level → Drafter 403 silent (gotcha #44).");
    }

    [Fact]
    public void ApprovalWorkflowsV2Controller_Create_POST_RequiresWorkflowsCreatePolicy()
    {
        // POST Create chỉ admin Designer được tạo workflow — phải có policy guard.
        var attr = GetActionAuthorize(typeof(ApprovalWorkflowsV2Controller), nameof(ApprovalWorkflowsV2Controller.Create));

        attr.Should().NotBeNull("POST Create phải có [Authorize(Policy = ...)] admin-only");
        attr!.Policy.Should().Be("Workflows.Create",
            "POST Create chỉ admin được tạo workflow mới (Mig 22 V2 Designer).");
    }

    [Fact]
    public void ApprovalWorkflowsV2Controller_Delete_RequiresWorkflowsCreatePolicy()
    {
        var attr = GetActionAuthorize(typeof(ApprovalWorkflowsV2Controller), nameof(ApprovalWorkflowsV2Controller.Delete));

        attr.Should().NotBeNull("DELETE phải có [Authorize(Policy = ...)] admin-only");
        attr!.Policy.Should().Be("Workflows.Create",
            "DELETE workflow chỉ admin (Designer).");
    }

    [Fact]
    public void ApprovalWorkflowsV2Controller_SetUserSelectable_PATCH_RequiresWorkflowsCreatePolicy()
    {
        // Mig 25 — admin toggle stick/unstick "cho user pick lúc create phiếu".
        var attr = GetActionAuthorize(typeof(ApprovalWorkflowsV2Controller), nameof(ApprovalWorkflowsV2Controller.SetUserSelectable));

        attr.Should().NotBeNull("PATCH user-selectable phải có [Authorize(Policy = ...)] admin-only");
        attr!.Policy.Should().Be("Workflows.Create",
            "PATCH user-selectable chỉ admin (Mig 25 Designer pin/unpin).");
    }

    // ===================================================================
    // Coverage gap #3 (MAJOR — S35/S36 backlog, closed S45 2026-06-01).
    // gotcha #44 regression cho 2 controller HRM bị MISS (chỉ ApprovalWorkflowsV2 có trước đó).
    //
    // 2 controller shape KHÁC nhau — lock đúng intent (KHÔNG ép về cùng 1 kiểu):
    //   - HrmConfigsController: class [Authorize] trần (any-authenticated GET dropdown), writes Admin-role.
    //   - EmployeesController: class Policy-gated "Hrm_HoSo.Read" + per-action Create/Update/Delete policy.
    // ===================================================================

    // ---------- HrmConfigsController (class [Authorize] trần + writes Admin-role) ----------

    [Fact]
    public void HrmConfigsController_ClassLevel_AuthorizeOnly_NoPolicy_NoRoles()
    {
        // Class-level [Authorize] trần — GET (leave-types/holidays/shifts/ot-policies) phải mở cho
        // MỌI authenticated user (dropdown master data). KHÔNG được hardcode Policy/Roles ở class-level
        // → sẽ block role không-Admin 403 silent khi GET (gotcha #44).
        var attr = GetClassLevelAuthorize(typeof(HrmConfigsController));

        attr.Should().NotBeNull("controller phải có [Authorize] class-level chặn anonymous");
        attr!.Policy.Should().BeNull("class-level KHÔNG được hardcode Policy — GET dropdown cho mọi authenticated");
        attr.Roles.Should().BeNull("class-level KHÔNG được hardcode Roles — chỉ writes mới gắn Roles=Admin");
    }

    [Fact]
    public void HrmConfigsController_ListHolidays_GET_InheritsClassLevel_NoActionAttr()
    {
        // GET list KHÔNG được override [Authorize(...)] action-level — mọi authenticated user
        // cần đọc cho dropdown (gotcha #44 silent 403 prevention).
        var attr = GetActionAuthorize(typeof(HrmConfigsController), nameof(HrmConfigsController.ListHolidays));

        attr.Should().BeNull("GET ListHolidays phải inherit class-level [Authorize] (any authenticated)");
    }

    [Fact]
    public void HrmConfigsController_CreateHoliday_POST_RequiresAdminRole()
    {
        var attr = GetActionAuthorize(typeof(HrmConfigsController), nameof(HrmConfigsController.CreateHoliday));

        attr.Should().NotBeNull("POST CreateHoliday phải có [Authorize(Roles = ...)] write-guard");
        attr!.Roles.Should().Be("Admin", "ghi Holiday chỉ Admin (role-based, KHÔNG policy).");
        attr.Policy.Should().BeNull("HrmConfigs writes dùng Roles=Admin, KHÔNG dùng Policy.");
    }

    [Fact]
    public void HrmConfigsController_UpdateHoliday_PUT_RequiresAdminRole()
    {
        var attr = GetActionAuthorize(typeof(HrmConfigsController), nameof(HrmConfigsController.UpdateHoliday));

        attr.Should().NotBeNull("PUT UpdateHoliday phải có [Authorize(Roles = ...)] write-guard");
        attr!.Roles.Should().Be("Admin");
    }

    [Fact]
    public void HrmConfigsController_DeleteHoliday_DELETE_RequiresAdminRole()
    {
        var attr = GetActionAuthorize(typeof(HrmConfigsController), nameof(HrmConfigsController.DeleteHoliday));

        attr.Should().NotBeNull("DELETE DeleteHoliday phải có [Authorize(Roles = ...)] write-guard");
        attr!.Roles.Should().Be("Admin");
    }

    // ---------- EmployeesController (class Policy-gated + per-action policy) ----------

    [Fact]
    public void EmployeesController_ClassLevel_RequiresHrmHoSoReadPolicy()
    {
        // Class-level Policy "Hrm_HoSo.Read" — KHÁC HrmConfigs (hồ sơ NS nhạy cảm hơn dropdown).
        // Lock intent: role thiếu Read sẽ 403 (FE PermissionGuard wrap để tránh silent UX — gotcha #44).
        var attr = GetClassLevelAuthorize(typeof(EmployeesController));

        attr.Should().NotBeNull("EmployeesController phải có [Authorize] class-level");
        attr!.Policy.Should().Be("Hrm_HoSo.Read",
            "hồ sơ nhân sự gate bằng policy Read class-level (KHÁC HrmConfigs any-authenticated).");
    }

    [Fact]
    public void EmployeesController_Create_POST_RequiresHrmHoSoCreatePolicy()
    {
        var attr = GetActionAuthorize(typeof(EmployeesController), nameof(EmployeesController.Create));

        attr.Should().NotBeNull("POST Create phải override action-level policy");
        attr!.Policy.Should().Be("Hrm_HoSo.Create");
    }

    [Fact]
    public void EmployeesController_Update_PUT_RequiresHrmHoSoUpdatePolicy()
    {
        var attr = GetActionAuthorize(typeof(EmployeesController), nameof(EmployeesController.Update));

        attr.Should().NotBeNull("PUT Update phải override action-level policy");
        attr!.Policy.Should().Be("Hrm_HoSo.Update");
    }

    [Fact]
    public void EmployeesController_Delete_DELETE_RequiresHrmHoSoDeletePolicy()
    {
        var attr = GetActionAuthorize(typeof(EmployeesController), nameof(EmployeesController.Delete));

        attr.Should().NotBeNull("DELETE Delete phải override action-level policy");
        attr!.Policy.Should().Be("Hrm_HoSo.Delete");
    }

    [Fact]
    public void EmployeesController_CreateWorkHistory_Satellite_RequiresHrmHoSoCreatePolicy()
    {
        // 1 satellite representative — satellite write inherit cùng Create policy như parent.
        var attr = GetActionAuthorize(typeof(EmployeesController), nameof(EmployeesController.CreateWorkHistory));

        attr.Should().NotBeNull("POST satellite CreateWorkHistory phải có action-level policy");
        attr!.Policy.Should().Be("Hrm_HoSo.Create");
    }
}
