namespace AdsServingService.API.Application.Services;

/// <summary>
/// EN: Candidate ad model used in serving pipeline.
/// VI: Model quảng cáo ứng viên dùng trong pipeline phục vụ quảng cáo.
/// </summary>
public record EligibleAdCandidate
{
    public Guid AdId { get; init; }
    public Guid CampaignId { get; init; }
    public decimal BidAmount { get; init; }
    public string Format { get; init; } = null!;
    public string? Headline { get; init; }
    public string? PrimaryText { get; init; }
    public string? CallToAction { get; init; }
    public string? CreativeUrl { get; init; }
    public string? DestinationUrl { get; init; }
}

/// <summary>
/// EN: Service abstraction to load eligible ads for a serve request.
/// VI: Abstraction service để tải danh sách quảng cáo đủ điều kiện cho request serve.
/// </summary>
public interface IEligibleAdsProvider
{
    Task<IReadOnlyList<EligibleAdCandidate>> GetEligibleAdsAsync(
        Guid userId,
        string placementType,
        IReadOnlyDictionary<string, string> userContext,
        CancellationToken cancellationToken);
}

/// <summary>
/// EN: In-memory eligible ads provider used as default fallback.
/// VI: Provider quảng cáo đủ điều kiện dạng in-memory dùng như fallback mặc định.
/// </summary>
public class InMemoryEligibleAdsProvider : IEligibleAdsProvider
{
    public Task<IReadOnlyList<EligibleAdCandidate>> GetEligibleAdsAsync(
        Guid userId,
        string placementType,
        IReadOnlyDictionary<string, string> userContext,
        CancellationToken cancellationToken)
    {
        // EN: Keep deterministic IDs per request/user so placement is reproducible.
        // VI: Giữ ID xác định theo request/user để kết quả placement có thể lặp lại.
        var hashSeed = Math.Abs(HashCode.Combine(userId, placementType.ToLowerInvariant()));
        var primaryCampaignId = DeterministicGuid($"{placementType}:{hashSeed}:campaign:primary");
        var secondaryCampaignId = DeterministicGuid($"{placementType}:{hashSeed}:campaign:secondary");

        IReadOnlyList<EligibleAdCandidate> candidates =
        [
            new EligibleAdCandidate
            {
                AdId = DeterministicGuid($"{placementType}:{hashSeed}:ad:primary"),
                CampaignId = primaryCampaignId,
                BidAmount = 5000m,
                Format = "single_image",
                Headline = "Amazing Product - Limited Offer!",
                PrimaryText = "Get 50% off today only",
                CallToAction = "Shop Now",
                CreativeUrl = "https://example.com/creative.jpg",
                DestinationUrl = "https://example.com/product",
            },
            new EligibleAdCandidate
            {
                AdId = DeterministicGuid($"{placementType}:{hashSeed}:ad:secondary"),
                CampaignId = secondaryCampaignId,
                BidAmount = 4500m,
                Format = "single_image",
                Headline = "Summer Sale",
                PrimaryText = "Up to 70% discount",
                CallToAction = "Learn More",
                CreativeUrl = "https://example.com/creative2.jpg",
                DestinationUrl = "https://example.com/sale",
            },
        ];

        return Task.FromResult(candidates);
    }

    private static Guid DeterministicGuid(string input)
    {
        var bytes = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(input));
        var guidBytes = new byte[16];
        Array.Copy(bytes, guidBytes, guidBytes.Length);
        return new Guid(guidBytes);
    }
}
