import { requiredPermissionsExists, UserToken } from '@axinom/mosaic-id-utils';
import React, { useCallback, useEffect, useState } from 'react';
import { useIdentityService } from '../IdentityServiceProvider/IdentityServiceProvider';
import { PermissionDeniedError } from '../PermissionDeniedError/PermissionDeniedError';

export type UserPermissions = UserToken['permissions'];

export interface EnsurePermissionProps {
  /** The required permissions. */
  permissions: UserPermissions;
  /** The component shown when permissions are insufficient. */
  denied?: React.ReactNode;
}

/**
 * Renders the children only if the current user has the required permissions.
 * The component that will be shown when the permissions are insufficient can be defined through the `denied` prop.
 */
export const EnsurePermission: React.FC<EnsurePermissionProps> = ({
  children,
  permissions,
  denied,
}) => {
  const checkPermission = useHasPermissions();
  const [hasPermission, setHasPermission] = useState(false);
  const [pending, setPending] = useState(true);
  useEffect(() => {
    checkPermission(permissions)
      .then((value) => setHasPermission(value))
      .catch(() => setHasPermission(true))
      .finally(() => setPending(false));
  }, [checkPermission, permissions]);

  if (pending) {
    return <></>;
  }
  if (hasPermission) {
    return <>{children}</>;
  } else {
    return denied ? <>{denied}</> : <PermissionDeniedError />;
  }
};

/**
 * Checks whether the current user has the permissions specified as `requiredPermissions`.
 * @param requiredPermissions The required permissions.
 */
export const useHasPermissions = (): ((
  requiredPermissions: UserPermissions,
) => Promise<boolean>) => {
  const { getToken } = useIdentityService();
  return useCallback(
    async (requiredPermissions) => {
      const { user } = await getToken();
      if (!user) {
        return false;
      }
      const currentPermissions = user.token.permissions;
      return requiredPermissionsExists(requiredPermissions, currentPermissions);
    },
    [getToken],
  );
};
