State machines quickly become burdensome if you hardcode each case in a giant case statement. So you need state-handlers, which load the user's current state for a given quest or conversation id, and handle the transition to the next state.
Each game tick (or time round the game loop, or event) you pass any queued or incoming state change requests to the relevant state-handler. So when in the client you click an option, your client will call the server API with the id of the spoken-to NPC, and the id of the selected conversation item.
The server will validate that the player is currently conversing with the NPC, and that they are at a state where that selected conversation item was available, then process that selected conversation item (displaying a response, giving items, etc), then update their stored state.