You can use a mix of time delay and backup codes. When the user activates 2FA, show him a couple Backup codes (like Google does) and ask them to save it somewhere, and ask one of the codes on the next screen. This will save you some trouble later.
From time to time, ask the user one of the backup codes when he logs in, even if he submits the 2FA token. If he does not know/don't have the backup codes, generate new ones, send to the user, ask him to save the codes, ask another random one in the next screen. User will be educated that the backup codes are special.
If user loses both the 2FA and the backup codes, create a very long process to retrieve the codes. Show him the necessary steps, how long they will take, and where he is:
User asks for a 2FA reset, send him a link and state that the link will be available from 24h to 48h AFTER he clicks the link
User receives a link telling someone with this IP/OS/browser asked to reset the 2FA, and if was him, he have to wait until next day and click the link.
- The next day, user clicks the link and receives another mail with a link and a reset token that must be entered on the site in 24h to 48h later. Add the same message that someone, maybe him, with this IP/OS/browser tried to reset his 2FA.
- Next day, user clicks the link, loads a page, enters your site, and enters the token. Tell him he will have to login in 24h to 48h again and 2FA will be disabled.
- User logs back in after 4 days, after receiving at least 3 emails telling him someone is trying to reset his 2FA.
I think 4 days is long enough for a hacker to lose interest in the account and a good timeframe to any user to detect and stop any account hijack. If 4 days is not enough, you can increase the time between each email. And any user really interested on recovering his account will follow the instructions.