import type { DoneActorEvent, ErrorActorEvent } from "xstate";

import { applyEntitlementSnapshotToView } from "../helper/view";
import type { UserFetchData } from "./actors/profile";
import {
  baseUserMachineSetup,
  createUserActorActionSetup,
} from "./setup";

const updateUserAction = baseUserMachineSetup.assign(({ event }) =>
  event.type === "UserUpdate"
    ? {
        currentUser: event.user,
        isVip: event.user.isVip,
        creditBalance: event.user.creditBalance,
      }
    : {},
);

const markUserLoadingAction = baseUserMachineSetup.assign({
  isLoading: true,
});

export const profileMachineSetup = baseUserMachineSetup.extend({
  actions: {
    updateUser: updateUserAction,
    markUserLoading: markUserLoadingAction,
  },
});

const userFetchDoneSetup =
  createUserActorActionSetup<DoneActorEvent<UserFetchData>>();
const userFetchErrorSetup = createUserActorActionSetup<ErrorActorEvent>();

const applyFetchedUserAction = userFetchDoneSetup.assign(
  ({ context, event }) => {
    const snapshot =
      event.output.snapshot ??
      (context.currentUser
        ? {
            isVip: context.isVip,
            creditBalance: context.creditBalance,
          }
        : null);
    const baseUser = event.output.user ?? context.currentUser;
    return {
      currentUser:
        applyEntitlementSnapshotToView(baseUser, snapshot) ??
        context.currentUser,
      isVip: snapshot?.isVip ?? context.isVip,
      creditBalance: snapshot?.creditBalance ?? context.creditBalance,
      entitlements: event.output.entitlements ?? context.entitlements,
      isLoading: false,
    };
  },
);

const markUserFetchFailedAction = userFetchErrorSetup.assign({
  isLoading: false,
});

export const fetchingState = profileMachineSetup.createStateConfig({
  entry: "markUserLoading",
  invoke: {
    src: "userFetch",
    onDone: {
      target: "idle",
      actions: applyFetchedUserAction,
    },
    onError: {
      target: "idle",
      actions: markUserFetchFailedAction,
    },
  },
});
