namespace Snake.Core;

public sealed class SnakeGame
{
    public SnakeGame(int width, int height, Random? random = null)
    {
        _random = random ?? Random.Shared;
        Board = new Board(width, height);

        var start = new Position(width / 2, height / 2);
        Snake = new Snake(start, length: 3, Direction.Right);
        Food = new Food(Board, Snake.Segments, _random);
    }

    private readonly Random _random;

    public Board Board { get; }

    public Snake Snake { get; }

    public Food Food { get; }

    public int Score { get; private set; }

    public bool IsGameOver { get; private set; }

    public const int PointsPerLevel = 5;

    public int Level => 1 + Score / PointsPerLevel;

    public static int GetTickIntervalMs(int score, int baseIntervalMs = 120, int minIntervalMs = 50) =>
        Math.Max(minIntervalMs, baseIntervalMs - (1 + score / PointsPerLevel - 1) * 10);

    public void SetDirection(Direction direction) => Snake.SetDirection(direction);

    public GameTickResult Tick()
    {
        if (IsGameOver)
            return GameTickResult.GameOver;

        var newHead = Snake.PeekNextHead();
        var ateFood = newHead == Food.Position;

        if (!Board.IsWithinBounds(newHead) || Snake.Occupies(newHead, excludeTail: !ateFood))
        {
            IsGameOver = true;
            return GameTickResult.GameOver;
        }
        Snake.Move(grow: ateFood);

        if (ateFood)
        {
            Score++;
            if (Snake.Segments.Count >= Board.Width * Board.Height)
            {
                IsGameOver = true;
                return GameTickResult.AteFood;
            }

            Food.Respawn(Snake.Segments);
            return GameTickResult.AteFood;
        }

        return GameTickResult.Moved;
    }
}
