using System;
using UnityEngine;

namespace LiveOpsExamples
{
  /// <summary>
  /// Gameplay world for the Cozy Collector demo — the Unity port of the Phaser
  /// GameScene.ts. Renders only the game world: player movement (WASD/arrows),
  /// round timer, spawn cadence and pickups. All chrome lives in the UI panels,
  /// all SDK calls in <see cref="CozyCollectorController"/>.
  ///
  /// The Phaser field is 1280×720 px at 100 PPU, i.e. 12.8×7.2 world units
  /// centered on the origin; tuning speeds/intervals stay in Phaser units
  /// (px/s, ms) and are converted here.
  /// </summary>
  public class GameWorld : MonoBehaviour
  {
    private const float PixelsPerUnit = 100f;

    [SerializeField] private SpriteRenderer player;
    [SerializeField] private SnackSpawner spawner;
    [SerializeField] private CozyCollectorController controller;
    [SerializeField] private ScorePopupView scorePopupPrefab;
    [SerializeField] private ParticleSystem collectBurstPrefab;
    [SerializeField] private Vector2 moveMin = new Vector2(-6.0f, -3.2f);
    [SerializeField] private Vector2 moveMax = new Vector2(6.0f, 3.2f);

    private bool _paused;
    private float _spawnElapsedMs;
    private int _lastSecond = -1;
    private bool _finishRaised;

    public int Score { get; private set; }
    public int Collected { get; private set; }
    public float TimeLeftSeconds { get; private set; }
    public bool RoundActive { get; private set; }

    public event Action StateChanged;
    public event Action<int, int> RoundFinished;

    private void Awake()
    {
      spawner.Player = player;
      spawner.Collected += OnSnackCollected;
    }

    private void OnDestroy()
    {
      spawner.Collected -= OnSnackCollected;
    }

    private void Update()
    {
      MovePlayer(_paused || !RoundActive);

      if (!RoundActive || _paused)
        return;

      TimeLeftSeconds = Mathf.Max(0f, TimeLeftSeconds - Time.deltaTime);
      _spawnElapsedMs += Time.deltaTime * 1000f;

      if (_spawnElapsedMs >= controller.Tuning.SpawnIntervalMs)
      {
        _spawnElapsedMs = 0f;
        spawner.Spawn();
      }

      var second = Mathf.CeilToInt(TimeLeftSeconds);
      if (second != _lastSecond)
      {
        _lastSecond = second;
        StateChanged?.Invoke();
      }

      if (TimeLeftSeconds <= 0f)
        FinishRound();
    }

    public void SetPaused(bool paused)
    {
      _paused = paused;
    }

    public void RestartRound()
    {
      Score = 0;
      Collected = 0;
      TimeLeftSeconds = controller.Tuning.RoundSeconds;
      _spawnElapsedMs = 0f;
      _lastSecond = -1;
      _finishRaised = false;
      spawner.Clear();
      RoundActive = true;
      spawner.Spawn();
      StateChanged?.Invoke();
    }

    private void MovePlayer(bool frozen)
    {
      if (frozen)
        return;

      var vx = (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A) ? -1 : 0) +
               (Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D) ? 1 : 0);
      var vy = (Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S) ? -1 : 0) +
               (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W) ? 1 : 0);

      if (vx == 0 && vy == 0)
        return;

      var direction = new Vector2(vx, vy).normalized;
      var speed = controller.Tuning.PlayerSpeed / PixelsPerUnit;
      var position = player.transform.position;
      position += (Vector3)(direction * speed * Time.deltaTime);
      position.x = Mathf.Clamp(position.x, moveMin.x, moveMax.x);
      position.y = Mathf.Clamp(position.y, moveMin.y, moveMax.y);
      player.transform.position = position;

      if (vx != 0)
        player.flipX = vx < 0;
    }

    private void OnSnackCollected(Snack snack)
    {
      var position = snack.transform.position;
      var gain = Mathf.RoundToInt(controller.Tuning.CollectibleScore);
      Score += gain;
      Collected += 1;

      var popup = Instantiate(scorePopupPrefab, position, Quaternion.identity);
      popup.Set(gain);
      Instantiate(collectBurstPrefab, position, Quaternion.identity);

      StateChanged?.Invoke();
    }

    private void FinishRound()
    {
      if (_finishRaised)
        return;

      _finishRaised = true;
      RoundActive = false;
      spawner.Clear();
      StateChanged?.Invoke();
      RoundFinished?.Invoke(Score, Collected);
    }
  }
}
