package service import ( "context" "encoding/json" "fmt" "bugulma/backend/internal/domain" "bugulma/backend/internal/financial" ) // EconomicService provides high-level economic analysis services type EconomicService struct { calculator financial.Calculator } // NewEconomicService creates a new economic service func NewEconomicService(calculator financial.Calculator) *EconomicService { return &EconomicService{ calculator: calculator, } } // AnalyzeMatch performs comprehensive economic analysis for a resource flow match func (es *EconomicService) AnalyzeMatch( ctx context.Context, source, target *domain.ResourceFlow, distanceKm float64, ) (*financial.EconomicAnalysis, error) { // Extract resource flow data data, err := es.extractResourceFlowData(source, target, distanceKm) if err != nil { return nil, fmt.Errorf("failed to extract resource flow data: %w", err) } // Get default assumptions (could be made configurable per organization/client) assumptions := es.getDefaultAssumptions() // Perform basic economic analysis first analysis, err := es.calculator.Calculate(financial.AnalysisTypeBasic, data, assumptions) if err != nil { return nil, fmt.Errorf("failed to perform economic analysis: %w", err) } // Type assert to get the EconomicAnalysis basicAnalysis, ok := analysis.(*financial.EconomicAnalysis) if !ok { return nil, fmt.Errorf("unexpected analysis type returned for basic analysis: %T", analysis) } return basicAnalysis, nil } // AnalyzeAdvancedMatch performs comprehensive advanced economic analysis for a resource flow match func (es *EconomicService) AnalyzeAdvancedMatch( ctx context.Context, source, target *domain.ResourceFlow, distanceKm float64, ) (*financial.AdvancedEconomicAnalysis, error) { // Extract resource flow data data, err := es.extractResourceFlowData(source, target, distanceKm) if err != nil { return nil, fmt.Errorf("failed to extract resource flow data: %w", err) } // Get default assumptions (could be made configurable per organization/client) assumptions := es.getDefaultAssumptions() // Perform advanced economic analysis analysis, err := es.calculator.Calculate(financial.AnalysisTypeAdvanced, data, assumptions) if err != nil { return nil, fmt.Errorf("failed to perform advanced economic analysis: %w", err) } // Type assert to get the AdvancedEconomicAnalysis advancedAnalysis, ok := analysis.(*financial.AdvancedEconomicAnalysis) if !ok { return nil, fmt.Errorf("unexpected analysis type returned for advanced analysis") } return advancedAnalysis, nil } // AnalyzeMatchWithSensitivity performs economic analysis with sensitivity analysis func (es *EconomicService) AnalyzeMatchWithSensitivity( ctx context.Context, source, target *domain.ResourceFlow, distanceKm float64, ) (*financial.EconomicAnalysis, error) { data, err := es.extractResourceFlowData(source, target, distanceKm) if err != nil { return nil, fmt.Errorf("failed to extract resource flow data: %w", err) } assumptions := es.getDefaultAssumptions() analysis, err := es.calculator.Calculate(financial.AnalysisTypeSensitivity, data, assumptions) if err != nil { return nil, fmt.Errorf("failed to perform sensitivity analysis: %w", err) } // Type assert to get the EconomicAnalysis sensitivityAnalysis, ok := analysis.(*financial.EconomicAnalysis) if !ok { return nil, fmt.Errorf("unexpected analysis type returned for sensitivity analysis: %T", analysis) } return sensitivityAnalysis, nil } // extractResourceFlowData extracts financial data from resource flow entities func (es *EconomicService) extractResourceFlowData( source, target *domain.ResourceFlow, distanceKm float64, ) (*financial.ResourceFlowData, error) { var sourceEcon, targetEcon domain.EconomicData if err := json.Unmarshal(source.EconomicData, &sourceEcon); err != nil { return nil, fmt.Errorf("failed to unmarshal source economic data: %w", err) } if err := json.Unmarshal(target.EconomicData, &targetEcon); err != nil { return nil, fmt.Errorf("failed to unmarshal target economic data: %w", err) } var sourceQty domain.Quantity if err := json.Unmarshal(source.Quantity, &sourceQty); err != nil { return nil, fmt.Errorf("failed to unmarshal source quantity: %w", err) } // Convert to annual volume annualVolume := es.calculateAnnualVolume(sourceQty.Amount, sourceQty.TemporalUnit) return &financial.ResourceFlowData{ ResourceType: string(source.Type), DistanceKm: distanceKm, AnnualVolume: annualVolume, CostIn: targetEcon.CostIn, // Buyer's cost CostOut: sourceEcon.CostOut, // Seller's cost TemporalUnit: string(sourceQty.TemporalUnit), }, nil } // calculateAnnualVolume converts temporal unit to annual volume func (es *EconomicService) calculateAnnualVolume(amount float64, unit domain.TemporalUnit) float64 { multipliers := map[domain.TemporalUnit]float64{ domain.UnitPerSecond: 31536000, // 365 * 24 * 3600 domain.UnitPerMinute: 525600, // 365 * 24 * 60 domain.UnitPerHour: 8760, // 365 * 24 domain.UnitPerDay: 365, domain.UnitPerWeek: 52, domain.UnitPerMonth: 12, domain.UnitPerYear: 1, domain.UnitContinuous: 1, // Already annual } multiplier, exists := multipliers[unit] if !exists { multiplier = 1 // Default to annual } return amount * multiplier } // getDefaultAssumptions returns default economic assumptions func (es *EconomicService) getDefaultAssumptions() *financial.EconomicAssumptions { return &financial.EconomicAssumptions{ DiscountRate: 0.08, // 8% ProjectLifeYears: 10, OperatingHoursYear: 8760, // 24/7 operation MaintenanceCostFactor: 0.02, // 2% of CAPEX EnergyCostInflation: 0.03, // 3% inflation InsuranceCostPct: 0.005, // 0.5% of CAPEX RegulatoryCostBuffer: 0.1, // 10% buffer ContingencyPct: 0.15, // 15% contingency } } // GetViabilitySummary provides a high-level viability assessment func (es *EconomicService) GetViabilitySummary(analysis *financial.EconomicAnalysis) string { if analysis.IsViable && analysis.ConfidenceScore > 0.8 { return "Highly viable - strong economic case with low risk" } else if analysis.IsViable && analysis.ConfidenceScore > 0.6 { return "Viable - positive economics with moderate confidence" } else if analysis.IsViable { return "Marginally viable - meets basic criteria but high uncertainty" } else { return "Not viable - negative economic indicators" } }