using System;
using Intrepid.Core.Camera.Events;
using Intrepid.SimpleEvents;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine;

namespace Intrepid.Core.Camera
{
    public sealed class CameraZoomManager : EventListener
    {
        [GroupConfigurations, SerializeField] private float startZoomSmoothTime = 1f;
        
        [GroupConfigurations, SerializeField] private float defaultOrthographicSize = 7f;
        [GroupConfigurations, SerializeField] private float zoomInOrthographicSize = 4f;
        [GroupConfigurations, SerializeField] private float zoomOutOrthographicSize = 10f;

        private float TargetOrthographicSize { get; set; }

        private float _zoomSmoothTime;
        private float _zoomCurrentVelocity;

        protected override void InitializeVariables()
        {
            TargetOrthographicSize = defaultOrthographicSize;
            _zoomSmoothTime = startZoomSmoothTime;
            _zoomCurrentVelocity = 0f;
        }

        protected override void OnBootstrap()
        {
        }

        protected override void Subscriptions()
        {
            Subscribe<CameraZoomEvent>(e =>
            {
                SetZoomLevel(e.ZoomLevel, e.ZoomVelocity);    
            });
        }
        
        [GroupDebug, Button(ButtonSizes.Medium, ButtonStyle.FoldoutButton)]
        private void ZoomNone(float zoomSmoothTime = .25f)
        {
            SetZoomLevel(zoomSmoothTime: zoomSmoothTime);
        }
        
        [GroupDebug, Button(ButtonSizes.Medium, ButtonStyle.FoldoutButton)]
        private void ZoomIn(float zoomSmoothTime = .25f)
        {
            SetZoomLevel(CameraZoomLevel.ZoomIn, zoomSmoothTime);
        }
        
        [GroupDebug, Button(ButtonSizes.Medium, ButtonStyle.FoldoutButton)]
        private void ZoomOut(float zoomSmoothTime = .25f)
        {
            SetZoomLevel(CameraZoomLevel.ZoomOut, zoomSmoothTime);
        }
        
        private void SetZoomLevel(
            CameraZoomLevel zoomLevel = CameraZoomLevel.None,
            float zoomSmoothTime = .25f)
        {
            _zoomSmoothTime = Mathf.Max(0.01f, zoomSmoothTime);
            _zoomCurrentVelocity = 0f;

            TargetOrthographicSize = zoomLevel switch
            {
                CameraZoomLevel.None => defaultOrthographicSize,
                CameraZoomLevel.ZoomIn => zoomInOrthographicSize,
                CameraZoomLevel.ZoomOut => zoomOutOrthographicSize,
                _ => throw new ArgumentOutOfRangeException(nameof(zoomLevel), zoomLevel, null)
            };
        }

        private void Update()
        {
            UpdateByStack(CameraSystem.Instance.CurrentCameraStreamStack);
        }

        private void UpdateByStack(CameraStreamStack stack)
        {
            if (stack.IsNull()) return;
            var lens = stack.TopCameraStream.VCamera.Lens;

            lens.OrthographicSize = Mathf.SmoothDamp(
                lens.OrthographicSize,
                TargetOrthographicSize,
                ref _zoomCurrentVelocity,
                _zoomSmoothTime,
                Mathf.Infinity,
                Time.deltaTime
            );

            stack.TopCameraStream.VCamera.Lens = lens;
        }
    }
}