using System; using System.Collections.Generic; namespace QRRapidoApp.Models.Ads { /// /// Represents the content for a single affiliate advertisement. /// public class AffiliateAdContent { public string Title { get; set; } = string.Empty; public string? Description { get; set; } public string ProductUrl { get; set; } = string.Empty; public string? ImageUrl { get; set; } public string? CtaText { get; set; } public string? BadgeText { get; set; } public string? PriceText { get; set; } public string? Category { get; set; } public bool IsEmpty() { return string.IsNullOrWhiteSpace(ProductUrl); } public AffiliateAdContent Clone() { return new AffiliateAdContent { Title = Title, Description = Description, ProductUrl = ProductUrl, ImageUrl = ImageUrl, CtaText = CtaText, BadgeText = BadgeText, PriceText = PriceText, Category = Category }; } } /// /// Configuration for an ad slot, supporting AdSense or affiliate content. /// public class AdSlotConfiguration { /// /// Provider type for the slot. Supported values: "AdSense", "Affiliate". /// Defaults to "AdSense" to preserve current behaviour. /// public string Provider { get; set; } = "AdSense"; /// /// Optional custom AdSense slot ID override. /// public string? AdSenseSlotId { get; set; } /// /// Affiliate content that will be rendered when is "Affiliate". /// public AffiliateAdContent? Affiliate { get; set; } public AdSlotConfiguration Clone() { return new AdSlotConfiguration { Provider = Provider, AdSenseSlotId = AdSenseSlotId, Affiliate = Affiliate?.Clone() }; } } /// /// Root options object bound from configuration for ad slots. /// public class AdsConfigurationOptions { private IDictionary _slots = new Dictionary(StringComparer.OrdinalIgnoreCase); private IDictionary> _locales = new Dictionary>(StringComparer.OrdinalIgnoreCase); public IDictionary Slots { get => _slots; set => _slots = CreateSlotDictionary(value); } public IDictionary> Locales { get => _locales; set { _locales = new Dictionary>(StringComparer.OrdinalIgnoreCase); if (value == null) { return; } foreach (var locale in value) { _locales[locale.Key] = CreateSlotDictionary(locale.Value); } } } public AdSlotConfiguration GetSlot(string slotKey, string? cultureName = null) { if (string.IsNullOrWhiteSpace(slotKey)) { slotKey = "header"; } var localeSlot = TryGetLocaleSlot(slotKey, cultureName); if (localeSlot != null) { return localeSlot.Clone(); } if (_slots.TryGetValue(slotKey, out var config)) { return config.Clone(); } // Default to AdSense when not configured return new AdSlotConfiguration(); } private AdSlotConfiguration? TryGetLocaleSlot(string slotKey, string? cultureName) { if (string.IsNullOrWhiteSpace(cultureName) || _locales.Count == 0) { return null; } if (_locales.TryGetValue(cultureName, out var localeSlots) && localeSlots.TryGetValue(slotKey, out var slot)) { return slot; } var neutralCulture = cultureName.Split('-')[0]; if (!string.Equals(neutralCulture, cultureName, StringComparison.OrdinalIgnoreCase) && _locales.TryGetValue(neutralCulture, out var neutralSlots) && neutralSlots.TryGetValue(slotKey, out var neutralSlot)) { return neutralSlot; } return null; } private static IDictionary CreateSlotDictionary(IDictionary? source) { var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (source == null) { return dictionary; } foreach (var kvp in source) { if (kvp.Value != null) { dictionary[kvp.Key] = kvp.Value; } } return dictionary; } } /// /// View model used by the affiliate ad partial. /// public class AffiliateAdViewModel { public string SlotKey { get; set; } = "header"; public string ContainerCssClass { get; set; } = string.Empty; public AffiliateAdContent Content { get; set; } = new AffiliateAdContent(); } }