using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;

/// <summary>
/// GAME 1303 Tier 7 — A shelf space that holds a stack of one product. Look at it holding
/// a matching box and press Pickup to stock it; customers take from it. Store Simulator.
///
/// SETUP
///  1. Empty GameObject on the shelf + BoxCollider (trigger, roughly the shelf space)
///     + this script, on a layer the player's Pickup ray can hit.
///  2. Assigned Product: what belongs here. Leave empty for "any product" and the first
///     box stocked claims the slot.
///  3. Capacity: how many units fit.
///  4. Item Anchor: an empty child where the products sit. The slot instantiates the
///     product's World Prefab in a small grid (Rows/Columns/Spacing) so a stocked shelf
///     actually looks stocked.
///  5. Stocking: with the player looking at the slot and carrying a matching Carryable,
///     the Pickup key transfers Units Per Press units. That's the loop from the reference
///     games — repetitive on purpose, satisfying because it's visible.
///  6. OnStockChanged(current, capacity) -> a small floating label if you want one.
///
/// The price is on the ProductType, not here, so a slot doesn't need its own economy.
/// </summary>
public class ShelfSlot : MonoBehaviour
{
    [Header("What goes here")]
    public ProductType assignedProduct;
    public int capacity = 12;
    public int unitsPerPress = 3;

    [Header("Display")]
    public Transform itemAnchor;
    public int columns = 3;
    public int rows = 2;
    public Vector3 spacing = new Vector3(0.25f, 0.25f, 0.25f);

    [Header("Interaction")]
    public float lookRange = 3f;
    public LayerMask playerLayers;

    [Header("Feedback")]
    public string stockSfx = "stock";

    [Header("Events")]
    public UnityEvent<int, int> OnStockChanged;    // (current, capacity)
    public UnityEvent OnEmptied;
    public UnityEvent OnFilled;

    public int Stock { get; private set; }
    public bool IsEmpty => Stock <= 0;
    public bool IsFull => Stock >= capacity;

    private readonly List<GameObject> displayed = new List<GameObject>();
    private PickupCarry watcher;

    void Start()
    {
        if (itemAnchor == null) itemAnchor = transform;
        RefreshDisplay();
        OnStockChanged?.Invoke(Stock, capacity);
    }

    void Update()
    {
        // Is a player looking at us while carrying the right box?
        if (watcher == null)
        {
            var p = GameObject.FindGameObjectWithTag("Player");
            if (p != null) watcher = p.GetComponent<PickupCarry>();
            if (watcher == null) return;
        }

        if (!watcher.IsCarrying) return;
        if (Keyboard.current == null || !Keyboard.current.eKey.wasPressedThisFrame) return;

        // Confirm they're actually aiming at this slot
        var cam = watcher.viewCamera;
        if (cam == null) return;
        if (!Physics.Raycast(cam.transform.position, cam.transform.forward, out RaycastHit hit, lookRange)) return;
        if (hit.collider.gameObject != gameObject && hit.collider.transform.parent != transform) return;

        TryStockFrom(watcher.Held);
    }

    /// <summary>Move units from a carried box onto this shelf.</summary>
    public bool TryStockFrom(Carryable box)
    {
        if (box == null || box.product == null || IsFull) return false;
        if (assignedProduct != null && box.product != assignedProduct) return false;
        if (assignedProduct == null) assignedProduct = box.product;

        int room = capacity - Stock;
        int taken = box.TakeUnits(Mathf.Min(unitsPerPress, room));
        if (taken <= 0) return false;

        Stock += taken;
        RefreshDisplay();
        if (!string.IsNullOrEmpty(stockSfx)) AudioManager.Instance?.PlaySFX(stockSfx);
        OnStockChanged?.Invoke(Stock, capacity);
        if (IsFull) OnFilled?.Invoke();
        return true;
    }

    /// <summary>A customer takes one. Returns false if the shelf is bare.</summary>
    public bool TakeOne()
    {
        if (IsEmpty) return false;
        Stock--;
        RefreshDisplay();
        OnStockChanged?.Invoke(Stock, capacity);
        if (IsEmpty) OnEmptied?.Invoke();
        return true;
    }

    void RefreshDisplay()
    {
        foreach (var o in displayed) if (o != null) Destroy(o);
        displayed.Clear();

        if (assignedProduct == null || assignedProduct.worldPrefab == null) return;

        int perLayer = Mathf.Max(1, columns * rows);
        int show = Mathf.Min(Stock, perLayer * 3);   // don't spawn hundreds of meshes

        for (int i = 0; i < show; i++)
        {
            int layer = i / perLayer;
            int inLayer = i % perLayer;
            int col = inLayer % columns;
            int row = inLayer / columns;

            Vector3 local = new Vector3(col * spacing.x, layer * spacing.y, row * spacing.z);
            var obj = Instantiate(assignedProduct.worldPrefab, itemAnchor);
            obj.transform.localPosition = local;
            obj.transform.localRotation = Quaternion.identity;

            // Display only — no physics, no grabbing
            foreach (var rb in obj.GetComponentsInChildren<Rigidbody>()) rb.isKinematic = true;
            foreach (var c in obj.GetComponentsInChildren<Collider>()) c.enabled = false;

            displayed.Add(obj);
        }
    }
}