namespace Gateway.Models; // ════════════════════════════════════════════════ // Request: Client → Gateway // ════════════════════════════════════════════════ public sealed class ChannelForecastRequest { /// Advertising objective: awareness, traffic, leads, sales public string Objective { get; set; } = "traffic"; /// Business category from wizard Step 1 public string? BusinessCategory { get; set; } /// Keywords from URL analysis (Step 1) public List Keywords { get; set; } = new(); /// Geo targeting from audience step public ForecastGeoTargeting? GeoTargeting { get; set; } /// Audience parameters from Step 2 public ForecastAudience? Audience { get; set; } /// Monthly budget in whole dollars public decimal MonthlyBudget { get; set; } /// Channels to estimate (defaults to all selected) public List? Channels { get; set; } } public sealed class ForecastGeoTargeting { public List? ZipCodes { get; set; } public double? RadiusMiles { get; set; } public List? GeoTargetIds { get; set; } } public sealed class ForecastAudience { public int? AgeMin { get; set; } public int? AgeMax { get; set; } public List? Genders { get; set; } public List? Interests { get; set; } } // ════════════════════════════════════════════════ // Response: Gateway → Client (normalized) // ════════════════════════════════════════════════ public sealed class ChannelForecastResponse { public bool Ok { get; set; } = true; public string Objective { get; set; } = string.Empty; public decimal TotalBudget { get; set; } public List Channels { get; set; } = new(); public ForecastRecommendation? Recommendation { get; set; } public ForecastMeta Metadata { get; set; } = new(); } public sealed class ChannelEstimate { public string Provider { get; set; } = string.Empty; public int AllocationPercent { get; set; } public decimal AllocatedBudget { get; set; } public ChannelEstimateMetrics Estimates { get; set; } = new(); public double EfficiencyScore { get; set; } public string StrengthLabel { get; set; } = string.Empty; public string Confidence { get; set; } = "none"; public string DataSource { get; set; } = "none"; } public sealed class ChannelEstimateMetrics { public double Impressions { get; set; } public double? Reach { get; set; } public double Clicks { get; set; } public double Conversions { get; set; } public decimal AvgCpc { get; set; } public decimal AvgCpm { get; set; } public decimal? EstimatedCpa { get; set; } public double Ctr { get; set; } } public sealed class ForecastRecommendation { public string Summary { get; set; } = string.Empty; public List Highlights { get; set; } = new(); } public sealed class ForecastMeta { public DateTimeOffset GeneratedAt { get; set; } = DateTimeOffset.UtcNow; public string ForecastPeriod { get; set; } = "30 days"; } // ════════════════════════════════════════════════ // Objective-weighted scoring // ════════════════════════════════════════════════ public sealed class MetricWeights { public double Reach { get; } public double Impressions { get; } public double Cpm { get; } public double Clicks { get; } public double Cpc { get; } public double Ctr { get; } public double Conversions { get; } public double Cpa { get; } public MetricWeights(double reach, double impressions, double cpm, double clicks, double cpc, double ctr, double conversions, double cpa) { Reach = reach; Impressions = impressions; Cpm = cpm; Clicks = clicks; Cpc = cpc; Ctr = ctr; Conversions = conversions; Cpa = cpa; } } public static class ObjectiveWeights { public static readonly Dictionary Weights = new(StringComparer.OrdinalIgnoreCase) { // reach imp cpm clicks cpc ctr conv cpa ["awareness"] = new MetricWeights(0.35, 0.25, 0.20, 0.05, 0.05, 0.05, 0.00, 0.00), ["traffic"] = new MetricWeights(0.05, 0.10, 0.10, 0.30, 0.30, 0.15, 0.00, 0.00), ["leads"] = new MetricWeights(0.05, 0.05, 0.05, 0.15, 0.15, 0.10, 0.25, 0.20), ["sales"] = new MetricWeights(0.05, 0.05, 0.05, 0.10, 0.10, 0.10, 0.30, 0.25), }; /// Fallback: balanced weights if objective not recognized public static readonly MetricWeights Default = new(0.10, 0.10, 0.10, 0.20, 0.20, 0.10, 0.10, 0.10); public static MetricWeights For(string objective) => Weights.TryGetValue(objective, out var w) ? w : Default; }