// EN: Order aggregate root - the orchestrator.
// VI: Aggregate root Order - bộ điều phối.

using OrderService.Domain.Events;
using OrderService.Domain.Exceptions;
using OrderService.Domain.SeedWork;

namespace OrderService.Domain.AggregatesModel.OrderAggregate;

/// <summary>
/// EN: Order aggregate root - orchestrates order processing across verticals.
/// VI: Aggregate root Order - điều phối xử lý đơn hàng qua các ngành dọc.
/// </summary>
public class Order : Entity, IAggregateRoot
{
    private Guid _shopId;
    private Guid? _customerId;
    private Guid? _tableId;
    private OrderStatus _status = null!;
    private decimal _totalAmount;
    private DateTime _createdAt;
    private DateTime? _updatedAt;

    private decimal _discountAmount;
    private string? _discountType;
    private string? _discountReference;

    private string? _paymentMethod;
    private string? _transactionId;
    private decimal? _amountTendered;
    private decimal? _changeAmount;

    private string? _returnReason;
    private DateTime? _returnedAt;
    private bool _isReturn;
    private Guid? _originalOrderId;

    private readonly List<OrderItem> _items = new();

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

    /// <summary>
    /// EN: Customer ID (optional for walk-in customers).
    /// VI: ID khách hàng (tùy chọn cho khách vãng lai).
    /// </summary>
    public Guid? CustomerId => _customerId;

    /// <summary>
    /// EN: Table ID (optional — for dine-in restaurant orders).
    /// VI: ID bàn (tùy chọn — cho đơn hàng dine-in nhà hàng).
    /// </summary>
    public Guid? TableId => _tableId;

    /// <summary>
    /// EN: Order status (resolved from StatusId when loaded from DB).
    /// VI: Trạng thái đơn hàng (resolve từ StatusId khi load từ DB).
    /// </summary>
    public OrderStatus Status => _status ?? Enumeration.FromValue<OrderStatus>(StatusId);

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

    /// <summary>
    /// EN: Total amount of the order.
    /// VI: Tổng số tiền của đơn hàng.
    /// </summary>
    public decimal TotalAmount => _totalAmount;

    /// <summary>
    /// EN: Order items (line items).
    /// VI: Các items trong đơn hàng (dòng hàng).
    /// </summary>
    public decimal DiscountAmount => _discountAmount;
    public string? DiscountType => _discountType;
    public string? DiscountReference => _discountReference;

    /// <summary>
    /// EN: Payment method used (cash, card, vnpay, momo).
    /// VI: Phương thức thanh toán đã dùng (cash, card, vnpay, momo).
    /// </summary>
    public string? PaymentMethod => _paymentMethod;

    /// <summary>
    /// EN: External transaction ID from payment gateway or generated for cash/card.
    /// VI: Mã giao dịch bên ngoài từ cổng thanh toán hoặc tạo cho tiền mặt/thẻ.
    /// </summary>
    public string? TransactionId => _transactionId;

    /// <summary>
    /// EN: Amount tendered by customer (for cash payments).
    /// VI: Số tiền khách đưa (cho thanh toán tiền mặt).
    /// </summary>
    public decimal? AmountTendered => _amountTendered;

    /// <summary>
    /// EN: Change returned to customer (for cash payments).
    /// VI: Tiền thối lại cho khách (cho thanh toán tiền mặt).
    /// </summary>
    public decimal? ChangeAmount => _changeAmount;

    public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly();

    /// <summary>
    /// EN: Reason for return (if this is a return order).
    /// VI: Lý do trả hàng (nếu đây là đơn trả hàng).
    /// </summary>
    public string? ReturnReason => _returnReason;

    /// <summary>
    /// EN: Timestamp when return was processed.
    /// VI: Thời gian xử lý trả hàng.
    /// </summary>
    public DateTime? ReturnedAt => _returnedAt;

    /// <summary>
    /// EN: Whether this order is a return order.
    /// VI: Đây có phải là đơn trả hàng không.
    /// </summary>
    public bool IsReturn => _isReturn;

    /// <summary>
    /// EN: Original order ID (for return/exchange orders).
    /// VI: ID đơn hàng gốc (cho đơn trả hàng/đổi hàng).
    /// </summary>
    public Guid? OriginalOrderId => _originalOrderId;

    /// <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 Order()
    {
    }

    /// <summary>
    /// EN: Create a new order.
    /// VI: Tạo đơn hàng mới.
    /// </summary>
    public Order(Guid shopId, Guid? customerId = null, Guid? tableId = null)
    {
        if (shopId == Guid.Empty)
            throw new DomainException("Shop ID cannot be empty");

        Id = Guid.NewGuid();
        _shopId = shopId;
        _customerId = customerId;
        _tableId = tableId;
        _status = OrderStatus.Draft;
        StatusId = OrderStatus.Draft.Id;
        _totalAmount = 0;
        _createdAt = DateTime.UtcNow;

        AddDomainEvent(new OrderCreatedDomainEvent(this));
    }

