#nullable enable
using UnityEngine;

namespace io.github.hatayama.uLoopMCP
{
    public static class SimulateMouseInputOverlayState
    {
        private const float BUTTON_MIN_DISPLAY_DURATION = 0.05f;

        private static bool _leftButtonHeld;
        private static bool _rightButtonHeld;
        private static bool _middleButtonHeld;
        private static float _leftButtonActiveUntil;
        private static float _rightButtonActiveUntil;
        private static float _middleButtonActiveUntil;

        public static bool IsLeftButtonHeld =>
            _leftButtonHeld || Time.realtimeSinceStartup < _leftButtonActiveUntil;
        public static bool IsRightButtonHeld =>
            _rightButtonHeld || Time.realtimeSinceStartup < _rightButtonActiveUntil;
        public static bool IsMiddleButtonHeld =>
            _middleButtonHeld || Time.realtimeSinceStartup < _middleButtonActiveUntil;

        private const float SCROLL_DISPLAY_DURATION = 0.05f;
        private static int _scrollDirection;
        private static float _scrollActiveUntil;

        public static int ScrollDirection =>
            _scrollDirection != 0 && Time.realtimeSinceStartup < _scrollActiveUntil
                ? _scrollDirection
                : 0;

        private const float MOVE_DISPLAY_DURATION = 0.3f;
        private const int MOVE_SAMPLE_FRAMES = 5;
        private static Vector2 _moveDelta;
        private static Vector2 _moveAccumulator;
        private static int _moveFrameCount;
        private static float _moveActiveUntil;

        public static Vector2 MoveDelta =>
            _moveDelta != Vector2.zero && Time.realtimeSinceStartup < _moveActiveUntil
                ? _moveDelta
                : Vector2.zero;

        public static float LastActivityTime { get; private set; }

        public static bool HasAnyActivity =>
            IsLeftButtonHeld || IsRightButtonHeld || IsMiddleButtonHeld
            || ScrollDirection != 0 || MoveDelta != Vector2.zero;

        public static void SetButtonHeld(MouseButton button, bool held)
        {
            // When releasing, set a minimum display time so short clicks are always visible
            float activeUntil = held ? 0f : Time.realtimeSinceStartup + BUTTON_MIN_DISPLAY_DURATION;

            switch (button)
            {
                case MouseButton.Left:
                    _leftButtonHeld = held;
                    if (!held) _leftButtonActiveUntil = activeUntil;
                    break;
                case MouseButton.Right:
                    _rightButtonHeld = held;
                    if (!held) _rightButtonActiveUntil = activeUntil;
                    break;
                case MouseButton.Middle:
                    _middleButtonHeld = held;
                    if (!held) _middleButtonActiveUntil = activeUntil;
                    break;
                default:
                    Debug.Assert(false, $"Unexpected MouseButton value: {button}");
                    break;
            }

            LastActivityTime = Time.realtimeSinceStartup;
        }

        public static void SetScrollDirection(int direction)
        {
            Debug.Assert(direction >= -1 && direction <= 1, $"direction must be -1, 0, or 1, got: {direction}");
            _scrollDirection = direction;
            _scrollActiveUntil = Time.realtimeSinceStartup + SCROLL_DISPLAY_DURATION;
            LastActivityTime = Time.realtimeSinceStartup;
        }

        public static void ClearScroll()
        {
            _scrollActiveUntil = 0f;
        }

        public static void SetMoveDelta(Vector2 delta)
        {
            _moveAccumulator += delta;
            _moveFrameCount++;

            // Update direction every frame from accumulated delta so single-call
            // MoveDelta actions are visible, while the accumulator reset every N
            // frames smooths out per-frame integer quantization noise.
            if (_moveAccumulator != Vector2.zero)
            {
                _moveDelta = _moveAccumulator.normalized;
            }

            if (_moveFrameCount >= MOVE_SAMPLE_FRAMES)
            {
                _moveAccumulator = Vector2.zero;
                _moveFrameCount = 0;
            }

            _moveActiveUntil = Time.realtimeSinceStartup + MOVE_DISPLAY_DURATION;
            LastActivityTime = Time.realtimeSinceStartup;
        }

        public static void Clear()
        {
            _leftButtonHeld = false;
            _rightButtonHeld = false;
            _middleButtonHeld = false;
            _leftButtonActiveUntil = 0f;
            _rightButtonActiveUntil = 0f;
            _middleButtonActiveUntil = 0f;
            _scrollDirection = 0;
            _scrollActiveUntil = 0f;
            _moveDelta = Vector2.zero;
            _moveAccumulator = Vector2.zero;
            _moveFrameCount = 0;
            _moveActiveUntil = 0f;
            LastActivityTime = 0f;
        }
    }
}
