import { describe, test, expect, mock, beforeEach } from "bun:test";

// The memberCache module depends on constants (prisma + redis) which are mocked
// in setup.ts. We can import the functions and test their pure-logic paths.

import {
  resolveMemberName,
  setMemberCache,
  getMemberCache,
  resolveMember,
} from "../../src/modules/cw-utils/members/memberCache";
import { Collection } from "@discordjs/collection";
import type { CWMember } from "../../src/modules/cw-utils/members/fetchAllMembers";

function buildTestMember(overrides: Partial<CWMember> = {}): CWMember {
  return {
    id: 10,
    identifier: "jroberts",
    firstName: "John",
    lastName: "Roberts",
    officeEmail: "john@test.com",
    inactiveFlag: false,
    _info: {},
    ...overrides,
  };
}

describe("memberCache", () => {
  beforeEach(async () => {
    // Reset cache to empty before each test
    await setMemberCache(new Collection<string, CWMember>());
  });

  describe("setMemberCache / getMemberCache", () => {
    test("stores and retrieves members", async () => {
      const members = new Collection<string, CWMember>();
      members.set("jroberts", buildTestMember());
      members.set(
        "asmith",
        buildTestMember({
          id: 20,
          identifier: "asmith",
          firstName: "Alice",
          lastName: "Smith",
        }),
      );

      await setMemberCache(members);
      const cached = await getMemberCache();

      expect(cached.size).toBe(2);
      expect(cached.get("jroberts")?.firstName).toBe("John");
      expect(cached.get("asmith")?.firstName).toBe("Alice");
    });

    test("empty cache returns empty collection", async () => {
      const cached = await getMemberCache();
      // May be empty or hydrated from redis mock (which returns null)
      expect(cached.size).toBe(0);
    });
  });

  describe("resolveMemberName", () => {
    test("returns full name when member exists", async () => {
      const members = new Collection<string, CWMember>();
      members.set("jroberts", buildTestMember());
      await setMemberCache(members);

      expect(resolveMemberName("jroberts")).toBe("John Roberts");
    });

    test("returns raw identifier when member not found", () => {
      expect(resolveMemberName("unknown-user")).toBe("unknown-user");
    });

    test("falls back to identifier if name parts are empty", async () => {
      const members = new Collection<string, CWMember>();
      members.set(
        "empty",
        buildTestMember({ identifier: "empty", firstName: "", lastName: "" }),
      );
      await setMemberCache(members);

      expect(resolveMemberName("empty")).toBe("empty");
    });
  });

  describe("resolveMember", () => {
    test("returns resolved member with local user id null when no local user", async () => {
      const members = new Collection<string, CWMember>();
      members.set("jroberts", buildTestMember());
      await setMemberCache(members);

      const resolved = await resolveMember("jroberts");

      expect(resolved.identifier).toBe("jroberts");
      expect(resolved.name).toBe("John Roberts");
      expect(resolved.cwMemberId).toBe(10);
      // prisma.user.findFirst is mocked to return null
      expect(resolved.id).toBeNull();
    });

    test("returns fallback values when member not in cache", async () => {
      const resolved = await resolveMember("unknown");

      expect(resolved.identifier).toBe("unknown");
      expect(resolved.name).toBe("unknown");
      expect(resolved.cwMemberId).toBeNull();
      expect(resolved.id).toBeNull();
    });
  });
});
