/** @format */

import {
  privateInstance,
  publicInstance,
  updatePrivateAxiosInstance,
  setAuthTransition,
} from "@/configs/axiosConfig";
/** @format */

import { ONBOARDING_POSITION, TOKEN_NAME } from "@/configs";
import { createStore } from "zustand/vanilla";
import { storage } from "@/configs/storage";
import { User } from "@/types/user";
import {
  clearPushNotificationSyncState,
  syncPushNotificationToken,
} from "@/services/notifications/pushNotificationService";
import { router } from "expo-router";

export type AuthState = {
  isAuthenticated: boolean;
  token: string;
  user: any;
  appInfo: any;
};

export type AuthActions = {
  login: (token: string, user: any) => void;
  logout: () => void;
  getUser: () => Promise<User | undefined>;
  setUser: (user: User) => void;
  loginWithToken: (token: string) => void;
};

export type AuthStore = AuthState & AuthActions;

export const defaultInitState: AuthState = {
  isAuthenticated: false,
  token: "",
  user: undefined,
  appInfo: null,
};

export const createAuthStore = (initState: AuthState = defaultInitState) => {
  return createStore<AuthStore>((set) => ({
    ...initState,
    /**
     * Authenticates a user by setting the authentication state to true and storing the user's token and data.
     *
     * @param {string} token - The authentication token for the user.
     * @param {any} user - The user's data.
     * @return {void} No return value.
     */
    loginWithToken: async (token) => {
      if (!token) {
        return;
      }
      try {
        const response = await publicInstance.get("/auth/me", {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        });
        const data = await response.data;
        const user: User = data?.data;
        if (!user) {
          return;
        }
        storage.set(TOKEN_NAME, token);
        set({
          isAuthenticated: true,
          token,
          user,
        });
        void syncPushNotificationToken();
      } catch (error) {
        console.log(error);
      }
    },
    /**
     * Authenticates a user by setting the authentication state to true and storing the user's token and data.
     *
     * @param {string} token - The authentication token for the user.
     * @param {any} user - The user's data.
     * @return {void} No return value.
     */
    login: async (token, user) => {
      setAuthTransition(true);
      try {
        updatePrivateAxiosInstance(token);
        storage.set(TOKEN_NAME, token);
        set({
          isAuthenticated: true,
          token,
          user,
        });
        void syncPushNotificationToken();
      } finally {
        // Small delay so any in-flight requests from prior state settle
        setTimeout(() => setAuthTransition(false), 2000);
      }
    },
    /**
     * Logs the user out of the application.
     *
     * Makes a POST request to the /auth/logout endpoint to invalidate the user's session.
     * If the request is successful, removes the authentication token from the cookie and updates the authentication state.
     *
     * @return {Promise<void>} A promise that resolves when the logout operation is complete.
     */
    logout: async () => {
      try {
        const response = await privateInstance.post("logout");

        if (response.status === 200) {
          router.push("/(auth)/sign-in");
          storage.remove(TOKEN_NAME);
          storage.remove(ONBOARDING_POSITION);
          clearPushNotificationSyncState();
          updatePrivateAxiosInstance("");
          set({
            isAuthenticated: false,
            token: "",
            user: null,
          });
        }
      } catch (error) {
        console.log(error);
      } },
    /**
     * Retrieves the authenticated user's data from the server.
     *
     * Makes a GET request to the /auth/user endpoint to fetch the user's data.
     * If the request is successful, updates the authentication state with the received user data.
     *
     * @return {Promise<void>} A promise that resolves when the user data is fetched and the authentication state is updated.
     */
    getUser: async () => {
      try {
        const response = await privateInstance.get("/me");
        const data = await response.data;
        const user: User = data?.data;
        set({ user, isAuthenticated: true });
        return user;
      } catch (error) {
        set({ user: null, isAuthenticated: false });
        console.log(error);
      }
    },
    setUser: (user: User) => {
      set({ user });
    },
  }));
};
