// #region PermissionsTest [C:3] [TYPE Module] [SEMANTICS auth, rbac, permission, test, guard]
// @BRIEF Verifies frontend RBAC permission parsing and access checks.
// @RELATION DEPENDS_ON -> [PermissionsModule]

// #region PermissionsTest:Module [TYPE Function]
// @SEMANTICS: tests, auth, permissions, rbac
// @PURPOSE: Verifies frontend RBAC permission parsing and access checks.
// @LAYER UI (Tests)
// @RELATION DEPENDS_ON -> [PermissionsModule]

import { describe, it, expect } from "vitest";
import {
  normalizePermissionRequirement,
  isAdminUser,
  hasPermission,
} from "../permissions";

interface User {
  roles?: Array<{
    name?: string;
    permissions?: Array<string | { resource?: string; action?: string }>;
  }>;
}

describe("auth.permissions", () => {
  it("normalizes resource-only requirement with default READ action", () => {
    expect(normalizePermissionRequirement("admin:settings")).toEqual({
      resource: "admin:settings",
      action: "READ",
    });
  });

  it("normalizes explicit resource:action requirement", () => {
    expect(normalizePermissionRequirement("admin:settings:write")).toEqual({
      resource: "admin:settings",
      action: "WRITE",
    });
  });

  it("detects admin role case-insensitively", () => {
    const user: User = {
      roles: [{ name: "ADMIN" }],
    };
    expect(isAdminUser(user)).toBe(true);
  });

  it("denies when user is absent and permission is required", () => {
    expect(hasPermission(null, "tasks", "READ")).toBe(false);
  });

  it("grants when permission object matches resource and action", () => {
    const user: User = {
      roles: [
        {
          name: "Operator",
          permissions: [{ resource: "tasks", action: "READ" }],
        },
      ],
    };

    expect(hasPermission(user, "tasks", "READ")).toBe(true);
  });

  it("grants when requirement is provided as resource:action", () => {
    const user: User = {
      roles: [
        {
          name: "Operator",
          permissions: [{ resource: "admin:settings", action: "READ" }],
        },
      ],
    };

    expect(hasPermission(user, "admin:settings:READ")).toBe(true);
  });

  it("grants when string permission entry matches", () => {
    const user: User = {
      roles: [
        {
          name: "Operator",
          permissions: ["plugin:migration:READ"],
        },
      ],
    };

    expect(hasPermission(user, "plugin:migration", "READ")).toBe(true);
  });

  it("denies when action does not match", () => {
    const user: User = {
      roles: [
        {
          name: "Operator",
          permissions: [{ resource: "tasks", action: "READ" }],
        },
      ],
    };

    expect(hasPermission(user, "tasks", "WRITE")).toBe(false);
  });

  it("always grants for admin role regardless of explicit permissions", () => {
    const adminUser: User = {
      roles: [{ name: "Admin", permissions: [] }],
    };

    expect(hasPermission(adminUser, "admin:users", "READ")).toBe(true);
    expect(hasPermission(adminUser, "plugin:migration", "EXECUTE")).toBe(true);
  });

  it("normalizes empty permission to null resource with default action", () => {
    expect(normalizePermissionRequirement("")).toEqual({
      resource: null,
      action: "READ",
    });
  });

  it("normalizes whitespace-only permission to null resource", () => {
    expect(normalizePermissionRequirement("   ")).toEqual({
      resource: null,
      action: "READ",
    });
  });

  it("treats non-KNOWN_ACTION suffix as part of resource", () => {
    expect(normalizePermissionRequirement("admin:settings:view")).toEqual({
      resource: "admin:settings:view",
      action: "READ",
    });
  });

  it("returns false for isAdminUser with null", () => {
    expect(isAdminUser(null)).toBe(false);
  });

  it("returns false for isAdminUser with undefined", () => {
    expect(isAdminUser(undefined)).toBe(false);
  });

  it("returns false for isAdminUser with empty roles array", () => {
    expect(isAdminUser({ roles: [] })).toBe(false);
  });

  it("returns false for isAdminUser with role missing name", () => {
    expect(isAdminUser({ roles: [{ permissions: [] }] })).toBe(false);
  });

  it("returns true when requirement is empty", () => {
    const user: User = {
      roles: [{ name: "Operator", permissions: [] }],
    };
    expect(hasPermission(user, "")).toBe(true);
  });

  it("returns true when requirement is whitespace", () => {
    const user: User = {
      roles: [{ name: "Operator", permissions: [] }],
    };
    expect(hasPermission(user, "   ")).toBe(true);
  });

  it("grants when string permission matches resource:action format", () => {
    const user: User = {
      roles: [
        {
          name: "Editor",
          permissions: ["datasets:WRITE"],
        },
      ],
    };
    expect(hasPermission(user, "datasets", "WRITE")).toBe(true);
  });

  it("denies when user is undefined", () => {
    expect(hasPermission(undefined, "tasks", "READ")).toBe(false);
  });

  it("denies when user has roles but no matching permission", () => {
    const user: User = {
      roles: [{ name: "Viewer", permissions: [{ resource: "reports", action: "READ" }] }],
    };
    expect(hasPermission(user, "admin:users", "EXECUTE")).toBe(false);
  });

  it("grants when matching permission is in second role", () => {
    const user: User = {
      roles: [
        { name: "Viewer", permissions: [{ resource: "reports", action: "READ" }] },
        { name: "Editor", permissions: [{ resource: "tasks", action: "WRITE" }] },
      ],
    };
    expect(hasPermission(user, "tasks", "WRITE")).toBe(true);
  });

  it("treats DENY as part of resource (not in KNOWN_ACTIONS)", () => {
    expect(normalizePermissionRequirement("secrets:DENY")).toEqual({
      resource: "secrets:DENY",
      action: "READ",
    });
  });

	it("normalizes lowercase action to uppercase", () => {
		expect(normalizePermissionRequirement("files:read")).toEqual({
			resource: "files",
			action: "READ",
		});
	});

	// ── Edge cases for hasPermission ─────────────────────────────

	it("grants when requirement normalizes to null resource (empty requ)", () => {
		const user: User = { roles: [] };
		// normalizePermissionRequirement('') gives null resource
		// hasPermission catches !requirement first → returns true
		expect(hasPermission(user, "")).toBe(true);
	});

	it("throws/denies when string permission does not match requirement", () => {
		const user: User = {
			roles: [
				{
					name: "Viewer",
					permissions: ["reports:WRITE"],
				},
			],
		};
		// String permission "reports:WRITE" doesn't match requirement "reports:READ"
		expect(hasPermission(user, "reports", "READ")).toBe(false);
	});

	it("iterates multiple permissions in a role until match or exhaustion", () => {
		const user: User = {
			roles: [
				{
					name: "Mixed",
					permissions: [
						"reports:READ",
						{ resource: "tasks", action: "WRITE" },
						"datasets:EXECUTE",
					],
				},
			],
		};
		// Second permission matches
		expect(hasPermission(user, "tasks", "WRITE")).toBe(true);
	});

	it("skips String permission that does not match by action", () => {
		const user: User = {
			roles: [
				{
					name: "Editor",
					permissions: ["datasets:READ"],
				},
			],
		};
		// String permission "datasets:READ" has resource=datasets but action=READ != WRITE
		expect(hasPermission(user, "datasets", "WRITE")).toBe(false);
	});

	it("skips String permission when resource does not match", () => {
		const user: User = {
			roles: [
				{
					name: "Manager",
					permissions: ["other:WRITE"],
				},
			],
		};
		// String permission "other:WRITE" has resource=other != datasets
		expect(hasPermission(user, "datasets", "WRITE")).toBe(false);
	});

	it("handles permission string without action (defaults to READ)", () => {
		const user: User = {
			roles: [
				{
					name: "Reader",
					permissions: ["datasets"],
				},
			],
		};
		// hasPermission normalizes "datasets" to {resource: "datasets", action: "READ"}
		// The user's permission "datasets" also normalizes to {resource: "datasets", action: "READ"}
		expect(hasPermission(user, "datasets", "READ")).toBe(true);
	});

	it("handles EXECUTE action from KNOWN_ACTIONS", () => {
		expect(normalizePermissionRequirement("scripts:EXECUTE")).toEqual({
			resource: "scripts",
			action: "EXECUTE",
		});
	});

	it("handles DELETE action from KNOWN_ACTIONS", () => {
		expect(normalizePermissionRequirement("items:DELETE")).toEqual({
			resource: "items",
			action: "DELETE",
		});
	});

	// ── Edge cases: non-array roles and permissions ───────────────

	it("handles user.roles being null (falls back to empty array)", () => {
		// Line 91: Array.isArray(null) → false → roles = []
		expect(hasPermission({ roles: null as any }, "tasks", "READ")).toBe(false);
	});

	it("handles role.permissions being null (falls back to empty array)", () => {
		// Line 93: Array.isArray(null) → false → permissions = []
		expect(hasPermission({ roles: [{ name: "Viewer", permissions: null as any }] }, "tasks", "READ")).toBe(false);
	});

	it("handles role.permissions being undefined (falls back to empty array)", () => {
		// Line 93: Array.isArray(undefined) → false → permissions = []
		const user: User = { roles: [{ name: "Viewer" }] };
		expect(hasPermission(user, "tasks", "READ")).toBe(false);
	});

	// ── Edge cases: PermissionObject with missing fields (lines 109-110) ──

	it("handles PermissionObject with empty resource", () => {
		// Line 109: String("" || "") → ""
		const user: User = {
			roles: [{ name: "Editor", permissions: [{ resource: "", action: "READ" }] }],
		};
		// Empty resource won't match "tasks"
		expect(hasPermission(user, "tasks", "READ")).toBe(false);
	});

	it("handles PermissionObject with undefined action (empty fallback)", () => {
		// Line 110: normalizeAction(undefined || "", "") → "" (fallback is empty string)
		// Empty action does NOT match required "READ", so permission check fails
		const user: User = {
			roles: [{ name: "Editor", permissions: [{ resource: "tasks", action: undefined as any }] }],
		};
		expect(hasPermission(user, "tasks", "READ")).toBe(false);
	});

	it("handles PermissionObject with null resource", () => {
		// Line 109: String(null || "") → ""
		const user: User = {
			roles: [{ name: "Operator", permissions: [{ resource: null as any, action: "WRITE" }] }],
		};
		expect(hasPermission(user, "tasks", "WRITE")).toBe(false);
	});
});

// #endregion PermissionsTest:Module
// #endregion PermissionsTest
