using UnityEngine;
using UnityEngine.EventSystems;
#if UNITY_6000_0_OR_NEWER
using UnityEngine.InputSystem;
#endif

public class CameraController : MonoBehaviour
{
    public enum Mode { FreeDrag, FollowNPC, RoundRobin }

    [SerializeField] private Mode _mode = Mode.FreeDrag;
    [SerializeField] private float _zoomSpeed = 2f;
    [SerializeField] private float _minZoom = 2f;
    [SerializeField] private float _maxZoom = 20f;

    [Header("Follow NPC")]
    [SerializeField] private Transform _followTarget;
    [SerializeField] private float _followSpeed = 5f;
    [SerializeField] private float _followOffsetY = 0f;

    [Header("Round Robin")]
    [SerializeField] private float _switchInterval = 5f;

    private Camera _cam;
    private Vector3 _lastMouseWorld;
    private bool _isDragging;
    private Transform[] _roundRobinTargets;
    private int _roundRobinIndex = -1;
    private float _switchTimer;
    private Bounds _mapBounds;
    private bool _hasBounds;

    public Mode CameraMode { get => _mode; set => _mode = value; }
    public Transform FollowTarget { get => _followTarget; set => _followTarget = value; }
    public float SwitchInterval { get => _switchInterval; set => _switchInterval = value; }
    public int RoundRobinIndex => _roundRobinIndex;

    public void SetMapBounds(Bounds bounds)
    {
        _mapBounds = bounds;
        _hasBounds = true;
    }

    void Start() => _cam = GetComponent<Camera>();

    public void SetRoundRobinTargets(Transform[] targets)
    {
        _roundRobinTargets = targets;
        _roundRobinIndex = -1;
        _switchTimer = 0f;
    }

    public Transform GetCurrentTarget()
    {
        return _followTarget;
    }

    void LateUpdate()
    {
        if (_cam == null) return;

        if (_mode == Mode.RoundRobin)
            UpdateRoundRobin();

        bool isOverUI = EventSystem.current != null && EventSystem.current.IsPointerOverGameObject();

        if ((_mode == Mode.FollowNPC || _mode == Mode.RoundRobin) && _followTarget != null)
        {
            var targetPos = _followTarget.position + Vector3.up * _followOffsetY;
            targetPos.z = transform.position.z;
            transform.position = Vector3.Lerp(transform.position, targetPos, _followSpeed * Time.deltaTime);
        }
        else if (!isOverUI)
        {
            HandleDrag();
        }

        if (!isOverUI)
            HandleZoom();

        ClampToBounds();
    }

    void ClampToBounds()
    {
        if (!_hasBounds || _cam == null) return;

        // Limit zoom so viewport doesn't exceed map
        float maxOrthoH = _mapBounds.extents.y;
        float maxOrthoW = _mapBounds.extents.x / _cam.aspect;
        float maxOrtho = Mathf.Min(maxOrthoH, maxOrthoW);
        _cam.orthographicSize = Mathf.Min(_cam.orthographicSize, maxOrtho);

        float halfH = _cam.orthographicSize;
        float halfW = halfH * _cam.aspect;

        var pos = transform.position;
        float minX = _mapBounds.min.x + halfW;
        float maxX = _mapBounds.max.x - halfW;
        float minY = _mapBounds.min.y + halfH;
        float maxY = _mapBounds.max.y - halfH;

        // If viewport fits, clamp. Otherwise center.
        pos.x = minX < maxX ? Mathf.Clamp(pos.x, minX, maxX) : _mapBounds.center.x;
        pos.y = minY < maxY ? Mathf.Clamp(pos.y, minY, maxY) : _mapBounds.center.y;
        transform.position = pos;
    }

    void UpdateRoundRobin()
    {
        if (_roundRobinTargets == null || _roundRobinTargets.Length == 0) return;

        _switchTimer -= Time.deltaTime;
        if (_switchTimer <= 0)
        {
            _roundRobinIndex = (_roundRobinIndex + 1) % _roundRobinTargets.Length;
            _followTarget = _roundRobinTargets[_roundRobinIndex];
            _switchTimer = _switchInterval;
        }
    }

    void HandleDrag()
    {
        #if UNITY_6000_0_OR_NEWER
        var mouse = Mouse.current;
        if (mouse == null) return;

        Vector3 mouseScreen = mouse.position.ReadValue();
        mouseScreen.z = -_cam.transform.position.z;
        Vector3 mouseWorld = _cam.ScreenToWorldPoint(mouseScreen);

        if (mouse.leftButton.wasPressedThisFrame)
        {
            _lastMouseWorld = mouseWorld;
            _isDragging = true;
        }

        if (mouse.leftButton.wasReleasedThisFrame)
            _isDragging = false;
        #else
        Vector3 mouseScreen = Input.mousePosition;
        mouseScreen.z = -_cam.transform.position.z;
        Vector3 mouseWorld = _cam.ScreenToWorldPoint(mouseScreen);

        if (Input.GetMouseButtonDown(0))
        {
            _lastMouseWorld = mouseWorld;
            _isDragging = true;
        }

        if (Input.GetMouseButtonUp(0))
            _isDragging = false;
        #endif

        if (_isDragging)
        {
            Vector3 delta = _lastMouseWorld - mouseWorld;
            transform.position += delta;
        }
    }

    void HandleZoom()
    {
        #if UNITY_6000_0_OR_NEWER
        var mouse = Mouse.current;
        if (mouse == null) return;
        float scroll = mouse.scroll.y.ReadValue();
        #else
        float scroll = Input.mouseScrollDelta.y;
        #endif

        if (Mathf.Abs(scroll) > 0.01f)
        {
            _cam.orthographicSize -= scroll * _zoomSpeed * 0.01f;
            _cam.orthographicSize = Mathf.Clamp(_cam.orthographicSize, _minZoom, _maxZoom);
        }
    }
}
