namespace ScoreboardApi.Functions
{
    using ScoreboardApi.Objects;

    public class GameManager : IGameManager
    {
        private readonly IPointsCalculator pointsCalculator;
        private Game? _game;

        public GameManager(IPointsCalculator pointsCalculator)
        {
            this.pointsCalculator = pointsCalculator;
        }

        public void StartNew(Team home, Team away, DateTimeOffset kickOffTime)
        {
            if (_game != null)
            {
                throw new InvalidOperationException("Game is already configured");
            }

            _game = new Game
            {
                Half = HalfType.Pre,
                Home = home,
                Away = away,
                StartedAt = kickOffTime,
                State = GameState.WaitingForStart
            };
        }

        public void Finish()
        {
            if (_game == null)
            {
                return;
            }

            _game.FinishedAt = DateTimeOffset.UtcNow;
            _game.State = GameState.Finished;
            _game.Half = HalfType.End;

            var evt = new Event
            {
                Category = EventCategory.Match,
                EventType = EventType.MatchEnd,
                TimeStamp = DateTimeOffset.Now
            };
            AddEvent(evt);
        }

        public void AddEvent(Event newEvent)
        {
            if (_game == null)
            {
                return;
            }

            newEvent.Id = GetEventId();

            newEvent.Points = pointsCalculator.GetEventScore(newEvent.EventType);

            if (newEvent.Category == EventCategory.Match)
            {
                _game.MatchEvents.Add(newEvent);
            }
            else if (newEvent.Category == EventCategory.Home)
            {
                _game.Home.EventHistory.Add(newEvent);
            }
            else
            {
                _game.Away.EventHistory.Add(newEvent);
            }

            switch (newEvent.EventType)
            {
                case EventType.HalfTime:
                    _game.State = GameState.HalfTime;
                    _game.Half = HalfType.First;
                    return;
                case EventType.SecondHalfStart:
                    _game.State = GameState.SecondHalf;
                    _game.Half = HalfType.Second;
                    return;
                case EventType.TimeOff:
                    _game.State = GameState.TimeOff;
                    return;
                case EventType.TimeOn:
                    if (_game.Half == HalfType.First)
                    {
                        _game.State = GameState.FirstHalf;
                    }
                    else if (_game.Half == HalfType.Second)
                    {
                        _game.State = GameState.SecondHalf;
                    }

                    break;
                case EventType.MatchEnd:
                    _game.State = GameState.Finished;
                    _game.Half = HalfType.End;
                    break;
            }
        }

        public void DeleteEvent(int eventId)
        {
            if (_game == null)
            {
                return;
            }

            _game.Teams.ForEach(
                (t) =>
                {
                    Event? eventToRemove = t.EventHistory.FirstOrDefault(x => x.Id == eventId);
                    if (eventToRemove != null)
                    {
                        t.EventHistory.Remove(eventToRemove);
                    }
                });
        }

        public void ResetGame() => _game = null;

        public Game GetScore()
        {
            if (_game == null)
            {
                return null;
            }

            if (_game.State == GameState.WaitingForStart
                && DateTimeOffset.UtcNow.UtcDateTime.CompareTo(_game.StartedAt.UtcDateTime) >= 0)
            {
                var evt = new Event
                {
                    Category = EventCategory.Match,
                    EventType = EventType.MatchStart,
                    TimeStamp = DateTimeOffset.Now
                };
                AddEvent(evt);
                _game.Half = HalfType.First;
                _game.State = GameState.FirstHalf;
            }

            return _game;
        }

        private int GetEventId()
        {
            IEnumerable<Event> events = _game.EventHistory;
            if (!events.Any())
            {
                return 1;
            }

            return events.Max(x => x.Id) + 1;
        }
    }
}