import { describe, expect, it, vi } from "vitest";

import { LoginStatus } from "@/data/schemas/auth";
import { Result } from "@/utils/result";

import {
  buildChatConversationKey,
  resolveChatCacheOwnerKey,
} from "../chat_cache_identity";

function createStorageState(input: {
  provider: (typeof LoginStatus)[keyof typeof LoginStatus] | null;
  userId?: string | null;
  deviceId?: string | null;
}) {
  return {
    authStorage: {
      getLoginProvider: vi.fn(async () => Result.ok(input.provider)),
      getDeviceId: vi.fn(async () => Result.ok(input.deviceId ?? null)),
    },
    userStorage: {
      getUserId: vi.fn(async () => Result.ok(input.userId ?? null)),
    },
  };
}

describe("chat cache identity", () => {
  it("isolates the same owner by character", () => {
    const elio = buildChatConversationKey("user:account-1", "elio");
    const aria = buildChatConversationKey("user:account-1", "character_aria");

    expect(elio).toBe("user:account-1::character:elio");
    expect(aria).toBe("user:account-1::character:character_aria");
    expect(elio).not.toBe(aria);
  });

  it("uses the current user id for authenticated and guest sessions", async () => {
    const authenticated = createStorageState({
      provider: LoginStatus.Email,
      userId: "account-1",
      deviceId: "shared-device",
    });
    const guest = createStorageState({
      provider: LoginStatus.Guest,
      userId: "guest-1",
      deviceId: "shared-device",
    });

    await expect(
      resolveChatCacheOwnerKey(
        authenticated.authStorage,
        authenticated.userStorage,
      ),
    ).resolves.toEqual(Result.ok("user:account-1"));
    await expect(
      resolveChatCacheOwnerKey(guest.authStorage, guest.userStorage),
    ).resolves.toEqual(Result.ok("user:guest-1"));
  });

  it("uses a device namespace only when the active session is guest", async () => {
    const guest = createStorageState({
      provider: LoginStatus.Guest,
      deviceId: "guest-device",
    });
    const authenticated = createStorageState({
      provider: LoginStatus.Google,
      deviceId: "shared-device",
    });

    await expect(
      resolveChatCacheOwnerKey(guest.authStorage, guest.userStorage),
    ).resolves.toEqual(Result.ok("device:guest-device"));

    const authenticatedResult = await resolveChatCacheOwnerKey(
      authenticated.authStorage,
      authenticated.userStorage,
    );
    expect(Result.isErr(authenticatedResult)).toBe(true);
    expect(authenticated.authStorage.getDeviceId).not.toHaveBeenCalled();
  });

  it("does not expose a cache while logged out", async () => {
    const loggedOut = createStorageState({
      provider: LoginStatus.NotLoggedIn,
      userId: "stale-user",
      deviceId: "shared-device",
    });

    const result = await resolveChatCacheOwnerKey(
      loggedOut.authStorage,
      loggedOut.userStorage,
    );

    expect(Result.isErr(result)).toBe(true);
    expect(loggedOut.userStorage.getUserId).not.toHaveBeenCalled();
    expect(loggedOut.authStorage.getDeviceId).not.toHaveBeenCalled();
  });
});
