using IntelligenceApi.Models;
namespace IntelligenceApi.Engines;
///
/// Derives audience recommendations from raw census data for a ZCTA.
///
/// This logic was previously embedded in the Gateway's DemographicsController
/// as BuildMarketAnalysis(). It belongs here — IntelligenceApi owns all
/// recommendation and analysis logic; the Gateway is a thin proxy.
///
/// Registered as a singleton: stateless, no IO.
///
public sealed class DemographicsAnalyzer
{
public DemographicAnalysisResponse Analyze(DemographicAnalysisRequest request)
{
var c = request.Census;
var zcta = request.Zcta;
// ── Age skew ──────────────────────────────────────────────────────────
var youngPct = c.Pct18to24 + c.Pct25to34;
var maturePct = c.Pct55to64 + c.Pct65plus;
string ageSkew;
if (youngPct > maturePct + 10) ageSkew = "young";
else if (maturePct > youngPct + 10) ageSkew = "mature";
else ageSkew = "balanced";
// ── Recommended age chips ─────────────────────────────────────────────
// Include brackets with meaningful population share.
var ageRanges = new List();
if (c.Pct18to24 >= 10) ageRanges.Add("AGE_18_24");
if (c.Pct25to34 >= 12) ageRanges.Add("AGE_25_34");
if (c.Pct35to44 >= 12) ageRanges.Add("AGE_35_44");
if (c.Pct45to54 >= 12) ageRanges.Add("AGE_45_54");
if (c.Pct55to64 >= 10) ageRanges.Add("AGE_55_64");
if (c.Pct65plus >= 12) ageRanges.Add("AGE_65_UP");
// Fallback: if no bracket clears the threshold, default to prime brackets
if (ageRanges.Count == 0)
{
ageRanges.Add("AGE_25_34");
ageRanges.Add("AGE_35_44");
}
// ── Recommended income chips ──────────────────────────────────────────
var incomes = c.MedianIncome switch
{
> 100_000 => new List { "TOP_10", "TOP_11_20" },
> 75_000 => new List { "TOP_11_20", "TOP_21_30" },
> 50_000 => new List { "TOP_21_30", "TOP_31_40" },
_ => new List { "TOP_41_50", "LOWER_50" }
};
// ── Human-readable insights ───────────────────────────────────────────
var insights = new List();
if (c.TotalPopulation > 0) insights.Add($"{c.TotalPopulation:N0} people");
if (c.MedianIncome > 0) insights.Add($"Median income ${c.MedianIncome:N0}");
if (c.PctBachelorPlus > 0) insights.Add($"{c.PctBachelorPlus}% college-educated");
if (c.PctOwnerOccupied > 55) insights.Add($"{c.PctOwnerOccupied}% homeowners");
else if (c.PctRenterOccupied > 55) insights.Add($"{c.PctRenterOccupied}% renters");
if (c.PctFamilyHouseholds > 60) insights.Add($"{c.PctFamilyHouseholds}% families");
else if (c.PctLivingAlone > 35) insights.Add($"{c.PctLivingAlone}% single-person households");
insights.Add(ageSkew switch
{
"young" => "Skews younger (18–34)",
"mature" => "Skews older (55+)",
_ => "Balanced age distribution"
});
return new DemographicAnalysisResponse
{
Ok = true,
Zcta = zcta,
Census = c,
Recommendations = new AudienceRecommendations
{
AgeRanges = ageRanges,
Incomes = incomes,
AgeSkew = ageSkew,
MarketScope = "local" // single ZIP is always local scope
},
Insights = insights
};
}
}