namespace PromotionService.Domain.AggregatesModel.CampaignAggregate;

using PromotionService.Domain.Exceptions;
using PromotionService.Domain.SeedWork;

/// <summary>
/// EN: Voucher entity representing a single voucher in a campaign.
/// VI: Entity voucher đại diện cho một voucher trong chiến dịch.
/// </summary>
public class Voucher : Entity
{
    /// <summary>
    /// EN: Campaign ID this voucher belongs to.
    /// VI: ID chiến dịch voucher này thuộc về.
    /// </summary>
    public Guid CampaignId { get; private set; }

    /// <summary>
    /// EN: Unique voucher code.
    /// VI: Mã voucher duy nhất.
    /// </summary>
    public string Code { get; private set; } = null!;

    /// <summary>
    /// EN: User ID who owns this voucher (null if unclaimed).
    /// VI: ID người dùng sở hữu voucher này (null nếu chưa nhận).
    /// </summary>
    public Guid? OwnerId { get; private set; }

    /// <summary>
    /// EN: Original face value of voucher.
    /// VI: Mệnh giá gốc của voucher.
    /// </summary>
    public decimal FaceValue { get; private set; }

    /// <summary>
    /// EN: Remaining value (after partial redemptions).
    /// VI: Giá trị còn lại (sau khi sử dụng một phần).
    /// </summary>
    public decimal RemainingValue { get; private set; }

    /// <summary>
    /// EN: Voucher status.
    /// VI: Trạng thái voucher.
    /// </summary>
    public VoucherStatus Status { get; private set; } = null!;
    public int StatusId { get; private set; }

    /// <summary>
    /// EN: When the voucher was claimed by a user.
    /// VI: Thời điểm voucher được nhận bởi người dùng.
    /// </summary>
    public DateTime? ClaimedAt { get; private set; }

    /// <summary>
    /// EN: When the voucher expires.
    /// VI: Thời điểm voucher hết hạn.
    /// </summary>
    public DateTime? ExpiresAt { get; private set; }

    /// <summary>
    /// EN: When the voucher was last redeemed.
    /// VI: Thời điểm voucher được sử dụng lần cuối.
    /// </summary>
    public DateTime? RedeemedAt { get; private set; }

    /// <summary>
    /// EN: Creation timestamp.
    /// VI: Thời điểm tạo.
    /// </summary>
    public DateTime CreatedAt { get; private set; }

    /// <summary>
    /// EN: Last update timestamp.
    /// VI: Thời điểm cập nhật cuối.
    /// </summary>
    public DateTime UpdatedAt { get; private set; }

    // EF Core constructor
    protected Voucher() { }

    /// <summary>
    /// EN: Create a new voucher.
    /// VI: Tạo voucher mới.
    /// </summary>
    public Voucher(Guid campaignId, string code, decimal faceValue, int validityDays)
    {
        if (string.IsNullOrWhiteSpace(code))
            throw new PromotionDomainException("Voucher code is required");
        if (faceValue <= 0)
            throw new PromotionDomainException("Face value must be greater than zero");

        Id = Guid.NewGuid();
        CampaignId = campaignId;
        Code = code;
        FaceValue = faceValue;
        RemainingValue = faceValue;
        Status = VoucherStatus.Available;
        StatusId = Status.Id;
        CreatedAt = DateTime.UtcNow;
        UpdatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Claim the voucher for a user.
    /// VI: Nhận voucher cho người dùng.
    /// </summary>
    public void Claim(Guid userId, int validityDays)
    {
        if (Status != VoucherStatus.Available)
            throw new VoucherAlreadyClaimedException(Id, Code);

        OwnerId = userId;
        Status = VoucherStatus.Claimed;
        StatusId = Status.Id;
        ClaimedAt = DateTime.UtcNow;
        ExpiresAt = DateTime.UtcNow.AddDays(validityDays);
        UpdatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Redeem a portion of the voucher value.
    /// VI: Sử dụng một phần giá trị voucher.
    /// </summary>
    /// <param name="amount">Amount to redeem / Số tiền sử dụng</param>
    /// <returns>Actual amount deducted / Số tiền thực tế bị trừ</returns>
    public decimal Redeem(decimal amount)
    {
        ValidateCanRedeem();

        if (amount <= 0)
            throw new PromotionDomainException("Redeem amount must be greater than zero");

        // Calculate actual amount (cannot exceed remaining)
        var actualAmount = Math.Min(amount, RemainingValue);
        
        RemainingValue -= actualAmount;
        RedeemedAt = DateTime.UtcNow;
        UpdatedAt = DateTime.UtcNow;

        // Update status
        Status = RemainingValue == 0 ? VoucherStatus.FullyRedeemed : VoucherStatus.PartiallyRedeemed;
        StatusId = Status.Id;

        return actualAmount;
    }

    /// <summary>
    /// EN: Mark voucher as expired.
    /// VI: Đánh dấu voucher hết hạn.
    /// </summary>
    public void Expire()
    {
        if (Status == VoucherStatus.FullyRedeemed)
            return; // Already fully used

        Status = VoucherStatus.Expired;
        StatusId = Status.Id;
        UpdatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Extend voucher expiry by additional days.
    /// VI: Gia hạn voucher thêm số ngày.
    /// </summary>
    public void ExtendExpiry(int additionalDays)
    {
        if (additionalDays <= 0)
            throw new PromotionDomainException("Additional days must be positive");

        if (Status == VoucherStatus.FullyRedeemed || Status == VoucherStatus.Expired)
            throw new PromotionDomainException("Cannot extend expired or fully redeemed voucher");

        if (!ExpiresAt.HasValue)
            ExpiresAt = DateTime.UtcNow.AddDays(additionalDays);
        else
            ExpiresAt = ExpiresAt.Value.AddDays(additionalDays);

        UpdatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Check if voucher is valid for redemption.
    /// VI: Kiểm tra voucher có hợp lệ để sử dụng.
    /// </summary>
    public bool IsValidForRedemption()
    {
        return (Status == VoucherStatus.Claimed || Status == VoucherStatus.PartiallyRedeemed)
               && RemainingValue > 0
               && (!ExpiresAt.HasValue || ExpiresAt.Value > DateTime.UtcNow);
    }

    /// <summary>
    /// EN: Check if voucher has expired.
    /// VI: Kiểm tra voucher đã hết hạn chưa.
    /// </summary>
    public bool IsExpired => ExpiresAt.HasValue && DateTime.UtcNow > ExpiresAt.Value;

    private void ValidateCanRedeem()
    {
        if (Status == VoucherStatus.Available)
            throw new PromotionDomainException("Voucher has not been claimed yet");

        if (Status == VoucherStatus.FullyRedeemed)
            throw new PromotionDomainException("Voucher has already been fully redeemed");

        if (Status == VoucherStatus.Expired)
            throw new VoucherExpiredException(Id, Code);

        if (IsExpired)
        {
            Expire();
            throw new VoucherExpiredException(Id, Code);
        }

        if (RemainingValue <= 0)
            throw new InsufficientVoucherValueException(Id, 0, 0);
    }
}
