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

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

describe("user profile flow", () => {
  it("updates user profile synchronously", () => {
    const actor = createActor(createTestUserMachine()).start();
    actor.send({ type: "UserUpdate", user: makeUser({ username: "After" }) });
    expect(actor.getSnapshot().context.currentUser?.username).toBe("After");
    actor.stop();
  });

  it("fetches the latest user and entitlements", async () => {
    const actor = createActor(
      createTestUserMachine({
        fetchedData: {
          user: makeUser({ username: "Fresh User" }),
          snapshot: makeEntitlementSnapshot({
            isVip: true,
            creditBalance: 120,
          }),
        },
      }),
    ).start();
    actor.send({ type: "UserFetch" });
    expect(actor.getSnapshot().context.isLoading).toBe(true);
    await waitFor(actor, (snapshot) => snapshot.matches("idle"));

    expect(actor.getSnapshot().context).toMatchObject({
      isVip: true,
      creditBalance: 120,
      isLoading: false,
    });
    expect(actor.getSnapshot().context.currentUser).toMatchObject({
      username: "Fresh User",
      isVip: true,
      creditBalance: 120,
    });
    actor.stop();
  });

  it("keeps current user when fetch returns null", async () => {
    const actor = createActor(
      createTestUserMachine({ fetchedUser: null }),
    ).start();
    actor.send({ type: "UserUpdate", user: makeUser({ username: "Cached" }) });
    actor.send({ type: "UserFetch" });
    await waitFor(actor, (snapshot) => snapshot.matches("idle"));
    expect(actor.getSnapshot().context.currentUser?.username).toBe("Cached");
    actor.stop();
  });

  it("clears loading state when fetch actor fails", async () => {
    const actor = createActor(
      createTestUserMachine({ fetchError: new Error("fetch failed") }),
    ).start();
    actor.send({ type: "UserFetch" });
    await waitFor(actor, (snapshot) => snapshot.matches("idle"));
    expect(actor.getSnapshot().context.isLoading).toBe(false);
    actor.stop();
  });
});
