package models import ( "fmt" "github.com/damirmukimov/city_resource_graph/models/cost" "github.com/damirmukimov/city_resource_graph/models/customer" "github.com/damirmukimov/city_resource_graph/models/impact" "github.com/damirmukimov/city_resource_graph/models/params" "github.com/damirmukimov/city_resource_graph/models/profitability" "github.com/damirmukimov/city_resource_graph/models/revenue" "github.com/damirmukimov/city_resource_graph/models/unit" "github.com/damirmukimov/city_resource_graph/models/validator" ) // YearResult contains all calculated metrics for a single year. type YearResult struct { Year int `json:"year"` Customer customer.CustomerMetrics `json:"customer"` TierDist customer.TierDistribution `json:"tier_distribution"` Revenue revenue.RevenueBreakdown `json:"revenue"` Costs cost.CostBreakdown `json:"costs"` Impact impact.ImpactMetrics `json:"impact"` UnitEconomics unit.UnitEconomics `json:"unit_economics"` Validation validator.ValidationResult `json:"validation"` Profit float64 `json:"profit"` Margin float64 `json:"margin"` // Percentage ARPU float64 `json:"arpu"` // Average revenue per user } // ModelResult contains results for all years. type ModelResult struct { Years []YearResult `json:"years"` Summary Summary `json:"summary"` Profitability profitability.ProfitabilityMetrics `json:"profitability"` } // Summary contains aggregated metrics across all years. type Summary struct { TotalRevenue float64 `json:"total_revenue"` TotalCosts float64 `json:"total_costs"` TotalProfit float64 `json:"total_profit"` TotalCO2Avoided float64 `json:"total_co2_avoided"` TotalWaterReused float64 `json:"total_water_reused"` TotalWasteDiverted float64 `json:"total_waste_diverted"` } // Calculate runs the complete mathematical model for all years specified in params. func Calculate(p *params.Params) (*ModelResult, error) { if err := p.Validate(); err != nil { return nil, fmt.Errorf("invalid params: %w", err) } var yearResults []YearResult for _, year := range p.Time.Years { result, err := CalculateYear(year, p) if err != nil { return nil, fmt.Errorf("year %d: %w", year, err) } yearResults = append(yearResults, *result) } summary := calculateSummary(yearResults) // Calculate profitability metrics (IRR, NPV, payback period) cashFlows := make([]float64, len(yearResults)) for i, year := range yearResults { cashFlows[i] = year.Profit } profitabilityMetrics := profitability.CalculateProfitability(cashFlows, p.Profitability.DiscountRate) return &ModelResult{ Years: yearResults, Summary: summary, Profitability: profitabilityMetrics, }, nil } // CalculateYear computes all metrics for a single year. func CalculateYear(year int, p *params.Params) (*YearResult, error) { // 1. Customer metrics custMetrics := customer.CalculateCustomerMetrics(year, p) tierDist := customer.CalculateTierDistribution(year, custMetrics.PayingOrgs, p) // 2. Revenue revBreakdown := revenue.CalculateRevenue(year, custMetrics, tierDist, p) // 3. Costs costBreakdown := cost.CalculateCosts(year, p) // 4. Impact impactMetrics := impact.CalculateImpact(year, custMetrics, p) // 5. Unit Economics unitEconomics := unit.CalculateUnitEconomics(year, custMetrics, tierDist, revBreakdown, p) // 6. Validation validationResult := validator.Validate(year, custMetrics, revBreakdown, costBreakdown, impactMetrics) // 7. Derived metrics profit := revBreakdown.Total - costBreakdown.Total var margin float64 if revBreakdown.Total > 0 { margin = (profit / revBreakdown.Total) * 100 } var arpu float64 if custMetrics.PayingOrgs > 0 { arpu = revBreakdown.Total / float64(custMetrics.PayingOrgs) } return &YearResult{ Year: year, Customer: custMetrics, TierDist: tierDist, Revenue: revBreakdown, Costs: costBreakdown, Impact: impactMetrics, UnitEconomics: unitEconomics, Validation: validationResult, Profit: profit, Margin: margin, ARPU: arpu, }, nil } // calculateSummary aggregates metrics across all years. func calculateSummary(results []YearResult) Summary { var totalRevenue, totalCosts, totalProfit float64 var totalCO2, totalWater, totalWaste float64 for _, result := range results { totalRevenue += result.Revenue.Total totalCosts += result.Costs.Total totalProfit += result.Profit totalCO2 += result.Impact.CO2Avoided totalWater += result.Impact.WaterReused totalWaste += result.Impact.WasteDiverted } return Summary{ TotalRevenue: totalRevenue, TotalCosts: totalCosts, TotalProfit: totalProfit, TotalCO2Avoided: totalCO2, TotalWaterReused: totalWater, TotalWasteDiverted: totalWaste, } }