using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Threading;
using Snake.Core;

namespace Snake.Avalonia.Views;

public partial class GameView : UserControl
{
    private const int BoardWidth = 20;
    private const int BoardHeight = 15;
    private const int BaseTickMs = 120;

    private SnakeGame _game = new(BoardWidth, BoardHeight);
    private readonly DispatcherTimer _gameTimer;
    private readonly DispatcherTimer _renderTimer;
    private DateTime _lastTickTime;
    private DateTime _lastRenderTime;
    private bool _isRunning;
    private bool _isPaused;
    private int _highScore;

    // Score popup state
    private int _scorePopupValue;
    private double _scorePopupElapsed;
    private Position _scorePopupPosition;

    public GameView()
    {
        InitializeComponent();

        // Load high score
        _highScore = HighScoreManager.Load();

        // Game timer — ticks the game logic
        _gameTimer = new DispatcherTimer();
        _gameTimer.Tick += OnGameTick;

        // Render timer — 60fps smooth animation
        _renderTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(16) };
        _renderTimer.Tick += OnRenderTick;

        // Button handlers
        GameOverRestartBtn.Click += (_, _) => RestartGame();
        GameOverQuitBtn.Click += (_, _) => CloseWindow();

        // Keyboard
        KeyDown += OnGlobalKeyDown;

        // Focus on click
        PointerPressed += (_, _) => Focus();

        // Initial HUD
        UpdateHud();

