using System;
using FlappyBird.Core;
using UnityEngine;
using Zenject;

namespace FlappyBird.Bird
{
    /// <summary>
    /// Pure service class that owns all bird physics logic.
    /// No MonoBehaviour ? follows AI_RULES.md architecture rule #1.
    /// Registered via Zenject and driven by BirdView.
    /// </summary>
    public class BirdController : IBirdController, IDisposable
    {
        private readonly BirdSettings _settings;
        private readonly IGameManager _gameManager;
        private readonly IDeathManager _deathManager;

        private BirdView _view;
        private bool _inputEnabled;
        private float _rotationVelocity;
        private Vector3 _startPosition;

        [Inject]
        public BirdController(BirdSettings settings, IGameManager gameManager, IDeathManager deathManager)
        {
            _settings = settings;
            _gameManager = gameManager;
            _deathManager = deathManager;
            _gameManager.OnGameStateChanged += HandleGameStateChanged;
            _deathManager.OnDied += HandleDied;
        }

        public void Bind(BirdView view)
        {
            _view = view;
            _startPosition = _view.transform.position;
            ApplyState(_gameManager.CurrentState);
        }

        /// <summary>
        /// Spawns bird in a frozen state waiting for first input.
        /// </summary>
        public void Initialize()
        {
            if (_view == null)
            {
                _inputEnabled = false;
                _rotationVelocity = 0f;
                return;
            }

            _inputEnabled = false;
            _rotationVelocity = 0f;
            _view.transform.position = _startPosition;
            _view.Rigidbody.constraints = RigidbodyConstraints2D.None;
            _view.Rigidbody.gravityScale = 0f;
            _view.Rigidbody.linearVelocity = Vector2.zero;
            _view.Rigidbody.angularVelocity = 0f;
            _view.transform.rotation = Quaternion.identity;
        }

        /// <summary>
        /// Applies upward impulse and un-freezes physics on first tap.
        /// </summary>
        public void Jump()
        {
            if (_view == null)
            {
                return;
            }

            if (_gameManager.CurrentState == GameState.GameOver)
            {
                RestartRun();
                return;
            }

            if (_deathManager.IsDead)
            {
                return;
            }

            if (_gameManager.CurrentState == GameState.Menu)
            {
                _gameManager.StartGame();
            }

            JumpInternal();
        }

        public void HandleCollision()
        {
            _deathManager.Die();
        }

        public void SetInputEnabled(bool enabled)
        {
            _inputEnabled = enabled;

            if (_view == null)
            {
                return;
            }

            if (!enabled)
            {
                _view.Rigidbody.gravityScale = 0f;
                _view.Rigidbody.linearVelocity = Vector2.zero;
                _view.Rigidbody.angularVelocity = 0f;
                return;
            }

            _view.Rigidbody.gravityScale = _settings.activeGravityScale;
        }

        /// <summary>
        /// Clamps fall velocity and rotates the bird based on vertical speed.
        /// Called from BirdView.FixedUpdate ? contains NO input polling.
        /// </summary>
        public void FixedTick()
        {
            if (!_inputEnabled || _view == null)
            {
                return;
            }

            ApplyGravityScale();
            ClampRiseVelocity();
            ClampFallVelocity();
            ApplyRotation();
        }

        public void Dispose()
        {
            _gameManager.OnGameStateChanged -= HandleGameStateChanged;
            _deathManager.OnDied -= HandleDied;
        }

        private void HandleGameStateChanged(GameState state)
        {
            ApplyState(state);
        }

        private void HandleDied()
        {
            if (_view == null)
            {
                _inputEnabled = false;
                _rotationVelocity = 0f;
                return;
            }

            _inputEnabled = false;
            _rotationVelocity = 0f;
            _view.Rigidbody.constraints = RigidbodyConstraints2D.None;
            _view.Rigidbody.gravityScale = _settings.activeGravityScale * _settings.fallGravityMultiplier;
            _view.Rigidbody.linearVelocity = new Vector2(
                _view.Rigidbody.linearVelocity.x,
                Mathf.Min(_view.Rigidbody.linearVelocity.y, 0f));
            _view.Rigidbody.angularVelocity = -Mathf.Abs(_settings.rotationSpeed);
        }

        private void ApplyState(GameState state)
        {
            if (state == GameState.Menu)
            {
                Initialize();
                return;
            }

            if (state == GameState.GameOver && !_deathManager.IsDead)
            {
                SetInputEnabled(false);
            }
        }

        private void RestartRun()
        {
            _gameManager.ReturnToMenu();
            _gameManager.StartGame();
            JumpInternal();
        }

        private void JumpInternal()
        {
            if (!_inputEnabled)
            {
                SetInputEnabled(true);
            }

            Vector2 velocity = _view.Rigidbody.linearVelocity;
            float carriedFallVelocity = Mathf.Min(velocity.y, 0f) * _settings.fallVelocityCarryover;
            _view.Rigidbody.gravityScale = _settings.activeGravityScale * _settings.riseGravityMultiplier;
            _view.Rigidbody.linearVelocity = new Vector2(velocity.x, _settings.jumpStartVelocity + carriedFallVelocity);
            _view.Rigidbody.AddForce(Vector2.up * _settings.jumpForce, ForceMode2D.Impulse);

            ClampRiseVelocity();
        }

        private void ApplyGravityScale()
        {
            float verticalVelocity = _view.Rigidbody.linearVelocity.y;
            float gravityMultiplier = verticalVelocity >= 0f
                ? _settings.riseGravityMultiplier
                : _settings.fallGravityMultiplier;

            _view.Rigidbody.gravityScale = _settings.activeGravityScale * gravityMultiplier;
        }

        private void ClampRiseVelocity()
        {
            Vector2 velocity = _view.Rigidbody.linearVelocity;
            if (velocity.y > _settings.maxRiseVelocity)
            {
                _view.Rigidbody.linearVelocity = new Vector2(velocity.x, _settings.maxRiseVelocity);
            }
        }

        private void ClampFallVelocity()
        {
            Vector2 velocity = _view.Rigidbody.linearVelocity;
            if (velocity.y < _settings.maxFallVelocity)
            {
                _view.Rigidbody.linearVelocity = new Vector2(velocity.x, _settings.maxFallVelocity);
            }
        }

        private void ApplyRotation()
        {
            float verticalVelocity = _view.Rigidbody.linearVelocity.y;
            float targetAngle = verticalVelocity >= 0f
                ? Mathf.Lerp(0f, _settings.maxUpAngle, Mathf.InverseLerp(0f, _settings.upAngleVelocity, verticalVelocity))
                : Mathf.Lerp(0f, _settings.maxDownAngle, Mathf.InverseLerp(0f, Mathf.Abs(_settings.downAngleVelocity), Mathf.Abs(verticalVelocity)));

            float currentAngle = _view.transform.eulerAngles.z;
            if (currentAngle > 180f)
            {
                currentAngle -= 360f;
            }

            float smoothTime = Mathf.Max(0.02f, 180f / Mathf.Max(1f, _settings.rotationSpeed) * 0.1f);
            float newAngle = Mathf.SmoothDampAngle(
                currentAngle,
                targetAngle,
                ref _rotationVelocity,
                smoothTime,
                Mathf.Max(1f, _settings.rotationSpeed),
                Time.fixedDeltaTime);

            _view.transform.rotation = Quaternion.Euler(0f, 0f, newAngle);
        }
    }
}
