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

import type { LoginStatus } from "@/data/schemas/auth";
import type { AuthProvider } from "@/lib/auth/auth_platform";
import { authMachine } from "@/stores/auth/auth-machine";
import type { BindFacebookIdentityInput } from "@/stores/auth/auth-events";

export function createTestAuthMachine(
  overrides: Partial<{
    checkAuthStatus: LoginStatus;
    guestLogin: LoginStatus;
    guestLoginSpy: () => void;
    emailLogin: LoginStatus;
    logoutSpy: () => void;
    bindFacebookIdentitySpy: (input: BindFacebookIdentityInput) => void;
    oauthSignInError: Error;
    googleSync: LoginStatus;
    facebookSync: LoginStatus;
  }> = {},
) {
  const logoutSpy = overrides.logoutSpy ?? vi.fn<() => void>();
  const guestLoginSpy = overrides.guestLoginSpy ?? vi.fn<() => void>();
  const bindFacebookIdentitySpy =
    overrides.bindFacebookIdentitySpy ??
    vi.fn<(input: BindFacebookIdentityInput) => void>();

  return authMachine.provide({
    actors: {
      checkAuthStatus: fromPromise<LoginStatus, void>(
        async () => overrides.checkAuthStatus ?? "notLoggedIn",
      ),
      bindFacebookIdentity: fromPromise<
        void,
        BindFacebookIdentityInput
      >(async ({ input }) => {
        bindFacebookIdentitySpy(input);
      }),
      guestLogin: fromPromise<LoginStatus, void>(async () => {
        guestLoginSpy();
        return overrides.guestLogin ?? "guest";
      }),
      emailLogin: fromPromise<
        LoginStatus,
        { email: string; password: string }
      >(async () => overrides.emailLogin ?? "email"),
      emailRegisterThenLogin: fromPromise<
        LoginStatus,
        {
          email: string;
          password: string;
          username: string;
          confirmPassword: string;
        }
      >(async () => "email"),
      oauthSignIn: fromPromise<void, AuthProvider>(async () => {
        if (overrides.oauthSignInError) throw overrides.oauthSignInError;
      }),
      syncGoogleBackend: fromPromise<LoginStatus, { idToken: string }>(
        async () => overrides.googleSync ?? "google",
      ),
      syncFacebookBackend: fromPromise<
        LoginStatus,
        { accessToken: string }
      >(async () => overrides.facebookSync ?? "facebook"),
      logout: fromPromise<LoginStatus>(async () => {
        logoutSpy();
        return "guest";
      }),
    },
  });
}
