I strongly disagree with the selected answer.
Your question is equivalent to asking what kind of surgery to do just by visually looking at the belly. By providing an answer in such conditions you are saying that by visually looking at the belly you know what's the illness and what surgery to do. It doesn't work that way.
The previous answer just suggests asking more doctors to look at the belly.
From another point of view, the client has a BIG problem in its hands. A horrible code that does something, but that's very difficult to maintain. That's their problem, not yours. That's the normal consequence of saving pennies the moment they were required.
First, there are two approaches when engaging software professionals: either the client pays for time (and accepts the results even if they are not finished after the time has finished) or the client pays for a product (regardless of the time). Of course, a good developer would deliver a good product in an optimal amount of time.
In this case, the client wants the product. But the constraints to develop such product are unknown, and will only be known after the proper examination. Therefore, there are two things to charge for: the proper examination (going further than just looking at the belly, take some x-rays, blood/urine analysis, whatever), and the actual surgery.
So, you are doing for free, within improper conditions, what you should do as part of a comprehensive and proper work. You are denying the BIG problem that the client has within its hands, and if you "don't take it too seriously", you will assume the final responsibilities.
What I would do is to budget a first period of work, where you would test the actual codebase and prepare a proof of concept of implementation of the requirements (after the first phase).
Then, with the best confidence (of course, will never be of 100%), you can then present a plan and budget for the actual functional implementation. The second phase is easy: just coding what you need.
Regarding how to approach the actual technical task, I've experienced this kind of situations. In this case, for small-medium -sized apps, the first phase, my approach was always to recognize (sometimes delete) the code unrelated to the functionality and keep the codebase working. Then, find all the necessary endpoints which the new functionality must interact with. The second phase implies just working.
To the person who wrote the selected answer: I would never engage you, if your philosophy regarding my code is "don't take it too seriously". That's a horrible approach of this beautiful discipline and a lack of respect to all clients. I don't know you, this is not personal. Please think on that.