using System.Collections;
using _PROJECT.Multiplayer.NewBow;
using _PROJECT.Scripts.Bow;
using FishNet.Object;
using UnityEngine;

public class ArrowCaster : NetworkBehaviour
{
    [SerializeField] private Transform tip;
    [SerializeField] private LayerMask layerMask = ~0;
    [SerializeField] private float speed = 2000f;

    private Vector3 lastPosition = Vector3.zero;

    private readonly float _maxLifeTime = 30f;
    private float _lifeTime;
    private bool _launched;

    private Rigidbody _rigidbody;
    private Arrow _arrow;

    private RaycastHit _hit;

    private void Awake()
    {
        _rigidbody = GetComponent<Rigidbody>();
        _arrow = GetComponent<Arrow>();
        _launched = false;
    }

    private void Update()
    {
        if (!IsServer) return;
        if (!_launched) return;
        if (Time.time - _lifeTime > _maxLifeTime)
            Despawn(DespawnType.Pool);
    }

    private bool CheckForCollision(out RaycastHit hit)
    {
        if (lastPosition == Vector3.zero)
            lastPosition = tip.position;

        bool collided = Physics.Linecast(lastPosition, tip.position, out hit, layerMask);

        lastPosition = collided ? lastPosition : tip.position;

        return collided;
    }

    public void LaunchArrow(TwoHandedBowNotch notch)
    {
        if (!IsServer) return;
        Debug.Log("Launching arrow");

        transform.rotation = Quaternion.LookRotation(notch.GetLaunchDirection());

        Debug.Log("Launching arrow with direction " + notch.GetLaunchDirection());
        Debug.Log("Launching arrow with pull amount " + notch.GetLastKnownPullAmount());

        LaunchArrow(notch.GetLaunchDirection().normalized * (notch.GetLastKnownPullAmount() * speed));
    }

    private void LaunchArrow(Vector3 force)
    {
        _lifeTime = Time.time;
        _launched = true;
        RemoveOwnership();
        transform.parent = null;
        EnablePhysics();
        ApplyForce(force);
        StartCoroutine(LaunchRoutine());
    }

    private void ApplyForce(Vector3 force)
    {
        _rigidbody.AddForce(force);
    }

    private IEnumerator LaunchRoutine()
    {
        // Set direction while flying
        while (!CheckForCollision(out _hit))
        {
            SetDirection();
            yield return null;
        }

        Debug.Log(_hit.transform.name);

        // Once the arrow has stopped flying
        DisablePhysics();
        CheckForHittable(_hit);
    }

    private void SetDirection()
    {
        if (_rigidbody.velocity.z > 0.5f)
            transform.forward = _rigidbody.velocity;
    }

    private void DisablePhysics()
    {
        _rigidbody.isKinematic = true;
        _rigidbody.useGravity = false;
    }

    private void EnablePhysics()
    {
        _rigidbody.isKinematic = false;
        _rigidbody.useGravity = true;
    }

    private void CheckForHittable(RaycastHit hit)
    {
        if (hit.transform.TryGetComponent(out IArrowHittable hittable))
            hittable.Hit(_arrow);
    }
}