using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;

/// <summary>
/// GAME 1303 Tier 7 — Pick a box up, carry it in front of you, put it down or throw it.
/// Store Simulator's basic verb.
///
/// SETUP
///  1. Add to the FirstPersonController player. Create an empty child of the CAMERA
///     called "CarryPoint" about 0.8 units forward and slightly down; drag it in.
///  2. Carryable objects: Rigidbody + Collider, on a layer called "Carryable", with a
///     Carryable component (see the other script).
///  3. Input: add a "Pickup" Button action (E, gamepad South/A). Throw uses "Attack"
///     (left mouse) — or set Can Throw off and it's just place-down.
///  4. Reach is how far you can grab from. The crosshair-ray uses the camera's forward,
///     so students should keep the camera in the CameraPivot as set up by the controller.
///  5. While carrying, OnCarryChanged fires with the object — use it to show the product
///     name and count on the HUD.
///
/// Carried objects become kinematic and are parented to the carry point, which is the
/// simple approach. It can clip through walls; that's a known trade and fine for a class
/// project. The physics-joint alternative is a good stretch goal for Sanad if he wants it.
/// </summary>
public class PickupCarry : MonoBehaviour
{
    [Header("Reach")]
    public Transform carryPoint;
    public Camera viewCamera;
    public float reach = 2.5f;
    public LayerMask carryableLayers;

    [Header("Throwing")]
    public bool canThrow = true;
    public float throwForce = 6f;
    public float placeDistance = 1.2f;

    [Header("Feedback")]
    public string pickupSfx = "pickup";
    public string dropSfx = "drop";

    [Header("Events")]
    public UnityEvent<Carryable> OnPickedUp;
    public UnityEvent OnDropped;
    public UnityEvent<Carryable> OnCarryChanged;

    public Carryable Held { get; private set; }
    public bool IsCarrying => Held != null;

    private Rigidbody heldBody;

    void Awake()
    {
        if (viewCamera == null) viewCamera = Camera.main;
    }

    void OnPickup(InputValue v)
    {
        if (!v.isPressed) return;
        if (IsCarrying) Drop(false);
        else TryPickUp();
    }

    void OnAttack(InputValue v)
    {
        if (!v.isPressed || !IsCarrying || !canThrow) return;
        Drop(true);
    }

    void TryPickUp()
    {
        if (viewCamera == null) return;
        Ray ray = new Ray(viewCamera.transform.position, viewCamera.transform.forward);
        if (!Physics.Raycast(ray, out RaycastHit hit, reach, carryableLayers)) return;

        var carryable = hit.collider.GetComponentInParent<Carryable>();
        if (carryable == null || !carryable.canBePickedUp) return;

        Held = carryable;
        heldBody = carryable.GetComponent<Rigidbody>();
        if (heldBody != null) { heldBody.isKinematic = true; heldBody.detectCollisions = false; }

        carryable.transform.SetParent(carryPoint);
        carryable.transform.localPosition = Vector3.zero;
        carryable.transform.localRotation = Quaternion.identity;
        carryable.OnPickedUp(this);

        if (!string.IsNullOrEmpty(pickupSfx)) AudioManager.Instance?.PlaySFX(pickupSfx);
        OnPickedUp?.Invoke(carryable);
        OnCarryChanged?.Invoke(carryable);
    }

    public void Drop(bool thrown)
    {
        if (!IsCarrying) return;

        var carryable = Held;
        carryable.transform.SetParent(null);

        if (heldBody != null)
        {
            heldBody.isKinematic = false;
            heldBody.detectCollisions = true;
            if (thrown && viewCamera != null)
                heldBody.AddForce(viewCamera.transform.forward * throwForce, ForceMode.Impulse);
            else if (viewCamera != null)
                carryable.transform.position = viewCamera.transform.position + viewCamera.transform.forward * placeDistance;
        }

        carryable.OnDropped();
        Held = null;
        heldBody = null;

        if (!string.IsNullOrEmpty(dropSfx)) AudioManager.Instance?.PlaySFX(dropSfx);
        OnDropped?.Invoke();
        OnCarryChanged?.Invoke(null);
    }

    /// <summary>ShelfSlot calls this when it takes the box out of your hands.</summary>
    public void ClearHeld()
    {
        Held = null;
        heldBody = null;
        OnCarryChanged?.Invoke(null);
    }
}