// EN: Table aggregate root for FnB.
// VI: Aggregate root Table cho FnB.

using FnbEngine.Domain.Exceptions;
using FnbEngine.Domain.SeedWork;

namespace FnbEngine.Domain.AggregatesModel.TableAggregate;

/// <summary>
/// EN: Table aggregate root - represents a dining table.
/// VI: Aggregate root Table - đại diện cho bàn ăn.
/// </summary>
public class Table : Entity, IAggregateRoot
{
    private Guid _shopId;
    private string _tableNumber = null!;
    private int _capacity;
    private string? _zone;
    private TableStatus _status = null!;
    private int? _positionX;
    private int? _positionY;
    private string? _qrToken;
    private decimal _hourlyRate;
    private DateTime _createdAt;
    private DateTime? _updatedAt;

    public Guid ShopId => _shopId;
    public string TableNumber => _tableNumber;
    public int Capacity => _capacity;
    public string? Zone => _zone;
    public TableStatus Status => _status ?? Enumeration.FromValue<TableStatus>(StatusId);
    public int StatusId { get; private set; }
    public int? PositionX => _positionX;
    public int? PositionY => _positionY;
    public string? QrToken => _qrToken;
    public decimal HourlyRate => _hourlyRate;
    public DateTime CreatedAt => _createdAt;
    public DateTime? UpdatedAt => _updatedAt;

    protected Table()
    {
    }

    public Table(Guid shopId, string tableNumber, int capacity, string? zone = null)
    {
        if (shopId == Guid.Empty)
            throw new DomainException("Shop ID cannot be empty");
        if (string.IsNullOrWhiteSpace(tableNumber))
            throw new DomainException("Table number cannot be empty");
        if (capacity <= 0)
            throw new DomainException("Capacity must be greater than zero");

        Id = Guid.NewGuid();
        _shopId = shopId;
        _tableNumber = tableNumber.Trim();
        _capacity = capacity;
        _zone = zone?.Trim();
        _status = TableStatus.Available;
        StatusId = TableStatus.Available.Id;
        _createdAt = DateTime.UtcNow;
    }

    private void EnsureStatusLoaded()
    {
        _status ??= Enumeration.FromValue<TableStatus>(StatusId);
    }

    public void MarkAsOccupied()
    {
        EnsureStatusLoaded();
        if (_status != TableStatus.Available && _status != TableStatus.Reserved)
            throw new DomainException($"Cannot occupy table with status {_status.Name}");

        _status = TableStatus.Occupied;
        StatusId = TableStatus.Occupied.Id;
        _updatedAt = DateTime.UtcNow;
    }

    public void MarkAsAvailable()
    {
        EnsureStatusLoaded();
        _status = TableStatus.Available;
        StatusId = TableStatus.Available.Id;
        _updatedAt = DateTime.UtcNow;
    }

    public void MarkAsCleaning()
    {
        EnsureStatusLoaded();
        _status = TableStatus.Cleaning;
        StatusId = TableStatus.Cleaning.Id;
        _updatedAt = DateTime.UtcNow;
    }

    public void SetHourlyRate(decimal rate)
    {
        _hourlyRate = rate;
        _updatedAt = DateTime.UtcNow;
    }

    public void SetPosition(int x, int y)
    {
        _positionX = x;
        _positionY = y;
        _updatedAt = DateTime.UtcNow;
    }

    public string GenerateQrToken()
    {
        _qrToken = Guid.NewGuid().ToString("N")[..16];
        _updatedAt = DateTime.UtcNow;
        return _qrToken;
    }

    public void ClearQrToken()
    {
        _qrToken = null;
        _updatedAt = DateTime.UtcNow;
    }
}
