// EN: Inventory item aggregate root.
// VI: Aggregate root InventoryItem.

using InventoryService.Domain.Events;
using InventoryService.Domain.Exceptions;
using InventoryService.Domain.SeedWork;

namespace InventoryService.Domain.AggregatesModel.InventoryAggregate;

/// <summary>
/// EN: Inventory item aggregate root - manages stock levels.
/// VI: Aggregate root InventoryItem - quản lý mức tồn kho.
/// </summary>
public class InventoryItem : Entity, IAggregateRoot
{
    private Guid _productId;
    private Guid _shopId;
    private string? _name;
    private ItemType _itemType = null!;
    private string _unit = "pcs";
    private decimal _costPerUnit;
    private string? _supplierName;
    private DateTime? _expiryDate;
    private int _quantity;
    private int _reservedQuantity;
    private int _reorderLevel;
    private DateTime _createdAt;
    private DateTime? _updatedAt;

    private readonly List<InventoryTransaction> _transactions = new();

    /// <summary>
    /// EN: Product ID from Catalog Service.
    /// VI: ID sản phẩm từ Catalog Service.
    /// </summary>
    public Guid ProductId => _productId;

    /// <summary>
    /// EN: Shop ID that owns this inventory.
    /// VI: ID shop sở hữu inventory này.
    /// </summary>
    public Guid ShopId => _shopId;

    /// <summary>
    /// EN: Item name (for raw materials not linked to catalog).
    /// VI: Tên item (cho nguyên liệu thô không liên kết catalog).
    /// </summary>
    public string? Name => _name;

    /// <summary>
    /// EN: Item type (RawMaterial, FinishedGood, Consumable).
    /// VI: Loại item (Nguyên liệu thô, Thành phẩm, Vật tư tiêu hao).
    /// </summary>
    public ItemType ItemType => _itemType;

    /// <summary>
    /// EN: Item type ID for EF Core mapping.
    /// VI: Item type ID cho EF Core mapping.
    /// </summary>
    public int ItemTypeId { get; private set; }

    /// <summary>
    /// EN: Unit of measure (e.g., "g", "ml", "pcs", "kg", "L").
    /// VI: Đơn vị tính (ví dụ: "g", "ml", "cái", "kg", "L").
    /// </summary>
    public string Unit => _unit;

    /// <summary>
    /// EN: Cost per unit (purchase price).
    /// VI: Giá mỗi đơn vị (giá mua).
    /// </summary>
    public decimal CostPerUnit => _costPerUnit;

    /// <summary>
    /// EN: Supplier name reference.
    /// VI: Tên nhà cung cấp.
    /// </summary>
    public string? SupplierName => _supplierName;

    /// <summary>
    /// EN: Expiry date for perishable items.
    /// VI: Ngày hết hạn cho hàng dễ hỏng.
    /// </summary>
    public DateTime? ExpiryDate => _expiryDate;

    /// <summary>
    /// EN: Total quantity in stock.
    /// VI: Tổng số lượng trong kho.
    /// </summary>
    public int Quantity => _quantity;

    /// <summary>
    /// EN: Reserved quantity for orders.
    /// VI: Số lượng đặt trước cho đơn hàng.
    /// </summary>
    public int ReservedQuantity => _reservedQuantity;

    /// <summary>
    /// EN: Available quantity (total - reserved).
    /// VI: Số lượng khả dụng (tổng - đặt trước).
    /// </summary>
    public int AvailableQuantity => _quantity - _reservedQuantity;

    /// <summary>
    /// EN: Reorder level - alert when stock goes below this.
    /// VI: Mức đặt hàng lại - cảnh báo khi tồn kho dưới mức này.
    /// </summary>
    public int ReorderLevel => _reorderLevel;

    /// <summary>
    /// EN: Transaction history.
    /// VI: Lịch sử giao dịch.
    /// </summary>
    public IReadOnlyCollection<InventoryTransaction> Transactions => _transactions.AsReadOnly();

