using System;
using UnityEngine;
using UnityEngine.InputSystem;

public class PseudoIsometricCamera : MonoBehaviour
{
    [Header("References")]
    public Camera cam;
    public Vector3 camTarget;
    public Vector3 camPosition;
    public Vector3 camDistance;
    private Vector3 baseCamDistance;

    [Header("Movement Settings")]
    private float baseSpeed = 5.0f;
    private Vector3 planarMoveVector;

    [Header("Rotation Settings")]
    private float rotateStepDeg = 30.0f;
    private float currentUpRotationInDeg = 0.0f;
    private float targetUpRotationInDeg = 0.0f;
    private float rotationLerpSpeed = 10.0f;
    private Quaternion currentRotationQuaternion;

    [Header("Zoom Settings")]
    private float minZoomMultiplier = .4f;
    private float maxZoomMultiplier = 2.0f;
    private float zoomStep = 0.2f;
    private float currentZoomScale = 1.0f;
    private float targetZoomScale = 1.0f;
    private float zoomLerpSpeed = 10.0f;

    public InputActionAsset inputAction;
    private InputAction moveInputAction;
    private InputAction rotateInputAction;
    private InputAction zoomInputAction;


    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        setCameraParameters();
        SetInputActions();
    }

    // Update is called once per frame
    void Update()
    {
        HandleZoom();
        HandleRotation();
        HandleMovement();
        UpdateCamera();
    }

    private void HandleMovement()
    {
        if (moveInputAction == null) return;
        Vector2 moveInput = moveInputAction.ReadValue<Vector2>();

        // safegauard
        if (moveInput.magnitude < 0.02f) return;

        // move relative to camera rotation on XZ plane
        Vector3 forward = cam.transform.forward;
        Vector3 right = cam.transform.right;

        forward.y = 0;
        right.y = 0;

        forward.Normalize();
        right.Normalize();

        planarMoveVector = (right * moveInput.x + forward * moveInput.y)
                     * baseSpeed * Time.deltaTime;

    }

    void HandleRotation()
    {
        // safeguard
        if (rotateInputAction == null) return;

        if (rotateInputAction.WasPressedThisFrame())
        {
            float rotateAxisSign = -rotateInputAction.ReadValue<float>();
            float rotationInDeg = rotateStepDeg * rotateAxisSign;
            targetUpRotationInDeg += rotationInDeg;
            targetUpRotationInDeg %= 360.0f;
        }

        currentUpRotationInDeg = Mathf.LerpAngle(currentUpRotationInDeg, targetUpRotationInDeg, rotationLerpSpeed * Time.deltaTime);
        currentUpRotationInDeg %= 360.0f;
        if (Mathf.Abs(currentUpRotationInDeg - targetUpRotationInDeg) < 0.05f)
        {
            currentUpRotationInDeg = targetUpRotationInDeg;
            return;
        }
    }

    void HandleZoom()
    {
        // safeguard
        if (zoomInputAction == null) return;
        // only react once per key scroll/press
        if (zoomInputAction.WasPressedThisFrame())
        {
            float zoomDelta = zoomInputAction.ReadValue<float>();
            targetZoomScale -= zoomDelta * zoomStep;
            targetZoomScale = Mathf.Clamp(targetZoomScale, minZoomMultiplier, maxZoomMultiplier);
        }

        // smoothly interpolate actual zoom
        currentZoomScale = Mathf.Lerp(currentZoomScale, targetZoomScale, zoomLerpSpeed * Time.deltaTime);
        //if no visible change, skip
        if (Mathf.Abs(currentZoomScale - targetZoomScale) < 0.02f)
        {
            currentZoomScale = targetZoomScale;
            return;
        }
    }

    void setCameraParameters()
    {
        // safeguard to check if everything nicely assigned
        camTarget = Vector3.zero;
        camPosition = new Vector3(0, 6, -10);

        if (cam == null || camTarget == null || camPosition == null)
        {
            Debug.LogError("CameraRig setup incomplete! Assign all references.");
            return;
        }

        cam.transform.position = camPosition;
        camDistance = camTarget - camPosition;
        baseCamDistance = camDistance;
        cam.transform.forward = camDistance.normalized;
    }

    public void SetInputActions()
    {
        var cameraMap = inputAction.FindActionMap("CameraActionMap", true);

        moveInputAction = cameraMap.FindAction("Movement", true);
        moveInputAction.Enable();

        rotateInputAction = cameraMap.FindAction("Rotate", true);
        rotateInputAction.Enable();

        zoomInputAction = cameraMap.FindAction("Zoom", true);
        zoomInputAction.Enable();
    }

    private void UpdateCamera()
    {
        // change camera variables if there is rotation or zoom
        if (currentUpRotationInDeg != targetUpRotationInDeg || currentZoomScale != targetZoomScale)
        {
            currentRotationQuaternion = Quaternion.Euler(0, currentUpRotationInDeg, 0);
            camPosition = camTarget - currentRotationQuaternion * baseCamDistance * currentZoomScale;
        }

        // change camera variable if there is movement
        if (planarMoveVector.magnitude > 0.02f)
        {
            camTarget += planarMoveVector;
            camPosition += planarMoveVector;
            planarMoveVector = Vector3.zero;
        }

        // apply changes if there is change in camera position
        if (cam.transform.position != camPosition)
        {
            camDistance = camTarget - camPosition;
            cam.transform.position = camPosition;
            cam.transform.LookAt(camTarget);
        }
    }

}