using UnityEngine;

/// <summary>
/// Attach to Main Camera in Client.unity.
/// Top-down perspective camera that follows the local player capsule.
///
/// Features:
///   • Auto-follow player when tracking (can be paused by dragging)
///   • Single-finger touch drag (or mouse drag) to pan
///   • Pinch gesture (or mouse scroll wheel) to zoom (changes camera height)
///   • Double-tap anywhere to instantly recenter on player
///   • Static Recenter() method called by the HUD recenter button
/// </summary>
public class MapCameraController : MonoBehaviour
{
    // ── Singleton (weak — no DontDestroyOnLoad needed, camera lives in Client.unity) ──
    public static MapCameraController Instance { get; private set; }

    // ── Public API ────────────────────────────────────────────────────────────
    public void SetTarget(GameObject target) { _target = target; }
    public void Recenter() { _isTracking = true; _resumeTimer = 0f; }

    // ── Tuning ────────────────────────────────────────────────────────────────
    private const float FollowSmoothing  = 8f;    // lerp speed when tracking
    private const float DefaultHeight    = 150f;  // camera Y (metres above ground)
    private const float MinHeight        = 30f;   // closest zoom
    private const float MaxHeight        = 350f;  // furthest zoom
    private const float PinchZoomSens    = 1.2f;  // multiplier for pinch speed
    private const float ScrollZoomSens   = 30f;   // world-units per scroll tick
    private const float ResumeDelay      = 3.5f;  // s after drag ends before auto-tracking resumes
    private const float DoubleTapWindow  = 0.32f; // s between taps to count as double
    private const float DragThreshold    = 8f;    // pixels moved before drag starts

    // ── Runtime state ─────────────────────────────────────────────────────────
    private Camera     _cam;
    private GameObject _target;
    private float      _currentHeight;
    private bool       _isTracking  = true;
    private float      _resumeTimer;

    // Drag
    private bool    _dragActive;
    private Vector2 _lastDragScreen;

    // Pinch
    private float _pinchStartDist = -1f;
    private float _pinchStartHeight;

    // Double-tap
    private int   _tapCount;
    private float _tapTimer;

    // ── MonoBehaviour ─────────────────────────────────────────────────────────
    void Awake()
    {
        Instance = this;
        _cam = GetComponent<Camera>();
        if (_cam == null) { Debug.LogError("[MapCamera] No Camera component!"); return; }

        // Keep existing perspective mode — just ensure straight-down orientation
        transform.rotation = Quaternion.Euler(90f, 0f, 0f);
        _currentHeight = transform.position.y > 1f ? transform.position.y : DefaultHeight;
        transform.position = new Vector3(transform.position.x, _currentHeight, transform.position.z);
    }

    void OnEnable() { Instance = this; }

    void LateUpdate()
    {
        HandleInput();
        FollowTarget();
    }

    // ── Target following ──────────────────────────────────────────────────────
    void FollowTarget()
    {
        if (!_isTracking || _target == null) return;
        Vector3 tp = _target.transform.position;
        Vector3 dest = new Vector3(tp.x, _currentHeight, tp.z);
        transform.position = Vector3.Lerp(transform.position, dest, Time.deltaTime * FollowSmoothing);
    }

    // ── Input ─────────────────────────────────────────────────────────────────
    void HandleInput()
    {
        // Auto-resume tracking after a period of no dragging
        if (!_isTracking)
        {
            _resumeTimer += Time.deltaTime;
            if (_resumeTimer >= ResumeDelay) _isTracking = true;
        }

        // Double-tap timer
        _tapTimer += Time.deltaTime;
        if (_tapTimer > DoubleTapWindow) _tapCount = 0;

        int tc = Input.touchCount;

        if (tc == 2)
        {
            HandlePinch();
            return;
        }

        _pinchStartDist = -1f; // reset pinch when not 2 fingers

        if (tc == 1)
        {
            Touch t = Input.GetTouch(0);
            switch (t.phase)
            {
                case TouchPhase.Began:
                    OnPointerDown(t.position);
                    break;
                case TouchPhase.Moved:
                case TouchPhase.Stationary:
                    OnPointerDrag(t.position);
                    break;
                case TouchPhase.Ended:
                case TouchPhase.Canceled:
                    OnPointerUp();
                    break;
            }
            return;
        }

        // Mouse fallback (editor / desktop)
        if (Input.GetMouseButtonDown(0)) OnPointerDown(Input.mousePosition);
        else if (Input.GetMouseButton(0)) OnPointerDrag(Input.mousePosition);
        else if (Input.GetMouseButtonUp(0)) OnPointerUp();

        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (Mathf.Abs(scroll) > 0.001f)
        {
            _currentHeight = Mathf.Clamp(_currentHeight - scroll * ScrollZoomSens, MinHeight, MaxHeight);
            transform.position = new Vector3(transform.position.x, _currentHeight, transform.position.z);
        }
    }

    void OnPointerDown(Vector2 screenPos)
    {
        _lastDragScreen = screenPos;
        _dragActive = false;

        // Double-tap detection
        _tapCount++;
        _tapTimer = 0f;
        if (_tapCount >= 2)
        {
            _tapCount = 0;
            Recenter();
        }
    }

    void OnPointerDrag(Vector2 screenPos)
    {
        Vector2 screenDelta = screenPos - _lastDragScreen;

        if (!_dragActive && screenDelta.magnitude > DragThreshold)
        {
            _dragActive = true;
            _isTracking = false;
            _resumeTimer = 0f;
        }

        if (_dragActive)
        {
            // Pan: move camera so that the world point under the finger stays fixed.
            // Because the camera faces straight down, we can use a simpler formula:
            // pixels → world = (camera height / focal length in pixels) ratio.
            // For perspective: visible half-height at ground = height * tan(fov/2)
            // world_per_pixel = 2 * height * tan(fov/2) / screenHeight
            float halfFovRad = _cam.fieldOfView * 0.5f * Mathf.Deg2Rad;
            float worldPerPixelY = 2f * _currentHeight * Mathf.Tan(halfFovRad) / Screen.height;
            float worldPerPixelX = worldPerPixelY * ((float)Screen.width / Screen.height);

            // Flip: dragging right moves world right (camera moves left)
            transform.position += new Vector3(
                -screenDelta.x * worldPerPixelX,
                0f,
                -screenDelta.y * worldPerPixelY
            );
        }

        _lastDragScreen = screenPos;
    }

    void OnPointerUp()
    {
        _dragActive = false;
    }

    // ── Pinch zoom ────────────────────────────────────────────────────────────
    void HandlePinch()
    {
        Touch t0 = Input.GetTouch(0);
        Touch t1 = Input.GetTouch(1);

        if (t0.phase == TouchPhase.Began || t1.phase == TouchPhase.Began)
        {
            _pinchStartDist   = Vector2.Distance(t0.position, t1.position);
            _pinchStartHeight = _currentHeight;
            return;
        }

        if (_pinchStartDist <= 0f) return;

        float currentDist = Vector2.Distance(t0.position, t1.position);
        if (currentDist < 1f) return;

        // Closer fingers = zoom in (lower height)
        float ratio = _pinchStartDist / currentDist;
        _currentHeight = Mathf.Clamp(_pinchStartHeight * ratio * PinchZoomSens, MinHeight, MaxHeight);
        transform.position = new Vector3(transform.position.x, _currentHeight, transform.position.z);
    }
}
