/**
 * 纯权限 reducer 单测(契约 P6 / D11 / 2.3)。
 * 矩阵覆盖:个人/Group/祖先继承/max 取最高/不降权/空权限/toggle 冻结;
 * 外加确定性随机化不变量(单调性:任何可用 grant 都不超过 effective)。
 */
import { describe, expect, it } from "vitest";
import { checkAccess, effectiveRole, type EffectiveRoleInput, type FileLibGrantFact } from "../../src/database/filelib/permission.js";

const base: EffectiveRoleInput = {
  nodeId: "N",
  nodeKind: "FOLDER",
  ancestorIds: ["A", "R"], // N ⊂ A ⊂ R
  independentPermissionsEnabled: false,
  userId: "u1",
  groupIds: ["g1"],
  grants: [],
};

function grant(partial: Partial<FileLibGrantFact>): FileLibGrantFact {
  return {
    nodeId: "N",
    principalType: "USER",
    principalId: "u1",
    role: "VIEW",
    isCreatorGrant: false,
    ...partial,
  };
}

describe("effectiveRole · 契约 P6 矩阵", () => {
  it("无任何 grant → null(无权限)", () => {
    expect(effectiveRole(base)).toBeNull();
  });

  it("个人直接 grant 在 self 上 → 生效", () => {
    expect(effectiveRole({ ...base, grants: [grant({ role: "EDIT" })] })).toBe("EDIT");
  });

  it("Group grant 且用户在组内 → 生效", () => {
    expect(effectiveRole({ ...base, grants: [grant({ principalType: "GROUP", principalId: "g1", role: "MANAGE" })] })).toBe("MANAGE");
  });

  it("Group grant 但用户不在组内 → null", () => {
    expect(effectiveRole({ ...base, grants: [grant({ principalType: "GROUP", principalId: "g9" })] })).toBeNull();
  });

  it("祖先链上的 grant 向下继承", () => {
    expect(effectiveRole({ ...base, grants: [grant({ nodeId: "A", role: "EDIT" })] })).toBe("EDIT");
    expect(effectiveRole({ ...base, grants: [grant({ nodeId: "R", role: "VIEW" })] })).toBe("VIEW");
  });

  it("取最高:个人低权限 + Group 高权限 → 高权限(个人不能降权)", () => {
    const grants = [
      grant({ role: "VIEW" }),
      grant({ principalType: "GROUP", principalId: "g1", role: "MANAGE" }),
    ];
    expect(effectiveRole({ ...base, grants })).toBe("MANAGE");
  });

  it("取最高:链上多处 grant,最高者胜", () => {
    const grants = [
      grant({ nodeId: "R", role: "MANAGE" }),
      grant({ nodeId: "A", role: "VIEW" }),
      grant({ nodeId: "N", role: "EDIT" }),
    ];
    expect(effectiveRole({ ...base, grants })).toBe("MANAGE");
  });

  it("链外节点的 grant 不参与", () => {
    expect(effectiveRole({ ...base, grants: [grant({ nodeId: "X", role: "MANAGE" })] })).toBeNull();
  });

  it("他人的个人 grant 不参与", () => {
    expect(effectiveRole({ ...base, grants: [grant({ principalId: "u2", role: "MANAGE" })] })).toBeNull();
  });
});

describe("effectiveRole · D11 独立权限开关", () => {
  const project: EffectiveRoleInput = { ...base, nodeKind: "PROJECT", nodeId: "P" };

  it("开关关闭:项目级非创建者 grant 冻结", () => {
    const grants = [grant({ nodeId: "P", role: "EDIT" })];
    expect(effectiveRole({ ...project, grants })).toBeNull();
  });

  it("开关关闭:创建者 grant 仍生效", () => {
    const grants = [grant({ nodeId: "P", role: "MANAGE", isCreatorGrant: true })];
    expect(effectiveRole({ ...project, grants })).toBe("MANAGE");
  });

  it("开关关闭:祖先链 grant 不受影响", () => {
    const grants = [
      grant({ nodeId: "P", role: "MANAGE" }), // 冻结
      grant({ nodeId: "A", role: "VIEW" }),   // 生效
    ];
    expect(effectiveRole({ ...project, grants })).toBe("VIEW");
  });

  it("开关开启:项目级 grant 恢复参与", () => {
    const grants = [grant({ nodeId: "P", role: "EDIT" })];
    expect(effectiveRole({ ...project, independentPermissionsEnabled: true, grants })).toBe("EDIT");
  });

  it("文件夹忽略开关(self grant 照常参与)", () => {
    const grants = [grant({ nodeId: "N", role: "EDIT" })];
    expect(effectiveRole({ ...base, grants })).toBe("EDIT");
  });
});

describe("effectiveRole · 随机化不变量", () => {
  // 确定性 LCG,替代 property-based 框架(不新增依赖)。
  function lcg(seed: number): () => number {
    let s = seed;
    return () => {
      s = (s * 1103515245 + 12345) % 2147483648;
      return s / 2147483648;
    };
  }
  const roles = ["VIEW", "EDIT", "MANAGE"] as const;

  it("effective 恰为所有可用 grant 的最高秩(单调、顺序无关)", () => {
    const rand = lcg(42);
    for (let round = 0; round < 200; round += 1) {
      const nodes = ["N", "A", "R", "X"];
      const principals = ["u1", "u2", "g1", "g9"];
      const grants: FileLibGrantFact[] = Array.from({ length: Math.floor(rand() * 12) }, () => {
        const principalId = principals[Math.floor(rand() * principals.length)]!;
        return grant({
          nodeId: nodes[Math.floor(rand() * nodes.length)]!,
          principalType: principalId.startsWith("g") ? "GROUP" : "USER",
          principalId,
          role: roles[Math.floor(rand() * roles.length)]!,
        });
      });
      const input: EffectiveRoleInput = { ...base, grants };
      const applicableRanks = grants
        .filter((g) => ["N", "A", "R"].includes(g.nodeId))
        .filter((g) => (g.principalType === "USER" ? g.principalId === "u1" : g.principalId === "g1"))
        .map((g) => ({ VIEW: 1, EDIT: 2, MANAGE: 3 })[g.role]);
      const expected = applicableRanks.length === 0 ? 0 : Math.max(...applicableRanks);
      const actual = effectiveRole(input);
      expect(actual === null ? 0 : { VIEW: 1, EDIT: 2, MANAGE: 3 }[actual]).toBe(expected);
      // 顺序无关
      expect(effectiveRole({ ...input, grants: [...grants].reverse() })).toBe(actual);
    }
  });
});

describe("checkAccess · D8 语义", () => {
  it("无权限 → not_found(404 不泄露)", () => {
    expect(checkAccess(null, "VIEW")).toEqual({ allowed: false, reason: "not_found" });
  });
  it("权限不足 → forbidden(403)", () => {
    expect(checkAccess("VIEW", "EDIT")).toEqual({ allowed: false, reason: "forbidden" });
  });
  it("权限足够 → allowed 并带回实际角色", () => {
    expect(checkAccess("MANAGE", "EDIT")).toEqual({ allowed: true, role: "MANAGE" });
  });
});
