import api from "src/api";
import { permissionStore } from "src/stores/permission";
import { useAuthStorage } from "src/composables/useAuthStorage";
import { APPROVAL_STATUS, useProviderApproval } from "src/composables/useProviderApproval";
import { usePushNotifications } from "src/composables/usePushNotifications";
import { userStore } from "src/stores/user";

let refreshPromise = null;

export const useAuth = () => {
  const { setRefreshToken, getRefreshToken, clearRefreshToken } = useAuthStorage();

  const clearAuthData = async () => {
    userStore().resetUser();
    permissionStore().resetScopes();

    await clearRefreshToken();
  };

  const setAuthDataFromPayload = async (tokens) => {
    const { access_token, refresh_token, user } = tokens;

    const store = userStore();

    store.setUser(user);
    store.accessToken = access_token;

    await setRefreshToken(refresh_token);

    // Fire-and-forget: o boot só consegue registrar o token quando o app abre
    // já autenticado. Quem acabou de se cadastrar ou de entrar registra aqui.
    registerPushDevice();

    if (String(user?.type).toUpperCase() === "PROVIDER" && !user?.provider) {
      await store.fetchUser();
    }

    if (useProviderApproval().isPendingProvider.value) {
      permissionStore().resetScopes();

      return;
    }

    await permissionStore().fetchScopes();
  };

  /**
   * Reconsulta o cadastro do prestador em análise e, se ele já tiver sido
   * aprovado, carrega os escopos para liberar o app sem exigir novo login.
   *
   * @return {Promise<boolean>} true quando o cadastro acabou de ser aprovado.
   */
  const refreshApprovalStatus = async () => {
    const { approvalStatus, isPendingProvider } = useProviderApproval();

    if (!isPendingProvider.value) {
      return false;
    }

    await userStore().fetchUser();

    if (approvalStatus.value !== APPROVAL_STATUS.ACCEPTED) {
      return false;
    }

    await permissionStore().fetchScopes();

    return true;
  };

  /**
   * Garante que o dispositivo esteja apto a receber push depois do login:
   * envia o token já capturado e (re)solicita o registro no FCM.
   */
  const registerPushDevice = async () => {
    const { isSupported, requestPermissionAndRegister, syncDeviceToken } =
      usePushNotifications();

    if (!isSupported()) {
      return;
    }

    try {
      await syncDeviceToken();
      await requestPermissionAndRegister();
    } catch (error) {
      console.error(error);
    }
  };

  //

  const login = async (email, password) => {
    try {
      const response = await api.post("/login", {
        email: email,
        password: password,
      });

      if (response.status === 200) {
        await setAuthDataFromPayload(response.data.payload);
      }

      return response;
    } catch (error) {
      return Promise.reject(error);
    }
  };

  const logout = async () => {
    try {
      // Precisa acontecer antes do /logout, enquanto o token ainda é válido.
      await usePushNotifications().unregisterDeviceToken();

      const response = await api.post("/logout");

      if (response.status === 200) {
        await clearAuthData();
      }
    } catch (error) {
      console.error(error);

      await clearAuthData();
    }
  };

  //

  const doRefresh = async () => {
    try {
      const refreshToken = await getRefreshToken();

      if (!refreshToken) {
        return Promise.reject(new Error("No refresh token available"));
      }

      const response = await api.post("/refresh-app", {
        refresh_token: refreshToken,
      });

      if (response.status === 200) {
        await setAuthDataFromPayload(response.data.payload);
      }

      return response;
    } catch (error) {
      await clearAuthData();

      return Promise.reject(error);
    }
  };

  const refresh = () => {
    if (!refreshPromise) {
      refreshPromise = doRefresh().finally(() => {
        refreshPromise = null;
      });
    }

    return refreshPromise;
  };

  return {
    clearAuthData,
    login,
    logout,
    refresh,
    refreshApprovalStatus,
    setAuthDataFromPayload,
  };
};
