/**
 * Tests for src/lib/codeowners.ts's `requiredOwnersApproved()` — the fix for
 * CODEOWNERS approval being advisory-only. Before this, `reviewersForChangedFiles()`
 * was only used to auto-*request* reviewers on PR open; nothing at merge time
 * checked whether a CODEOWNERS-designated reviewer actually approved.
 *
 * `getCodeownersForRepo` and `loadApprovedUsernames` are passed in via the
 * injectable `deps` param (same DI rationale as push-workflow-sync.ts /
 * pr-workflow-sync.ts): the real implementations touch ../git/repository and
 * ../db respectively, both imported by dozens of unrelated test files, so a
 * global mock.module() on either would leak across the whole `bun test` run.
 * `matchOwners` / `expandOwnerTokens` (pure + already covered by
 * green-ecosystem.test.ts) run for real — only usernames (no team tokens)
 * are used here so `expandOwnerTokens` never touches the DB.
 */

import { describe, expect, it } from "bun:test";
import { requiredOwnersApproved } from "../lib/codeowners";
import type { OwnerRule } from "../lib/codeowners";

function deps(rules: OwnerRule[], approved: Set<string>) {
  return {
    getCodeownersForRepo: async () => rules,
    loadApprovedUsernames: async () => approved,
  };
}

describe("requiredOwnersApproved", () => {
  it("is satisfied when there is no CODEOWNERS file", async () => {
    const result = await requiredOwnersApproved(
      "acme",
      "widgets",
      "main",
      "pr-1",
      ["src/api/foo.ts"],
      deps([], new Set())
    );
    expect(result).toEqual({ satisfied: true, missingOwners: [] });
  });

  it("is satisfied when CODEOWNERS exists but matches none of the changed files", async () => {
    const rules: OwnerRule[] = [{ pattern: "docs/**", owners: ["alice"] }];
    const result = await requiredOwnersApproved(
      "acme",
      "widgets",
      "main",
      "pr-1",
      ["src/api/foo.ts"],
      deps(rules, new Set())
    );
    expect(result).toEqual({ satisfied: true, missingOwners: [] });
  });

  it("blocks when the required owner has not approved", async () => {
    const rules: OwnerRule[] = [{ pattern: "src/api/**", owners: ["alice"] }];
    const result = await requiredOwnersApproved(
      "acme",
      "widgets",
      "main",
      "pr-1",
      ["src/api/foo.ts"],
      deps(rules, new Set())
    );
    expect(result.satisfied).toBe(false);
    expect(result.missingOwners).toEqual(["alice"]);
  });

  it("is satisfied when the required owner has approved", async () => {
    const rules: OwnerRule[] = [{ pattern: "src/api/**", owners: ["alice"] }];
    const result = await requiredOwnersApproved(
      "acme",
      "widgets",
      "main",
      "pr-1",
      ["src/api/foo.ts"],
      deps(rules, new Set(["alice"]))
    );
    expect(result).toEqual({ satisfied: true, missingOwners: [] });
  });

  it("reports only the owners who haven't approved out of several", async () => {
    const rules: OwnerRule[] = [
      { pattern: "src/api/**", owners: ["alice", "bob"] },
    ];
    const result = await requiredOwnersApproved(
      "acme",
      "widgets",
      "main",
      "pr-1",
      ["src/api/foo.ts"],
      deps(rules, new Set(["alice"]))
    );
    expect(result.satisfied).toBe(false);
    expect(result.missingOwners).toEqual(["bob"]);
  });

  it("fails open (satisfied: true) when getCodeownersForRepo throws", async () => {
    const result = await requiredOwnersApproved(
      "acme",
      "widgets",
      "main",
      "pr-1",
      ["src/api/foo.ts"],
      {
        getCodeownersForRepo: async () => {
          throw new Error("git boom");
        },
        loadApprovedUsernames: async () => new Set(),
      }
    );
    expect(result).toEqual({ satisfied: true, missingOwners: [] });
  });

  it("fails open (satisfied: true) when loadApprovedUsernames throws", async () => {
    const rules: OwnerRule[] = [{ pattern: "src/api/**", owners: ["alice"] }];
    const result = await requiredOwnersApproved(
      "acme",
      "widgets",
      "main",
      "pr-1",
      ["src/api/foo.ts"],
      {
        getCodeownersForRepo: async () => rules,
        loadApprovedUsernames: async () => {
          throw new Error("db boom");
        },
      }
    );
    expect(result).toEqual({ satisfied: true, missingOwners: [] });
  });

  it("is satisfied when no files changed match any rule across multiple paths", async () => {
    const rules: OwnerRule[] = [{ pattern: "/docs", owners: ["carol"] }];
    const result = await requiredOwnersApproved(
      "acme",
      "widgets",
      "main",
      "pr-1",
      ["src/a.ts", "src/b.ts"],
      deps(rules, new Set())
    );
    expect(result).toEqual({ satisfied: true, missingOwners: [] });
  });
});
