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

import {
  createTestUserMachine,
  makeUser,
} from "./user-machine.test-utils";

describe("user session flow", () => {
  it("initializes user and avatar from actor output", async () => {
    const actor = createActor(
      createTestUserMachine({
        initData: {
          user: makeUser({ username: "Local User" }),
          avatarUrl: "https://example.com/local-avatar.png",
          snapshot: null,
        },
        fetchedUser: null,
      }),
    ).start();
    actor.send({ type: "UserInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("idle"));

    expect(actor.getSnapshot().context.currentUser?.username).toBe(
      "Local User",
    );
    expect(actor.getSnapshot().context.avatarUrl).toBe(
      "https://example.com/local-avatar.png",
    );
    actor.stop();
  });

  it("continues to fetch when initialization fails", async () => {
    const actor = createActor(
      createTestUserMachine({ initError: new Error("storage unavailable") }),
    ).start();
    actor.send({ type: "UserInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("idle"));
    expect(actor.getSnapshot().context.currentUser?.username).toBe("Fetched");
    actor.stop();
  });

  it("clears user data after logout", async () => {
    const logoutSpy = vi.fn<() => void>();
    const actor = createActor(
      createTestUserMachine({ logoutSpy }),
    ).start();
    actor.send({ type: "UserUpdate", user: makeUser() });
    actor.send({ type: "UserLogout" });
    await waitFor(actor, (snapshot) => snapshot.matches("idle"));

    expect(logoutSpy).toHaveBeenCalledOnce();
    expect(actor.getSnapshot().context.currentUser).toBeNull();
    expect(actor.getSnapshot().context.avatarUrl).toBeNull();
    actor.stop();
  });

  it("clears user data even when logout actor fails", async () => {
    const actor = createActor(
      createTestUserMachine({ logoutError: new Error("logout failed") }),
    ).start();
    actor.send({ type: "UserUpdate", user: makeUser() });
    actor.send({ type: "UserLogout" });
    await waitFor(actor, (snapshot) => snapshot.matches("idle"));
    expect(actor.getSnapshot().context.currentUser).toBeNull();
    actor.stop();
  });
});
