using AfterHours.Data;
using AfterHours.Interaction;
using AfterHours.Networking;
using Unity.Netcode;
using UnityEngine;
namespace AfterHours.Shop
{
///
/// Ein Regalfach: hält genau eine Produktsorte. Auffüllen geht über
/// Interact(), Entnahme durch Kunden serverseitig über TakeOne().
///
public sealed class ShelfSlot : NetworkBehaviour, IInteractable, IPourTarget
{
[SerializeField] private ProductCatalog _catalog;
[SerializeField] private int _capacityUnits = 12;
[Tooltip("Ist dieses Fach vom Schaufenster aus einsehbar? Setzt der Level-Designer.")]
[SerializeField] private bool _exposedToStreet;
[SerializeField] private Transform _displayRoot;
[Header("Anzeige-Raster (sauber in Reihen eingeräumt)")]
[SerializeField] private int _displayColumns = 4;
[SerializeField] private int _displayRows = 3;
[SerializeField] private Vector3 _displaySpacing = new(0.1f, 0.1f, 0.1f);
private readonly NetworkVariable _productId = new(-1);
private readonly NetworkVariable _quantity = new(0);
private readonly NetworkVariable _price = new(0f);
public int ProductId => _productId.Value;
public int Quantity => _quantity.Value;
public float Price => _price.Value;
public bool ExposedToStreet => _exposedToStreet;
public bool IsEmpty => _quantity.Value <= 0;
public bool IsFull => _quantity.Value >= Capacity();
// Explizite Implementierung, weil "transform" (Component) den Namen schon belegt.
Transform IPourTarget.Transform => transform;
private PackageDisplay _packageDisplay;
private void Awake() => _packageDisplay = new PackageDisplay(_displayRoot, _displayColumns, _displayRows, _displaySpacing);
/// Weltposition des Rasterplatzes mit gegebenem Index – Ziel fürs Flug-Visual (siehe PlayerCarry).
public Vector3 GetSlotWorldPosition(int index)
{
return _displayRoot != null ? _displayRoot.TransformPoint(_packageDisplay.SlotLocalPosition(index)) : transform.position;
}
public override void OnNetworkSpawn()
{
_quantity.OnValueChanged += (_, _) => RefreshVisuals();
_productId.OnValueChanged += (_, _) => RefreshVisuals();
RefreshVisuals();
if (IsServer) ShelfRegistry.Instance.Register(this);
}
public override void OnNetworkDespawn()
{
if (IsServer) ShelfRegistry.Instance.Unregister(this);
}
public string GetPrompt(ulong clientId)
{
if (IsEmpty) return "Regal befüllen";
var product = _catalog.Get(_productId.Value);
string name = product != null ? product.DisplayName : "?";
return $"{name} — {_quantity.Value}/{Capacity()} — {_price.Value:0.00} €";
}
/// Server: Spieler füllt aus der Hand auf.
public void Interact(NetworkObject actor)
{
if (!IsServer) return;
// TODO: Debug.Log-Zeilen entfernen, sobald Regal-Befüllen zuverlässig läuft.
if (!actor.TryGetComponent(out var hands) || hands.IsEmpty)
{
Debug.Log("[ShelfSlot] Abbruch: keine PlayerHands am Actor oder Hände leer.");
return;
}
int incomingProductId = hands.ProductId;
// Fach ist mit anderer Ware belegt -> nichts tun.
if (!IsEmpty && _productId.Value != incomingProductId)
{
Debug.Log($"[ShelfSlot] Abbruch: Fach belegt mit ProductId {_productId.Value}, Hand trägt {incomingProductId}.");
return;
}
int free = Capacity() - _quantity.Value;
if (free <= 0)
{
Debug.Log($"[ShelfSlot] Abbruch: Fach voll ({_quantity.Value}/{Capacity()}).");
return;
}
int moved = hands.TryGive(Mathf.Min(free, hands.Quantity));
if (moved <= 0)
{
Debug.Log("[ShelfSlot] Abbruch: TryGive() lieferte 0 Stück.");
return;
}
if (IsEmpty)
{
_productId.Value = incomingProductId;
var def = _catalog.Get(_productId.Value);
if (def != null && _price.Value <= 0f) _price.Value = def.RecommendedPrice;
}
_quantity.Value += moved;
Debug.Log($"[ShelfSlot] Eingeräumt: {moved} Stück, jetzt {_quantity.Value}/{Capacity()}.");
}
/// Server: ein Stück aus einem IPourSource einräumen (siehe PlayerCarry).
public bool TryAddOne(int productId)
{
if (!IsServer) return false;
if (!IsEmpty && _productId.Value != productId) return false;
if (_quantity.Value >= Capacity()) return false;
if (IsEmpty)
{
_productId.Value = productId;
var def = _catalog.Get(productId);
if (def != null && _price.Value <= 0f) _price.Value = def.RecommendedPrice;
}
_quantity.Value++;
return true;
}
/// Server: Kunde entnimmt ein Stück.
public bool TakeOne()
{
if (!IsServer || IsEmpty) return false;
_quantity.Value--;
if (_quantity.Value == 0) _productId.Value = -1;
return true;
}
/// Server: Preis setzen (vom Preis-UI aus).
public void SetPrice(float price)
{
if (!IsServer) return;
_price.Value = Mathf.Max(0.01f, price);
}
/// Kapazität in Stück, abhängig von der Produktgröße.
private int Capacity()
{
var product = _catalog.Get(_productId.Value);
int size = product != null ? (int)product.ShelfSize : 1;
return Mathf.Max(1, _capacityUnits / size);
}
private void RefreshVisuals()
{
var product = _catalog != null ? _catalog.Get(_productId.Value) : null;
GameObject prefab = product != null ? product.PackagePrefab : null;
_packageDisplay.SetTarget(prefab, _quantity.Value, Capacity());
}
}
}