using MediatR;

namespace MyService.Domain.SeedWork;

/// <summary>
/// EN: Base class for all domain entities.
/// VI: Lớp cơ sở cho tất cả các entity trong domain.
/// </summary>
public abstract class Entity
{
    private int? _requestedHashCode;
    private Guid _id;
    private List<INotification> _domainEvents = new();

    /// <summary>
    /// EN: Unique identifier for the entity.
    /// VI: Định danh duy nhất cho entity.
    /// </summary>
    public virtual Guid Id
    {
        get => _id;
        protected set => _id = value;
    }

    /// <summary>
    /// EN: Domain events raised by this entity.
    /// VI: Các domain event được phát ra bởi entity này.
    /// </summary>
    public IReadOnlyCollection<INotification> DomainEvents => _domainEvents.AsReadOnly();

    /// <summary>
    /// EN: Add a domain event to be dispatched.
    /// VI: Thêm một domain event để dispatch.
    /// </summary>
    public void AddDomainEvent(INotification eventItem)
    {
        _domainEvents.Add(eventItem);
    }

    /// <summary>
    /// EN: Remove a domain event.
    /// VI: Xóa một domain event.
    /// </summary>
    public void RemoveDomainEvent(INotification eventItem)
    {
        _domainEvents.Remove(eventItem);
    }

    /// <summary>
    /// EN: Clear all domain events.
    /// VI: Xóa tất cả domain events.
    /// </summary>
    public void ClearDomainEvents()
    {
        _domainEvents.Clear();
    }

    /// <summary>
    /// EN: Check if entity is transient (not persisted yet).
    /// VI: Kiểm tra xem entity có phải là transient (chưa lưu) không.
    /// </summary>
    public bool IsTransient()
    {
        return Id == default;
    }

    public override bool Equals(object? obj)
    {
        if (obj is not Entity item)
            return false;

        if (ReferenceEquals(this, item))
            return true;

        if (GetType() != item.GetType())
            return false;

        if (item.IsTransient() || IsTransient())
            return false;

        return item.Id == Id;
    }

    public override int GetHashCode()
    {
        if (IsTransient())
            return base.GetHashCode();

        _requestedHashCode ??= Id.GetHashCode() ^ 31;
        return _requestedHashCode.Value;
    }

    public static bool operator ==(Entity? left, Entity? right)
    {
        return left?.Equals(right) ?? right is null;
    }

    public static bool operator !=(Entity? left, Entity? right)
    {
        return !(left == right);
    }
}
