생선 and 고기 are heavily contextualized words. You should think in terms of their context.
물고기 is a formal word not used that much in everyday conversation.
Note their meanings when you're talking about food:
- 생선 = fish.
- 고기 = meat (i.e. any kind of meat, excluding fish).
- 물고기 = not used. We always refer to fish as 생선 in food context.
And when you're not talking about food:
- 생선 = rarely used. 생선 always means caught fish prepared for food.
- 고기 = live fish. It is short for 물고기, but more natural than 물고기 in real life situations.
- 물고기 = "fish" as a formal word, as in news reports, textbooks, etc.
고기 can be tricky as it means two different things depending on the context.
For example, 고기잡이 means fishery, 고기잡이배 fishing boat, and 고기(를) 잡으러 가자 "Let's go catch some fish". 물고기 is rarely used in ordinary speech.
Live animals like cows and pigs are called 가축.