    /// <summary>
    /// EN: Creation timestamp.
    /// VI: Thời gian tạo.
    /// </summary>
    public DateTime CreatedAt => _createdAt;

    /// <summary>
    /// EN: Last update timestamp.
    /// VI: Thời gian cập nhật cuối.
    /// </summary>
    public DateTime? UpdatedAt => _updatedAt;

    /// <summary>
    /// EN: Private constructor for EF Core.
    /// VI: Constructor private cho EF Core.
    /// </summary>
    protected InventoryItem()
    {
    }

    /// <summary>
    /// EN: Create a new inventory item (finished good linked to catalog product).
    /// VI: Tạo inventory item mới (thành phẩm liên kết với sản phẩm catalog).
    /// </summary>
    public InventoryItem(Guid productId, Guid shopId, int reorderLevel = 10)
    {
        if (productId == Guid.Empty)
            throw new DomainException("Product ID cannot be empty");
        if (shopId == Guid.Empty)
            throw new DomainException("Shop ID cannot be empty");

        Id = Guid.NewGuid();
        _productId = productId;
        _shopId = shopId;
        _itemType = ItemType.FinishedGood;
        ItemTypeId = _itemType.Id;
        _unit = "pcs";
        _costPerUnit = 0;
        _quantity = 0;
        _reservedQuantity = 0;
        _reorderLevel = reorderLevel;
        _createdAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Create a new inventory item with full details (for raw materials / consumables).
    /// VI: Tạo inventory item mới với đầy đủ thông tin (cho nguyên liệu thô / vật tư tiêu hao).
    /// </summary>
    public InventoryItem(
        Guid shopId,
        string name,
        ItemType itemType,
        string unit,
        decimal costPerUnit,
        int initialQuantity = 0,
        int reorderLevel = 10,
        string? supplierName = null,
        DateTime? expiryDate = null)
    {
        if (shopId == Guid.Empty)
            throw new DomainException("Shop ID cannot be empty");
        if (string.IsNullOrWhiteSpace(name))
            throw new DomainException("Name is required for raw material items");
        if (itemType == null)
            throw new DomainException("Item type is required");
        if (string.IsNullOrWhiteSpace(unit))
            throw new DomainException("Unit is required");
        if (costPerUnit < 0)
            throw new DomainException("Cost per unit cannot be negative");

        Id = Guid.NewGuid();
        _productId = Guid.NewGuid(); // EN: Self-generated ID for non-catalog items / VI: ID tự sinh cho items không từ catalog
        _shopId = shopId;
        _name = name;
        _itemType = itemType;
        ItemTypeId = itemType.Id;
        _unit = unit;
        _costPerUnit = costPerUnit;
        _supplierName = supplierName;
        _expiryDate = expiryDate;
        _quantity = initialQuantity;
        _reservedQuantity = 0;
        _reorderLevel = reorderLevel;
        _createdAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Update item details (name, unit, cost, supplier, expiry).
    /// VI: Cập nhật thông tin item (tên, đơn vị, giá, nhà cung cấp, hạn dùng).
    /// </summary>
    public void UpdateItemDetails(
        string? name = null,
        string? unit = null,
        decimal? costPerUnit = null,
        string? supplierName = null,
        DateTime? expiryDate = null)
    {
        if (name != null) _name = name;
        if (unit != null) _unit = unit;
        if (costPerUnit.HasValue)
        {
            if (costPerUnit.Value < 0)
                throw new DomainException("Cost per unit cannot be negative");
            _costPerUnit = costPerUnit.Value;
        }
        _supplierName = supplierName ?? _supplierName;
        _expiryDate = expiryDate ?? _expiryDate;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Stock in operation.
    /// VI: Thao tác nhập kho.
    /// </summary>
    public void StockIn(int amount, string? notes = null, Guid? referenceId = null, string? invoiceImageUrl = null, decimal? unitCost = null)
    {
        if (amount <= 0)
            throw new DomainException("Stock in amount must be positive");

        _quantity += amount;
        _updatedAt = DateTime.UtcNow;

        var transaction = new InventoryTransaction(Id, TransactionType.In, amount, referenceId, notes, invoiceImageUrl, unitCost);
        _transactions.Add(transaction);

        AddDomainEvent(new StockChangedDomainEvent(this));
    }

    /// <summary>
    /// EN: Stock out operation.
    /// VI: Thao tác xuất kho.
    /// </summary>
    public void StockOut(int amount, string? notes = null, Guid? referenceId = null)
    {
        if (amount <= 0)
            throw new DomainException("Stock out amount must be positive");
        if (AvailableQuantity < amount)
            throw new DomainException($"Insufficient stock. Available: {AvailableQuantity}, Requested: {amount}");

        _quantity -= amount;
        _updatedAt = DateTime.UtcNow;

        var transaction = new InventoryTransaction(Id, TransactionType.Out, -amount, referenceId, notes);
        _transactions.Add(transaction);

        AddDomainEvent(new StockChangedDomainEvent(this));
    }

    /// <summary>
    /// EN: Reserve stock for order.
    /// VI: Đặt trước tồn kho cho đơn hàng.
    /// </summary>
    public void Reserve(int amount, Guid orderId)
    {
        if (amount <= 0)
            throw new DomainException("Reserve amount must be positive");
        if (AvailableQuantity < amount)
            throw new DomainException($"Insufficient stock to reserve. Available: {AvailableQuantity}, Requested: {amount}");

        _reservedQuantity += amount;
        _updatedAt = DateTime.UtcNow;

        var transaction = new InventoryTransaction(Id, TransactionType.Reserve, amount, orderId);
        _transactions.Add(transaction);
    }

    /// <summary>
    /// EN: Release reservation.
    /// VI: Giải phóng đặt trước.
    /// </summary>
    public void ReleaseReservation(int amount, Guid orderId)
    {
        if (amount <= 0)
            throw new DomainException("Release amount must be positive");
        if (_reservedQuantity < amount)
            throw new DomainException("Cannot release more than reserved");

        _reservedQuantity -= amount;
        _updatedAt = DateTime.UtcNow;

        var transaction = new InventoryTransaction(Id, TransactionType.Release, -amount, orderId);
        _transactions.Add(transaction);
    }

    /// <summary>
    /// EN: Set reorder level (low stock alert threshold).
    /// VI: Đặt mức đặt hàng lại (ngưỡng cảnh báo stock thấp).
    /// </summary>
    public void SetReorderLevel(int reorderLevel)
    {
        if (reorderLevel < 0)
            throw new DomainException("Reorder level cannot be negative");

        _reorderLevel = reorderLevel;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Manual adjustment (stocktake).
    /// VI: Điều chỉnh thủ công (kiểm kê).
    /// </summary>
    public void Adjust(int newQuantity, string notes)
    {
        var diff = newQuantity - _quantity;
        _quantity = newQuantity;
        _updatedAt = DateTime.UtcNow;

        var transaction = new InventoryTransaction(Id, TransactionType.Adjustment, diff, null, notes);
        _transactions.Add(transaction);

        AddDomainEvent(new StockChangedDomainEvent(this));
    }

    /// <summary>
    /// EN: Record wastage/shrinkage (expired, damaged, spilled items).
    /// VI: Ghi nhận hao hụt (hàng hết hạn, hư hỏng, đổ tràn).
    /// </summary>
    public void RecordWastage(int amount, string reason, string? notes = null)
    {
        if (amount <= 0) throw new DomainException("Wastage amount must be positive");
        if (string.IsNullOrWhiteSpace(reason)) throw new DomainException("Wastage reason is required");
        _quantity -= amount;
        _updatedAt = DateTime.UtcNow;
        var transaction = new InventoryTransaction(Id, TransactionType.Wastage, -amount, null, $"{reason}: {notes}");
        _transactions.Add(transaction);
        AddDomainEvent(new StockChangedDomainEvent(this));
    }
}
