private void ProcessPlayerTeamSeasons(MlbStatsContext context, int teamId, int season, IEnumerable <Feeds.BoxscoreFeed.GamePlayer> feedPlayers)
        {
            var playerIds           = feedPlayers.Select(x => x.Person.Id).ToList();
            var dbPlayers           = context.Players.Where(x => playerIds.Contains(x.PlayerID)).ToDictionary(x => x.PlayerID);
            var dbPlayerTeamSeasons = context.PlayerTeamSeasons.Where(x => playerIds.Contains(x.PlayerID) && x.TeamID == teamId && x.Season == season).ToDictionary(x => x.PlayerID);

            if (dbPlayers.Count == playerIds.Count && dbPlayerTeamSeasons.Count == playerIds.Count)
            {
                // NO DB CHANGES NEEDED
                return;
            }
            if (dbPlayers.Count != playerIds.Count || dbPlayerTeamSeasons.Count != playerIds.Count)
            {
                // DB CHANGES NEEDED
                foreach (var feedPlayer in feedPlayers)
                {
                    if (!dbPlayers.TryGetValue(feedPlayer.Person.Id, out Player dbPlayer))
                    {
                        dbPlayer = new Player
                        {
                            PlayerID   = feedPlayer.Person.Id,
                            FullName   = feedPlayer.Person.FullName,
                            PlayerLink = feedPlayer.Person.Link,
                        };
                        context.Players.Add(dbPlayer);
                        dbPlayers.Add(dbPlayer.PlayerID, dbPlayer);
                    }

                    if (!dbPlayerTeamSeasons.TryGetValue(dbPlayer.PlayerID, out PlayerTeamSeason dbPlayerTeamSeason))
                    {
                        // TODO: PROCESS PLAYER DATA FROM PLAYER API CALL
                        dbPlayerTeamSeason = new PlayerTeamSeason
                        {
                            Player = dbPlayer,
                            TeamID = teamId,
                            Season = season
                        };
                        context.PlayerTeamSeasons.Add(dbPlayerTeamSeason);
                        dbPlayerTeamSeasons.Add(dbPlayer.PlayerID, dbPlayerTeamSeason);
                    }
                }
                context.SaveChanges();
            }
        }
        public void Run(MlbStatsContext context)
        {
            Feeds.BoxscoreFeed feed;
            using (var client = new WebClient())
            {
                var    url     = Feeds.BoxscoreFeed.GetFeedUrl(this.GameId);
                string rawJson = JsonUtility.GetRawJsonFromUrl(url);;
                if (rawJson == null)
                {
                    return;
                }
                feed = Feeds.BoxscoreFeed.FromJson(rawJson);

                if (feed != null)
                {
                    var dbGame = context.Games.SingleOrDefault(x => x.GameID == this.GameId);
                    if (dbGame == null)
                    {
                        throw new NullReferenceException($"GAME NOT FOUND IN DB: {this.GameId}");
                    }

                    if (feed.Officials != null && feed.Officials.Count > 0)
                    {
                        this.ProcessUmpires(context, dbGame, feed.Officials);
                    }

                    if (feed.Teams.Away?.Players != null && feed.Teams.Home?.Players != null)
                    {
                        ProcessPlayerTeamSeasons(context, dbGame.AwayTeamID.Value, dbGame.Season, feed.Teams.Away.Players.Values);
                        ProcessPlayerTeamSeasons(context, dbGame.HomeTeamID.Value, dbGame.Season, feed.Teams.Home.Players.Values);


                        bool gameHasHittingBoxscores = feed.Teams.Away.Players.Any(x => x.Value.Stats?.Batting != null && !x.Value.Stats.Batting.IsDefault()) &&
                                                       feed.Teams.Home.Players.Any(x => x.Value.Stats?.Batting != null && !x.Value.Stats.Batting.IsDefault());

                        bool gameHasPitchingBoxscores = feed.Teams.Away.Players.Any(x => x.Value.Stats?.Pitching != null && !x.Value.Stats.Pitching.IsDefault()) &&
                                                        feed.Teams.Home.Players.Any(x => x.Value.Stats?.Pitching != null && !x.Value.Stats.Pitching.IsDefault());

                        bool gameHasFieldingBoxscores = feed.Teams.Away.Players.Any(x => x.Value.Stats?.Fielding != null && !x.Value.Stats.Fielding.IsDefault()) &&
                                                        feed.Teams.Home.Players.Any(x => x.Value.Stats?.Fielding != null && !x.Value.Stats.Fielding.IsDefault());

                        // ASSUME FINISHED GAME WILL HAVE HITTING AND PITCHING DATA... FIELDING NOT SO MUCH
                        if (gameHasHittingBoxscores && gameHasPitchingBoxscores)
                        {
                            var dbPlayerBoxscores = context.PlayerGameBoxscores.Where(x => x.GameID == this.GameId).ToDictionary(x => x.PlayerID);
                            var awayPlayer        = feed.Teams?.Away?.Players.Where(x => x.Value != null).Select(y => y.Value).ToList();
                            ProcessPlayerGameBoxscores(context, dbGame.AwayTeamID.Value, dbGame.Season, dbPlayerBoxscores, awayPlayer);
                            var homePlayers = feed.Teams?.Home?.Players.Where(x => x.Value != null).Select(y => y.Value).ToList();
                            ProcessPlayerGameBoxscores(context, dbGame.HomeTeamID.Value, dbGame.Season, dbPlayerBoxscores, homePlayers);

                            var dbPlayerHittingBoxscores = context.PlayerHittingBoxscores.Where(x => x.GameID == this.GameId).ToDictionary(x => x.PlayerID);
                            var awayHitters = feed.Teams?.Away?.Players.Where(x => x.Value.Stats?.Batting != null && !x.Value.Stats.Batting.IsDefault()).Select(y => y.Value).ToList();
                            ProcessHitterBoxscores(context, dbGame.AwayTeamID.Value, dbGame.Season, dbPlayerHittingBoxscores, awayHitters);
                            var homeHitters = feed.Teams?.Home?.Players.Where(x => x.Value.Stats?.Batting != null && !x.Value.Stats.Batting.IsDefault()).Select(y => y.Value).ToList();
                            ProcessHitterBoxscores(context, dbGame.HomeTeamID.Value, dbGame.Season, dbPlayerHittingBoxscores, homeHitters);

                            var dbPlayerPitchingBoxscores = context.PlayerPitchingBoxscores.Where(x => x.GameID == this.GameId).ToDictionary(x => x.PlayerID);
                            var awayPitchers = feed.Teams?.Away?.Players.Where(x => x.Value.Stats?.Pitching != null && !x.Value.Stats.Pitching.IsDefault()).Select(y => y.Value).ToList();
                            ProcessPitcherBoxscores(context, dbGame.AwayTeamID.Value, dbGame.Season, dbPlayerPitchingBoxscores, awayPitchers);
                            var homePitchers = feed.Teams?.Home?.Players.Where(x => x.Value.Stats?.Pitching != null && !x.Value.Stats.Pitching.IsDefault()).Select(y => y.Value).ToList();
                            ProcessPitcherBoxscores(context, dbGame.HomeTeamID.Value, dbGame.Season, dbPlayerPitchingBoxscores, homePitchers);

                            if (gameHasFieldingBoxscores)
                            {
                                var dbPlayerFieldingBoxscores = context.PlayerFieldingBoxscores.Where(x => x.GameID == this.GameId).ToDictionary(x => x.PlayerID);
                                var awayFielders = feed.Teams?.Away?.Players.Where(x => x.Value.Stats?.Fielding != null && !x.Value.Stats.Fielding.IsDefault()).Select(y => y.Value).ToList();
                                ProcessFielderBoxscores(context, dbGame.AwayTeamID.Value, dbGame.Season, dbPlayerFieldingBoxscores, awayFielders);
                                var homeFielders = feed.Teams?.Home?.Players.Where(x => x.Value.Stats?.Fielding != null && !x.Value.Stats.Fielding.IsDefault()).Select(y => y.Value).ToList();
                                ProcessFielderBoxscores(context, dbGame.HomeTeamID.Value, dbGame.Season, dbPlayerFieldingBoxscores, homeFielders);
                            }
                        }
                    }

                    context.SaveChanges();
                }
            }
        }