using UnityEngine;
using UnityEngine.InputSystem;

namespace AsteroidGame
{
    public class CameraController : MonoBehaviour
    {
        private CameraControllActions _cameraActions;
        private InputAction _movement;
        private Transform _cameraTransform;
        private Camera _cameraReference;

        // Horizontal Movement
        [SerializeField]
        private float _maxSpeed = 5f;
        private float _speed;
        [SerializeField]
        private float _acceleration = 10f;
        [SerializeField]
        private float _damping = 15f;

        //Vertial Motion - Zooming
        [SerializeField]
        private float _stepSize = 2f;
        [SerializeField]
        private float _zoomDampening = 7.5f;
        [SerializeField]
        private float _minHeight = 5f;
        [SerializeField]
        private float _maxHeight = 50f;
        [SerializeField]
        private float _zoomSpeed = 2f;

        //Rotation
        [SerializeField]
        private float _maxRotationSpeed = 1f;

        //Screen Edge Motion
        [SerializeField]
        [Range(0f, 0.1f)]
        private float _edgeTolerance = 0.05f;
        [SerializeField]
        private bool _useScreenEdge = false;

        private Vector3 _targetPosition;
        private float _zoomHeight;
        private Vector3 _horizontalVelocity;
        private Vector3 _lastPosition;
        private Plane _cameraPlane;

        Vector3 _startDrag;

        private void Awake()
        {
            _cameraActions = new CameraControllActions();
            
            _cameraReference = this.GetComponentInChildren<Camera>();
            _cameraTransform = _cameraReference.transform;
        }

        private void OnEnable()
        {
            _cameraPlane = new Plane(Vector3.up, Vector3.zero);
            _zoomHeight = _cameraTransform.localPosition.y;
            _cameraTransform.LookAt(this.transform);
            _lastPosition = this.transform.position;
            _movement = _cameraActions.Camera.Movement;
            _cameraActions.Camera.RotateCamera.performed += RotateCamera;
            _cameraActions.Camera.ZoomCamera.performed += ZoomCamera;
            _cameraActions.Camera.Enable();

        }

        private void OnDisable()
        {
            _cameraActions.Camera.RotateCamera.performed -= RotateCamera;
            _cameraActions.Camera.ZoomCamera.performed -= ZoomCamera;
            _cameraActions.Disable();
        }



        private void Update()
        {
            GetKeyboardMovement();
            if (_useScreenEdge)
            {
                CheckMouseAtScreenEdge();
            }
            DragCamera();
            UpdateVelocity();
            UpdateCameraPosition();
            UpdateBasePosition();

        }

        private void UpdateVelocity()
        {
            _horizontalVelocity = (this.transform.position - _lastPosition) / Time.deltaTime;
            _horizontalVelocity.y = 0;
            _lastPosition = this.transform.position;
        }

        private void GetKeyboardMovement()
        {
            Vector3 inputValue = _movement.ReadValue<Vector2>().x * GetCameraRight()
                                + _movement.ReadValue<Vector2>().y * GetCameraForward();
            inputValue = inputValue.normalized;

            if (inputValue.sqrMagnitude > 0.1f)
            {
                _targetPosition += inputValue;
            }
        }

        private Vector3 GetCameraRight()
        {
            Vector3 right = _cameraTransform.right;
            right.y = 0;
            return right;
        }

        private Vector3 GetCameraForward()
        {
            Vector3 forward = _cameraTransform.forward;
            forward.y = 0;
            return forward;
        }

        private void UpdateBasePosition()
        {
            if (_targetPosition.sqrMagnitude > 0.1f)
            {
                _speed = Mathf.Lerp(_speed, _maxSpeed, Time.deltaTime * _acceleration);
                transform.position +=  _speed * Time.deltaTime * _targetPosition;
            }
            else
            {
                _horizontalVelocity = Vector3.Lerp(_horizontalVelocity, Vector3.zero, Time.deltaTime * _damping);
                transform.position += _horizontalVelocity * Time.deltaTime;
            }

            _targetPosition = Vector3.zero;
        }

        private void RotateCamera(InputAction.CallbackContext inputValue)
        {
            if (!Mouse.current.middleButton.isPressed) { return; }

            float value = inputValue.ReadValue<Vector2>().x;
            transform.rotation = Quaternion.Euler(0f, value * _maxRotationSpeed + transform.rotation.eulerAngles.y, 0f);
        }

        private void ZoomCamera(InputAction.CallbackContext inputValue)
        {
            float value = -inputValue.ReadValue<Vector2>().y / 100f;

            if (Mathf.Abs(value) > 0.1f)
            {
                _zoomHeight = _cameraTransform.localPosition.y + value * _stepSize;
                if (_zoomHeight < _minHeight)
                    _zoomHeight = _minHeight;
                else if (_zoomHeight > _maxHeight)
                    _zoomHeight = _maxHeight;
            }
        }

        private void UpdateCameraPosition()
        {
            Vector3 zoomTarget = new Vector3(_cameraTransform.localPosition.x, _zoomHeight, _cameraTransform.localPosition.z);
            zoomTarget -= _zoomSpeed * (_zoomHeight - _cameraTransform.localPosition.y) * Vector3.forward;

            _cameraTransform.localPosition = Vector3.Lerp(_cameraTransform.localPosition, zoomTarget, Time.deltaTime * _zoomDampening);
            _cameraTransform.LookAt(this.transform);
        }

        private void CheckMouseAtScreenEdge()
        {
            Vector2 mousePosition = Mouse.current.position.ReadValue();
            Vector3 moveDirection = Vector3.zero;

            if (mousePosition.x < _edgeTolerance * Screen.width)
                moveDirection += -GetCameraRight();
            else if (mousePosition.x > (1f - _edgeTolerance) * Screen.width)
                moveDirection += GetCameraRight();

            if (mousePosition.y < _edgeTolerance * Screen.height)
                moveDirection += -GetCameraForward();
            else if (mousePosition.y > (1f - _edgeTolerance) * Screen.height)
                moveDirection += GetCameraForward();

            _targetPosition += moveDirection;
        }

        private void DragCamera()
        {
            if (!Mouse.current.rightButton.isPressed) { return; }

            
            Ray ray = _cameraReference.ScreenPointToRay(Mouse.current.position.ReadValue());
            if (_cameraPlane.Raycast(ray,out float distance))
            {
                if (Mouse.current.rightButton.wasPressedThisFrame)
                    _startDrag = ray.GetPoint(distance);
                else
                    _targetPosition += _startDrag - ray.GetPoint(distance);
            }
        }


    }
}
