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

import { ChatMessage, ChatMessageSchema } from "@/data/schemas/chat";
import { LocalMessage } from "@/data/storage/chat";
import { Result } from "@/utils/result";

import { ChatLocalMessageStore } from "../chat_local_message_store";

function makeMessage(id: string, content: string): ChatMessage {
  return ChatMessageSchema.parse({
    id,
    role: "assistant",
    type: "text",
    content,
    createdAt: "2026-07-13T00:00:00.000Z",
    audioUrl: null,
    image: { type: null, url: null },
    lockDetail: { locked: false, reason: null },
  });
}

function createMemoryStorage() {
  const buckets = new Map<string, LocalMessage[]>();
  return {
    buckets,
    storage: {
      saveMessage: vi.fn(async (message: LocalMessage) => {
        const messages = buckets.get(message.sessionId) ?? [];
        buckets.set(message.sessionId, [...messages, message]);
        return Result.ok(undefined);
      }),
      replaceMessagesBySession: vi.fn(
        async (identity: string, messages: readonly LocalMessage[]) => {
          buckets.set(identity, [...messages]);
          return Result.ok(undefined);
        },
      ),
      getAllMessagesBySession: vi.fn(async (identity: string) =>
        Result.ok([...(buckets.get(identity) ?? [])]),
      ),
      clearMessagesBySession: vi.fn(async (identity: string) => {
        buckets.delete(identity);
        return Result.ok(undefined);
      }),
      getMessageCountBySession: vi.fn(async (identity: string) =>
        Result.ok(buckets.get(identity)?.length ?? 0),
      ),
    },
  };
}

describe("ChatLocalMessageStore identity isolation", () => {
  it("reads, replaces, and clears only the current identity", async () => {
    const memory = createMemoryStorage();
    let identity = "user:account-a";
    const store = new ChatLocalMessageStore(memory.storage, async () =>
      Result.ok(identity),
    );

    await store.saveMessages([makeMessage("a-1", "account A")]);

    identity = "user:account-b";
    await expect(store.getMessages()).resolves.toEqual(Result.ok([]));
    await store.saveMessages([makeMessage("b-1", "account B")]);
    await store.clearMessages();

    identity = "user:account-a";
    const accountAResult = await store.getMessages();
    expect(Result.isOk(accountAResult) && accountAResult.data).toMatchObject([
      { id: "a-1", content: "account A" },
    ]);
    expect(memory.buckets.has("user:account-b")).toBe(false);
  });

  it("keeps one identity snapshot throughout an unlock update", async () => {
    const memory = createMemoryStorage();
    memory.buckets.set("user:account-a", [
      LocalMessage.from({
        id: "locked-a",
        role: "assistant",
        type: "image",
        content: "locked",
        createdAt: "2026-07-13T00:00:00.000Z",
        audioUrl: null,
        image: { type: "jpg", url: "https://example.com/a.jpg" },
        lockDetail: { locked: true, reason: "image_paywall" },
        sessionId: "user:account-a",
      }),
    ]);
    memory.buckets.set("user:account-b", []);
    const resolveIdentity = vi
      .fn()
      .mockResolvedValueOnce(Result.ok("user:account-a"))
      .mockResolvedValue(Result.ok("user:account-b"));
    const store = new ChatLocalMessageStore(memory.storage, resolveIdentity);

    const result = await store.markMessageUnlocked("locked-a");

    expect(Result.isOk(result)).toBe(true);
    expect(resolveIdentity).toHaveBeenCalledTimes(1);
    expect(memory.buckets.get("user:account-a")?.[0]?.lockDetail.locked).toBe(
      false,
    );
    expect(memory.buckets.get("user:account-b")).toEqual([]);
  });
});
