namespace AdsServingService.API.Application.Services;

/// <summary>
/// EN: Scoring signal tuple used to place bids in auction.
/// VI: Bộ tín hiệu scoring dùng để đặt bid trong auction.
/// </summary>
public record AuctionBidSignals(decimal PredictedCtr, decimal QualityScore);

/// <summary>
/// EN: Abstraction for scoring hooks in ad serving auction.
/// VI: Abstraction cho scoring hook trong auction phục vụ quảng cáo.
/// </summary>
public interface IAuctionScoringService
{
    AuctionBidSignals Score(
        EligibleAdCandidate candidate,
        string placementType,
        IReadOnlyDictionary<string, string> userContext);
}

/// <summary>
/// EN: Default heuristic scoring strategy.
/// VI: Chiến lược scoring heuristic mặc định.
/// </summary>
public class DefaultAuctionScoringService : IAuctionScoringService
{
    public AuctionBidSignals Score(
        EligibleAdCandidate candidate,
        string placementType,
        IReadOnlyDictionary<string, string> userContext)
    {
        var baseCtr = placementType.ToLowerInvariant() switch
        {
            "story" => 0.018m,
            "banner" => 0.012m,
            _ => 0.020m,
        };

        var segmentBoost = 1.0m;
        if (userContext.TryGetValue("segment", out var segmentValue) &&
            segmentValue.Equals("high-intent", StringComparison.OrdinalIgnoreCase))
        {
            segmentBoost += 0.25m;
        }

        var recencyBoost = 1.0m;
        if (userContext.TryGetValue("recentPurchaseDays", out var recencyRaw) &&
            int.TryParse(recencyRaw, out var recencyDays) &&
            recencyDays <= 14)
        {
            recencyBoost += 0.10m;
        }

        var predictedCtr = Math.Round(baseCtr * segmentBoost * recencyBoost, 4);

        var qualityScore = candidate.Format.ToLowerInvariant() switch
        {
            "single_image" => 1.0m,
            "video" => 1.1m,
            _ => 0.95m,
        };

        return new AuctionBidSignals(predictedCtr, qualityScore);
    }
}