    /// <summary>
    /// EN: Add an item to the order.
    /// VI: Thêm item vào đơn hàng.
    /// </summary>
    public void AddItem(OrderItem item)
    {
        if (_status != OrderStatus.Draft)
            throw new DomainException("Cannot add items to non-draft orders");

        _items.Add(item);
        RecalculateTotal();
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Mark order as validated.
    /// VI: Đánh dấu đơn hàng đã xác thực.
    /// </summary>
    public void MarkAsValidated()
    {
        if (Status != OrderStatus.Draft)
            throw new DomainException($"Cannot validate order with status {Status.Name}");
        if (!_items.Any())
            throw new DomainException("Cannot validate order with no items");

        _status = OrderStatus.Validated;
        StatusId = OrderStatus.Validated.Id;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Mark order as paid with payment details.
    /// VI: Đánh dấu đơn hàng đã thanh toán với chi tiết thanh toán.
    /// </summary>
    public void MarkAsPaid(string paymentMethod, string transactionId, decimal? amountTendered = null)
    {
        if (Status != OrderStatus.Validated && Status != OrderStatus.PaymentPending)
            throw new DomainException($"Cannot mark as paid order with status {Status.Name}");

        _paymentMethod = paymentMethod;
        _transactionId = transactionId;
        _amountTendered = amountTendered;
        if (amountTendered.HasValue)
            _changeAmount = amountTendered.Value - _totalAmount;

        _status = OrderStatus.Paid;
        StatusId = OrderStatus.Paid.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new OrderPaidDomainEvent(this));
    }

    /// <summary>
    /// EN: Mark order as payment pending (waiting for online payment gateway confirmation).
    /// VI: Đánh dấu đơn hàng chờ thanh toán (đang chờ xác nhận từ cổng thanh toán trực tuyến).
    /// </summary>
    public void MarkAsPaymentPending(string paymentMethod, string transactionId)
    {
        if (Status != OrderStatus.Validated)
            throw new DomainException($"Cannot mark as payment pending order with status {Status.Name}");

        _paymentMethod = paymentMethod;
        _transactionId = transactionId;
        _status = OrderStatus.PaymentPending;
        StatusId = OrderStatus.PaymentPending.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new OrderPaymentPendingDomainEvent(this));
    }

    /// <summary>
    /// EN: Complete payment after gateway confirmation (transitions from PaymentPending to Paid).
    /// VI: Hoàn tất thanh toán sau xác nhận cổng thanh toán (chuyển từ PaymentPending sang Paid).
    /// </summary>
    public void CompletePayment(string gatewayTransactionId)
    {
        if (Status != OrderStatus.PaymentPending)
            throw new DomainException($"Cannot complete payment for order with status {Status.Name}");

        _transactionId = gatewayTransactionId;
        _status = OrderStatus.Paid;
        StatusId = OrderStatus.Paid.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new OrderPaidDomainEvent(this));
    }

    /// <summary>
    /// EN: Mark order as processing.
    /// VI: Đánh dấu đơn hàng đang xử lý.
    /// </summary>
    public void MarkAsProcessing()
    {
        if (Status != OrderStatus.Paid)
            throw new DomainException($"Cannot process order with status {Status.Name}");

        _status = OrderStatus.Processing;
        StatusId = OrderStatus.Processing.Id;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Mark order as completed.
    /// VI: Đánh dấu đơn hàng hoàn thành.
    /// </summary>
    public void MarkAsCompleted()
    {
        if (Status != OrderStatus.Processing)
            throw new DomainException($"Cannot complete order with status {Status.Name}");

        _status = OrderStatus.Completed;
        StatusId = OrderStatus.Completed.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new OrderCompletedDomainEvent(this));
    }

    /// <summary>
    /// EN: Cancel the order.
    /// VI: Hủy đơn hàng.
    /// </summary>
    public void Cancel(string reason)
    {
        if (Status == OrderStatus.Completed)
            throw new DomainException("Cannot cancel completed order");
        if (Status == OrderStatus.Cancelled)
            throw new DomainException("Order is already cancelled");

        _status = OrderStatus.Cancelled;
        StatusId = OrderStatus.Cancelled.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new OrderCancelledDomainEvent(this, reason));
    }

    /// <summary>
    /// EN: Process return — marks the order as a return order with reason and original order reference.
    /// VI: Xử lý trả hàng — đánh dấu đơn hàng là đơn trả hàng với lý do và tham chiếu đơn gốc.
    /// </summary>
    public void ProcessReturn(string reason, Guid? originalOrderId)
    {
        if (string.IsNullOrWhiteSpace(reason))
            throw new DomainException("Return reason is required / Lý do trả hàng là bắt buộc");

        _isReturn = true;
        _returnReason = reason;
        _originalOrderId = originalOrderId;
        _returnedAt = DateTime.UtcNow;
        _status = OrderStatus.Returned;
        StatusId = OrderStatus.Returned.Id;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new OrderReturnedDomainEvent(this));
    }

    /// <summary>
    /// EN: Apply discount to order.
    /// VI: Áp dụng giảm giá cho đơn hàng.
    /// </summary>
    public void ApplyDiscount(decimal amount, string? type = null, string? reference = null)
    {
        if (amount < 0) throw new DomainException("Discount amount cannot be negative");
        _discountAmount = amount;
        _discountType = type;
        _discountReference = reference;
        RecalculateTotal();
        _updatedAt = DateTime.UtcNow;
    }

    private void RecalculateTotal()
    {
        _totalAmount = _items.Sum(i => i.TotalPrice) - _discountAmount;
        if (_totalAmount < 0) _totalAmount = 0;
    }
}
