using System;
using UnityEngine;

[RequireComponent(typeof(Camera))]
public class CameraController : MonoBehaviour
{
    [SerializeField] private Transform _lookPoint;
    [SerializeField] private LayerMask _ignoreLayers;

    [SerializeField] private float _sensitivityX = 10F;
    [SerializeField] private float _sensitivityY = 10F;
    [SerializeField] private float _minimumX = -360F;
    [SerializeField] private float _maximumX = 360F;
    [SerializeField] private float _minimumY = -60F;
    [SerializeField] private float _maximumY = 60F;
    [SerializeField] private float _distance = 5.0f;
    [SerializeField] private float _distanceMin = .5f;
    [SerializeField] private float _distanceMax = 15f;

    public float Sensitivity => _sensitivityX;

    public Vector2 AimPoint { get; private set; }

    private float _rotationX = 0F;
    private float _rotationY = 0F;
    private bool _controlledByUser = true;
    private Quaternion _originalRotation;

    public void SetControlled(bool controlled)
    {
        _controlledByUser = controlled;
    }

    public void SetSensitivity(float sensitivity)
    {
        _sensitivityX = sensitivity;
        _sensitivityY = sensitivity;
    }
    
    private void Start()
    {
        _originalRotation = transform.localRotation;
    }

    private void LateUpdate()
    {
        if (Math.Abs(Time.timeScale) < 0.001f)
            return;

        if (_controlledByUser)
        {
            _rotationX += Input.GetAxis("Mouse X") * _sensitivityX;
            _rotationY += Input.GetAxis("Mouse Y") * _sensitivityY;
            _distance = Mathf.Clamp(_distance - Input.GetAxis("Mouse ScrollWheel") * 5, _distanceMin, _distanceMax);
        }

        _rotationX = ClampAngle(_rotationX, _minimumX, _maximumX);
        _rotationY = ClampAngle(_rotationY, _minimumY, _maximumY);
        Quaternion xQuaternion = Quaternion.AngleAxis(_rotationX, Vector3.up);
        Quaternion yQuaternion = Quaternion.AngleAxis(_rotationY, -Vector3.right);
        transform.localRotation = _originalRotation * xQuaternion * yQuaternion;

        Vector3 lookPoint = _lookPoint.transform.position + Vector3.up * 2;
        transform.position = _lookPoint.transform.position + Vector3.up * 2 - _distance * transform.forward;

        RaycastHit aim;
        if (Physics.Raycast(transform.position, transform.forward, out aim, _ignoreLayers))
        {
            AimPoint = aim.point;
        }

        RaycastHit hit;
        if (Physics.Linecast(lookPoint, transform.position, out hit, ~_ignoreLayers))
        {
            transform.position += transform.forward * (_distance - hit.distance + 0.1f);
        }
    }

    private static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360F)
            angle += 360F;
        if (angle > 360F)
            angle -= 360F;
        return Mathf.Clamp(angle, min, max);
    }
}