        // Start render loop immediately (for idle animation)
        _lastRenderTime = DateTime.UtcNow;
        _renderTimer.Start();
    }

    private void OnGlobalKeyDown(object? sender, KeyEventArgs e)
    {
        var key = e.Key;

        // Quit
        if (key == Key.Escape)
        {
            CloseWindow();
            return;
        }

        // Restart
        if (key == Key.R)
        {
            RestartGame();
            return;
        }

        // Pause/Resume
        if (key == Key.Space || key == Key.P)
        {
            TogglePause();
            return;
        }

        // Start game
        if (key == Key.Enter && (!_isRunning || _game.IsGameOver))
        {
            if (_game.IsGameOver)
                ResetGame();

            StartGame();
            return;
        }

        // Direction input — also starts game if not running
        var dir = KeyToDirection(key);
        if (dir.HasValue)
        {
            if (!_isRunning && !_game.IsGameOver)
            {
                StartGame();
            }

            if (_isRunning && !_game.IsGameOver && !_isPaused)
            {
                _game.SetDirection(dir.Value);
            }
        }

        e.Handled = true;
    }

    private static Direction? KeyToDirection(Key key) => key switch
    {
        Key.Up or Key.W => Direction.Up,
        Key.Down or Key.S => Direction.Down,
        Key.Left or Key.A => Direction.Left,
        Key.Right or Key.D => Direction.Right,
        _ => null
    };

    private void OnGameTick(object? sender, EventArgs e)
    {
        if (!_isRunning || _game.IsGameOver || _isPaused)
            return;

        _lastTickTime = DateTime.UtcNow;

        var result = _game.Tick();

        if (_game.IsGameOver)
        {
            _isRunning = false;
            _gameTimer.Stop();

            // Save high score
            if (_game.Score > _highScore)
            {
                _highScore = _game.Score;
                HighScoreManager.Save(_highScore);
            }

            ShowGameOver();
        }
        else
        {
            // Update game speed based on level
            var interval = SnakeGame.GetTickIntervalMs(_game.Score, BaseTickMs);
            var newInterval = TimeSpan.FromMilliseconds(interval);
            if (_gameTimer.Interval != newInterval)
                _gameTimer.Interval = newInterval;

            // Food eaten popup
            if (result == GameTickResult.AteFood)
            {
                _scorePopupValue = _game.Score;
                _scorePopupElapsed = 0;
                _scorePopupPosition = _game.Food.Position;
            }
        }

        UpdateHud();
    }

    private void OnRenderTick(object? sender, EventArgs e)
    {
        var now = DateTime.UtcNow;
        _lastRenderTime = now;

        if (_isRunning && !_isPaused && !_game.IsGameOver)
        {
            // Calculate interpolation T
            var elapsedSinceTick = (now - _lastTickTime).TotalMilliseconds;
            var tickInterval = SnakeGame.GetTickIntervalMs(_game.Score, BaseTickMs);
            var t = Math.Clamp(elapsedSinceTick / tickInterval, 0.0, 1.0);
            SnakeCanvas.InterpolationT = t;
        }
        else
        {
            // Keep t frozen when paused/game over
            SnakeCanvas.InterpolationT = Math.Clamp(SnakeCanvas.InterpolationT, 0.0, 1.0);
        }

        // Update score popup timer
        if (_scorePopupElapsed < 1.5)
        {
            _scorePopupElapsed += 0.016; // ~60fps
        }

        // Update snake state for renderer
        SnakeCanvas.Segments = _game.Snake.Segments.ToList();
        SnakeCanvas.PreviousSegments = _game.Snake.PreviousSegments;
        SnakeCanvas.SnakeDirection = _game.Snake.Direction;
        SnakeCanvas.FoodPosition = _game.Food.Position;
        SnakeCanvas.TotalElapsedSeconds += 0.016;

        // Score popup
        SnakeCanvas.ScorePopup = _scorePopupValue;
        SnakeCanvas.ScorePopupElapsed = _scorePopupElapsed;
        SnakeCanvas.ScorePopupPosition = _scorePopupPosition;

        // Trigger redraw
        SnakeCanvas.InvalidateVisual();
    }

    private void StartGame()
    {
        _isRunning = true;
        _isPaused = false;

        var interval = SnakeGame.GetTickIntervalMs(_game.Score, BaseTickMs);
        _gameTimer.Interval = TimeSpan.FromMilliseconds(interval);
        _lastTickTime = DateTime.UtcNow;
        _gameTimer.Start();

        StartOverlay.IsVisible = false;
        PauseOverlay.IsVisible = false;
        GameOverOverlay.IsVisible = false;

        UpdateHud();
        Focus();
    }

    private void TogglePause()
    {
        if (!_isRunning || _game.IsGameOver)
            return;

        _isPaused = !_isPaused;

        if (_isPaused)
        {
            _gameTimer.Stop();
            PauseOverlay.IsVisible = true;
        }
        else
        {
            _lastTickTime = DateTime.UtcNow;
            _gameTimer.Start();
            PauseOverlay.IsVisible = false;
        }

        UpdateHud();
    }

    private void RestartGame()
    {
        ResetGame();
        StartGame();
    }

    private void ResetGame()
    {
        _isRunning = false;
        _isPaused = false;
        _gameTimer.Stop();
        _game = new SnakeGame(BoardWidth, BoardHeight);
        _lastTickTime = DateTime.UtcNow;
        _scorePopupValue = 0;
        _scorePopupElapsed = 0;

        StartOverlay.IsVisible = false;
        PauseOverlay.IsVisible = false;
        GameOverOverlay.IsVisible = false;

        SnakeCanvas.InterpolationT = 0;
        SnakeCanvas.TotalElapsedSeconds = 0;

        UpdateHud();
    }

    private void ShowGameOver()
    {
        GameOverOverlay.IsVisible = true;
        GameOverScoreText.Text = $"Score: {_game.Score}";
        GameOverHighScoreText.Text = _game.Score >= _highScore
            ? "🏆  NEW HIGH SCORE!"
            : $"High Score: {_highScore}";
    }

    private void CloseWindow()
    {
        if (VisualRoot is Window window)
            window.Close();
    }

    private void UpdateHud()
    {
        ScoreText.Text = $"{_game.Score}";
        LevelText.Text = $"Level {_game.Level}";
        HighScoreText.Text = $"{_highScore}";

        // Speed bar width proportional to level
        var maxLevel = 15;
        var levelFraction = Math.Min((double)_game.Level / maxLevel, 1.0);
        SpeedBar.Width = 4 + levelFraction * 52; // 4-56px range
    }
}
