using AdsServingService.Domain.SeedWork;

namespace AdsServingService.Domain.AggregatesModel.AuctionAggregate;

/// <summary>
/// EN: Auction aggregate root - represents a single real-time bidding auction.
/// VI: Auction aggregate root - đại diện cho một phiên đấu giá thời gian thực.
/// </summary>
public class Auction : Entity, IAggregateRoot
{
    private List<Bid> _bids = new();
    private AuctionResult? _result;
    private DateTime _auctionTime;

    /// <summary>
    /// EN: User context for this auction.
    /// VI: Ngữ cảnh người dùng cho phiên đấu giá này.
    /// </summary>
    public Guid UserId { get; private set; }
    
    /// <summary>
    /// EN: Ad placement type (feed, story, banner, etc.).
    /// VI: Loại vị trí quảng cáo (feed, story, banner, v.v.).
    /// </summary>
    public string PlacementType { get; private set; } = null!;

    /// <summary>
    /// EN: All bids submitted for this auction.
    /// VI: Tất cả bid được submit cho phiên đấu giá này.
    /// </summary>
    public IReadOnlyCollection<Bid> Bids => _bids.AsReadOnly();

    /// <summary>
    /// EN: Auction result (winner and final price).
    /// VI: Kết quả đấu giá (người thắng và giá cuối).
    /// </summary>
    public AuctionResult? Result => _result;

    public DateTime AuctionTime => _auctionTime;

    protected Auction() { }

    public Auction(Guid userId, string placementType)
    {
        Id = Guid.NewGuid();
        UserId = userId;
        PlacementType = placementType;
        _auctionTime = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Add a bid to the auction.
    /// VI: Thêm bid vào phiên đấu giá.
    /// </summary>
    public void AddBid(Guid adId, Guid campaignId, decimal bidAmount, decimal predictedCTR, decimal qualityScore)
    {
        var bid = new Bid(adId, campaignId, bidAmount, predictedCTR, qualityScore);
        _bids.Add(bid);
    }

    /// <summary>
    /// EN: Run the auction and determine the winner based on eCPM.
    /// VI: Chạy đấu giá và xác định người thắng dựa trên eCPM.
    /// </summary>
    public void RunAuction()
    {
        if (_bids.Count == 0)
            return;

        // Sort bids by eCPM descending
        var sortedBids = _bids.OrderByDescending(b => b.eCPM).ToList();
        var winningBid = sortedBids[0];
        
        // Second-price auction: winner pays the second-highest eCPM + $0.01
        var finalPrice = sortedBids.Count > 1 
            ? sortedBids[1].eCPM + 0.01m 
            : winningBid.BidAmount;

        _result = new AuctionResult(winningBid.AdId, winningBid.CampaignId, finalPrice, winningBid.eCPM);
    }
}

/// <summary>
/// EN: Bid entity - represents a single bid in the auction.
/// VI: Bid entity - đại diện cho một bid trong phiên đấu giá.
/// </summary>
public class Bid : Entity
{
    public Guid AdId { get; private set; }
    public Guid CampaignId { get; private set; }
    public decimal BidAmount { get; private set; }
    public decimal PredictedCTR { get; private set; }
    public decimal QualityScore { get; private set; }

    /// <summary>
    /// EN: Effective CPM (eCPM) = Bid × Predicted CTR × Quality Score.
    /// VI: CPM hiệu quả (eCPM) = Bid × CTR dự đoán × Điểm chất lượng.
    /// </summary>
    public decimal eCPM => BidAmount * PredictedCTR * QualityScore;

    protected Bid() { }

    public Bid(Guid adId, Guid campaignId, decimal bidAmount, decimal predictedCTR, decimal qualityScore)
    {
        Id = Guid.NewGuid();
        AdId = adId;
        CampaignId = campaignId;
        BidAmount = bidAmount;
        PredictedCTR = predictedCTR;
        QualityScore = qualityScore;
    }
}

/// <summary>
/// EN: Auction result value object.
/// VI: Value object kết quả đấu giá.
/// </summary>
public class AuctionResult : ValueObject
{
    public Guid WinningAdId { get; private set; }
    public Guid WinningCampaignId { get; private set; }
    public decimal FinalPrice { get; private set; }
    public decimal WinningeCPM { get; private set; }

    protected AuctionResult() { }

    public AuctionResult(Guid winningAdId, Guid winningCampaignId, decimal finalPrice, decimal winningeCPM)
    {
        WinningAdId = winningAdId;
        WinningCampaignId = winningCampaignId;
        FinalPrice = finalPrice;
        WinningeCPM = winningeCPM;
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return WinningAdId;
        yield return FinalPrice;
    }
}
