import { setup, type ActionFunction, type EventObject } from "xstate";

import type { AuthEvent } from "../auth-events";
import type { AuthState } from "../auth-state";
import {
  emailLoginActor,
  emailRegisterThenLoginActor,
} from "./actors/credentials";
import { bindFacebookIdentityActor } from "./actors/identity";
import {
  oauthSignInActor,
  syncFacebookBackendActor,
  syncGoogleBackendActor,
} from "./actors/oauth";
import {
  checkAuthStatusActor,
  guestLoginActor,
  logoutActor,
} from "./actors/session";

export const baseAuthMachineSetup = setup({
  types: {
    context: {} as AuthState,
    events: {} as AuthEvent,
  },
  actors: {
    emailLogin: emailLoginActor,
    emailRegisterThenLogin: emailRegisterThenLoginActor,
    oauthSignIn: oauthSignInActor,
    syncGoogleBackend: syncGoogleBackendActor,
    syncFacebookBackend: syncFacebookBackendActor,
    guestLogin: guestLoginActor,
    checkAuthStatus: checkAuthStatusActor,
    bindFacebookIdentity: bindFacebookIdentityActor,
    logout: logoutActor,
  },
  guards: {
    canSubmitGuestLogin: ({ context }) =>
      context.loginStatus === "notLoggedIn",
    canLogoutToGuest: ({ context }) =>
      context.loginStatus !== "notLoggedIn" &&
      context.loginStatus !== "guest",
  },
});

type AuthActorAction<TEvent extends EventObject> = ActionFunction<
  AuthState,
  TEvent,
  AuthEvent,
  undefined,
  never,
  never,
  never,
  never,
  never
>;

export function createAuthActorActionSetup<TEvent extends EventObject>() {
  const actorActionSetup = setup({
    types: {
      context: {} as AuthState,
      events: {} as TEvent,
    },
  });
  return {
    assign(
      assignment: Parameters<typeof actorActionSetup.assign>[0],
    ): AuthActorAction<TEvent> {
      return actorActionSetup.assign(assignment) as unknown as AuthActorAction<TEvent>;
    },
  };
}
