using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using Cinemachine;

/// <summary>
/// Custom Cinemachine input handler that replaces CinemachineInputProvider.
/// Uses CinemachineCore.GetInputAxis delegate for total control over camera orbit.
/// 
/// Supports:
/// - Gamepad right stick (X = horizontal orbit, Y = vertical orbit)
/// - Mobile touch drag (right half of screen, ignoring UI)
/// - Mouse delta (desktop/editor)
/// 
/// Attach this to the CinemachineFreeLook camera GameObject.
/// Remove or disable CinemachineInputProvider if present.
/// </summary>
public class CinemachineCameraInput : MonoBehaviour
{
    [Header("Sensitivity")]
    [SerializeField] private float gamepadSensitivityX = 200f;
    [SerializeField] private float gamepadSensitivityY = 3f;
    [SerializeField] private float touchSensitivityX = 0.15f;
    [SerializeField] private float touchSensitivityY = 0.003f;
    [SerializeField] private float mouseSensitivityX = 3f;
    [SerializeField] private float mouseSensitivityY = 0.03f;
    
    [Header("Touch Settings")]
    [Tooltip("Only process touches on the right portion of the screen (0.5 = right half)")]
    [Range(0f, 1f)]
    [SerializeField] private float touchActiveScreenRatio = 0.4f;
    
    [Tooltip("Ignore touches over UI elements (buttons, joysticks, etc.)")]
    [SerializeField] private bool ignoreUITouches = true;
    
    [Header("Filtering")]
    [SerializeField] private float stickDeadzone = 0.15f;
    [SerializeField] private float smoothing = 0.1f;

    // Touch tracking
    private int activeTouchId = -1;
    private Vector2 lastTouchPos;
    
    // Smoothed output
    private float smoothedX;
    private float smoothedY;
    private float rawX;
    private float rawY;
    
    // Axis names used by CinemachineFreeLook
    private const string XAxisName = "Mouse X";
    private const string YAxisName = "Mouse Y";
    
    private CinemachineFreeLook freeLookCam;
    
    private void Awake()
    {
        freeLookCam = GetComponent<CinemachineFreeLook>();
    }

    private void OnEnable()
    {
        // Override Cinemachine's input gathering with our custom handler
        CinemachineCore.GetInputAxis = HandleCinemachineInput;
        
        // Disable CinemachineInputProvider if present (we replace it)
        var provider = GetComponent<CinemachineInputProvider>();
        if (provider != null)
        {
            provider.enabled = false;
            Debug.Log("[CinemachineCameraInput] Disabled CinemachineInputProvider — using custom input handler");
        }
        
        // Clear the default input axis names on the FreeLook camera
        // This prevents Cinemachine from also reading legacy Input Manager axes
        if (freeLookCam != null)
        {
            freeLookCam.m_XAxis.m_InputAxisName = "";
            freeLookCam.m_YAxis.m_InputAxisName = "";
        }
        
        Debug.Log("[CinemachineCameraInput] Custom camera input enabled (gamepad + touch + mouse)");
    }

    private void OnDisable()
    {
        // Restore default input handling when disabled
        CinemachineCore.GetInputAxis = UnityEngine.Input.GetAxis;
    }

    private void Update()
    {
        // Gather raw input from all sources
        rawX = 0f;
        rawY = 0f;

        ReadGamepadInput();
        ReadTouchInput();
        ReadMouseInput();

        // Smooth the input
        if (smoothing > 0.001f)
        {
            smoothedX = Mathf.Lerp(smoothedX, rawX, Time.unscaledDeltaTime / smoothing);
            smoothedY = Mathf.Lerp(smoothedY, rawY, Time.unscaledDeltaTime / smoothing);
        }
        else
        {
            smoothedX = rawX;
            smoothedY = rawY;
        }
    }

    /// <summary>
    /// Delegate callback — Cinemachine calls this instead of Input.GetAxis
    /// </summary>
    private float HandleCinemachineInput(string axisName)
    {
        if (axisName == XAxisName) return smoothedX;
        if (axisName == YAxisName) return smoothedY;
        return 0f;
    }

    // ======================== GAMEPAD ========================
    private void ReadGamepadInput()
    {
        if (Gamepad.current == null) return;

        Vector2 rightStick = Gamepad.current.rightStick.ReadValue();

        // Apply deadzone
        if (rightStick.magnitude < stickDeadzone)
            return;

        rawX += rightStick.x * gamepadSensitivityX * Time.unscaledDeltaTime;
        rawY += rightStick.y * gamepadSensitivityY * Time.unscaledDeltaTime;
    }

    // ======================== TOUCH ========================
    private void ReadTouchInput()
    {
        if (Touchscreen.current == null) return;

        var touches = Touchscreen.current.touches;
        bool foundActiveTouch = false;

        for (int i = 0; i < touches.Count; i++)
        {
            var touch = touches[i];
            if (!touch.press.isPressed) continue;

            int id = touch.touchId.ReadValue();
            Vector2 pos = touch.position.ReadValue();

            // If no active touch, try to start tracking this one
            if (activeTouchId == -1)
            {
                // Only track touches on the right portion of the screen
                if (pos.x < Screen.width * touchActiveScreenRatio)
                    continue;
                    
                // Ignore touches over UI elements
                if (ignoreUITouches && EventSystem.current != null && 
                    EventSystem.current.IsPointerOverGameObject(id))
                    continue;

                activeTouchId = id;
                lastTouchPos = pos;
                continue;
            }

            if (touch.touchId.ReadValue() != activeTouchId)
                continue;

            foundActiveTouch = true;
            Vector2 delta = pos - lastTouchPos;
            lastTouchPos = pos;

            rawX += delta.x * touchSensitivityX;
            rawY += delta.y * touchSensitivityY;
        }

        // Reset tracking if active touch released
        if (activeTouchId != -1 && !foundActiveTouch)
        {
            activeTouchId = -1;
        }
    }

    // ======================== MOUSE ========================
    private void ReadMouseInput()
    {
        if (Mouse.current == null) return;
        
        // Only use mouse if right button is held (prevents accidental camera rotation)
        if (!Mouse.current.rightButton.isPressed) return;

        Vector2 mouseDelta = Mouse.current.delta.ReadValue();
        
        rawX += mouseDelta.x * mouseSensitivityX * Time.unscaledDeltaTime;
        rawY += mouseDelta.y * mouseSensitivityY * Time.unscaledDeltaTime;
    }
}
