import { vi } from "vitest";
import { fromPromise } from "xstate";

import type { UserView } from "@/data/dto/user";
import { userMachine } from "@/stores/user/user-machine";
import type { UserFetchData } from "@/stores/user/machine/actors/profile";
import type { InitData } from "@/stores/user/machine/actors/session";

export function makeUser(overrides: Partial<UserView> = {}): UserView {
  return {
    id: "user-1",
    username: "Elio Fan",
    countryCode: "HK",
    creditBalance: 7,
    dailyFreeChatLimit: 30,
    dailyFreeChatRemaining: 30,
    dailyFreePrivateLimit: 2,
    dailyFreePrivateRemaining: 2,
    isVip: false,
    ...overrides,
  };
}

export function makeEntitlementSnapshot(
  overrides: Partial<InitData["snapshot"]> = {},
): NonNullable<InitData["snapshot"]> {
  return {
    isVip: false,
    creditBalance: 7,
    ...overrides,
  };
}

export function createTestUserMachine(
  overrides: Partial<{
    initData: InitData;
    initError: Error;
    fetchedUser: UserView | null;
    fetchedData: UserFetchData;
    fetchError: Error;
    logoutSpy: () => void;
    logoutError: Error;
  }> = {},
) {
  const logoutSpy = overrides.logoutSpy ?? vi.fn<() => void>();

  return userMachine.provide({
    actors: {
      userInit: fromPromise<InitData>(async () => {
        if (overrides.initError) throw overrides.initError;
        return {
          user: overrides.initData?.user ?? makeUser(),
          avatarUrl:
            overrides.initData?.avatarUrl ?? "https://example.com/avatar.png",
          snapshot: overrides.initData?.snapshot ?? null,
        };
      }),
      userFetch: fromPromise<UserFetchData>(async () => {
        if (overrides.fetchError) throw overrides.fetchError;
        if ("fetchedData" in overrides && overrides.fetchedData) {
          return overrides.fetchedData;
        }
        if ("fetchedUser" in overrides) {
          return { user: overrides.fetchedUser ?? null, snapshot: null };
        }
        return {
          user: makeUser({ username: "Fetched" }),
          snapshot: null,
        };
      }),
      userLogout: fromPromise<void>(async () => {
        logoutSpy();
        if (overrides.logoutError) throw overrides.logoutError;
      }),
    },
  });
}
