some notes on code style:
if you are getting the facing value more than once, set it to a variable and reuse it.
you are using the same state for top, mid and bottom for checking placement. you shouldnt need to set them to variables if they are the same blockstate. if they are not supposed to be the same blockstate, you should fix that
if you dont need a nullable value, use boolean not Boolean. Boolean is a wrapper around the native boolean that allows null.
fail early checks do not need an else clause:
if (!top || !bottom) return null;
BlockState state = getDefaultState();
state = state.with(FACING, ctx.getHorizontalPlayerFacing().getOpposite());
return state;
to optimize a little more, you can do the !top directly instead of waiting until the bottom is also checked:
if ( !topState.canPlaceAt(world,topPos) ||
world.getBlockState(topPos).canReplace(ctx))
) {
return null;
}