package financial import ( "fmt" "math" "time" ) // FinancialCalculator implements the Calculator interface with proper separation of concerns type FinancialCalculator struct { config *Config npvCalc NPVCalculator irrCalc IRRCalculator paybackCalc PaybackCalculator sensitivityCalc SensitivityAnalyzer riskAssessor RiskAssessor co2Calc CO2Calculator capexEstimator CapexEstimator transportCalc TransportCostCalculator } // NewFinancialCalculator creates a new financial calculator with all dependencies func NewFinancialCalculator( config *Config, npvCalc NPVCalculator, irrCalc IRRCalculator, paybackCalc PaybackCalculator, sensitivityCalc SensitivityAnalyzer, riskAssessor RiskAssessor, co2Calc CO2Calculator, capexEstimator CapexEstimator, transportCalc TransportCostCalculator, ) *FinancialCalculator { return &FinancialCalculator{ config: config, npvCalc: npvCalc, irrCalc: irrCalc, paybackCalc: paybackCalc, sensitivityCalc: sensitivityCalc, riskAssessor: riskAssessor, co2Calc: co2Calc, capexEstimator: capexEstimator, transportCalc: transportCalc, } } // Calculate performs economic analysis based on the specified type func (fc *FinancialCalculator) Calculate( analysisType AnalysisType, data *ResourceFlowData, assumptions *EconomicAssumptions, ) (interface{}, error) { // Ensure assumptions have defaults if assumptions == nil { assumptions = fc.getDefaultAssumptions() } // Perform basic economic analysis basicAnalysis, err := fc.calculateBasicAnalysis(data, assumptions) if err != nil { return nil, fmt.Errorf("failed to calculate basic analysis: %w", err) } // Add analysis metadata basicAnalysis.AnalysisType = analysisType basicAnalysis.AnalysisDate = time.Now() basicAnalysis.Version = "1.0.0" switch analysisType { case AnalysisTypeBasic: return basicAnalysis, nil case AnalysisTypeAdvanced: return fc.calculateAdvancedAnalysis(basicAnalysis, data, assumptions) case AnalysisTypeSensitivity: return fc.calculateSensitivityAnalysis(basicAnalysis, assumptions) default: return nil, fmt.Errorf("unsupported analysis type: %s", analysisType) } } // calculateBasicAnalysis performs the core economic calculations func (fc *FinancialCalculator) calculateBasicAnalysis( data *ResourceFlowData, assumptions *EconomicAssumptions, ) (*EconomicAnalysis, error) { // Calculate transport cost per unit transportCostPerUnit := fc.transportCalc.CalculateTransportCost(data.ResourceType, data.DistanceKm) // Annual savings calculation // savings = (buyer_cost - seller_cost - transport) × annual_volume unitSavings := data.CostIn - data.CostOut - transportCostPerUnit annualSavings := unitSavings * data.AnnualVolume // Estimate CAPEX capex := fc.capexEstimator.EstimateCapex(data.ResourceType, data.DistanceKm, data.AnnualVolume) // Estimate OPEX opex := capex * fc.config.OpexPctOfCapex // Calculate net annual cash flow netAnnualCashFlow := annualSavings - opex // Calculate NPV npv := fc.npvCalc.CalculateNPV(capex, netAnnualCashFlow, assumptions.DiscountRate, assumptions.ProjectLifeYears) // Calculate IRR irr := fc.irrCalc.CalculateIRR(capex, netAnnualCashFlow, assumptions.ProjectLifeYears) // Calculate simple payback period payback := fc.paybackCalc.CalculatePaybackPeriod(capex, netAnnualCashFlow) // Calculate CO₂ avoided co2Avoided := fc.co2Calc.CalculateCO2Reduction(data.ResourceType, data.AnnualVolume) // Determine viability isViable := fc.assessViability(npv, irr, payback, assumptions) // Calculate confidence score based on data completeness and risk confidenceScore := fc.calculateConfidenceScore(data, assumptions) return &EconomicAnalysis{ NPV: npv, IRR: irr, PaybackYears: payback, AnnualSavings: annualSavings, CapexRequired: capex, OpexPerYear: opex, CO2AvoidedTonnes: co2Avoided, IsViable: isViable, ConfidenceScore: confidenceScore, }, nil } // calculateAdvancedAnalysis performs comprehensive advanced economic analysis func (fc *FinancialCalculator) calculateAdvancedAnalysis( basic *EconomicAnalysis, data *ResourceFlowData, assumptions *EconomicAssumptions, ) (*AdvancedEconomicAnalysis, error) { // Perform sensitivity analysis sensitivityScenarios := fc.sensitivityCalc.AnalyzeSensitivity(basic, assumptions) // Perform risk assessment riskAssessment := fc.riskAssessor.AssessRisk(data) // Calculate risk-adjusted metrics riskAdjustedNPV := fc.calculateRiskAdjustedNPV(basic.NPV, riskAssessment) riskAdjustedIRR := fc.calculateRiskAdjustedIRR(basic.IRR, riskAssessment) // Assess implementation complexity implementationComplexity := fc.assessImplementationComplexity(data) // Identify regulatory requirements regulatoryRequirements := fc.identifyRegulatoryRequirements(data.ResourceType) // Calculate detailed CO2 reduction breakdown co2Breakdown := fc.calculateCO2ReductionBreakdown(data.ResourceType, data.AnnualVolume, data.DistanceKm) // Generate mitigation strategies mitigationStrategies := fc.generateMitigationStrategies(riskAssessment) // Generate optimization recommendations recommendedActions := fc.generateOptimizationRecommendations(basic, sensitivityScenarios, riskAssessment) // Adjust confidence score based on risk and sensitivity confidenceScore := fc.calculateAdvancedConfidenceScore(basic.ConfidenceScore, sensitivityScenarios, riskAssessment) return &AdvancedEconomicAnalysis{ EconomicAnalysis: EconomicAnalysis{ NPV: basic.NPV, IRR: basic.IRR, PaybackYears: basic.PaybackYears, AnnualSavings: basic.AnnualSavings, CapexRequired: basic.CapexRequired, OpexPerYear: basic.OpexPerYear, CO2AvoidedTonnes: basic.CO2AvoidedTonnes, IsViable: basic.IsViable, ConfidenceScore: confidenceScore, AnalysisDate: time.Now(), AnalysisType: AnalysisTypeAdvanced, Version: "2.0.0", }, SensitivityScenarios: sensitivityScenarios, RiskAdjustedNPV: riskAdjustedNPV, RiskAdjustedIRR: riskAdjustedIRR, ImplementationComplexity: implementationComplexity, RegulatoryRequirements: regulatoryRequirements, CO2ReductionBreakdown: co2Breakdown, RiskProfile: *riskAssessment, MitigationStrategies: mitigationStrategies, RecommendedActions: recommendedActions, }, nil } // calculateSensitivityAnalysis performs detailed sensitivity analysis func (fc *FinancialCalculator) calculateSensitivityAnalysis( basic *EconomicAnalysis, assumptions *EconomicAssumptions, ) (*EconomicAnalysis, error) { // Start with basic analysis sensitivity := *basic sensitivity.AnalysisType = AnalysisTypeSensitivity // Perform sensitivity analysis scenarios := fc.sensitivityCalc.AnalyzeSensitivity(basic, assumptions) // Calculate confidence based on sensitivity results sensitivity.ConfidenceScore = fc.calculateSensitivityConfidence(scenarios) return &sensitivity, nil } // assessViability determines if the investment is economically viable func (fc *FinancialCalculator) assessViability(npv, irr, payback float64, assumptions *EconomicAssumptions) bool { // NPV must be positive if npv <= 0 { return false } // IRR must be greater than discount rate if irr <= assumptions.DiscountRate*100 { return false } // Payback period must be within project life if payback >= float64(assumptions.ProjectLifeYears) { return false } return true } // calculateConfidenceScore calculates confidence in the analysis based on data quality func (fc *FinancialCalculator) calculateConfidenceScore(data *ResourceFlowData, assumptions *EconomicAssumptions) float64 { confidence := 1.0 // Reduce confidence if distance is very long (higher uncertainty) if data.DistanceKm > 50 { confidence *= 0.9 } // Reduce confidence if volume is very low (higher per-unit cost uncertainty) if data.AnnualVolume < 100 { confidence *= 0.95 } // Reduce confidence if project life is very long (higher uncertainty) if assumptions.ProjectLifeYears > 20 { confidence *= 0.9 } return confidence } // adjustConfidenceForRisk adjusts confidence based on risk assessment func (fc *FinancialCalculator) adjustConfidenceForRisk(confidence float64, risk *RiskAssessment) float64 { // Reduce confidence based on overall risk level switch risk.RiskLevel { case "low": return confidence * 1.0 case "medium": return confidence * 0.9 case "high": return confidence * 0.7 case "critical": return confidence * 0.5 default: return confidence * 0.8 } } // calculateSensitivityConfidence calculates confidence based on sensitivity analysis func (fc *FinancialCalculator) calculateSensitivityConfidence(scenarios []SensitivityScenario) float64 { // Analyze how NPV changes with parameter variations var totalVariation float64 var variationCount int for _, scenario := range scenarios { if scenario.VariableName == "discount_rate" || scenario.VariableName == "capex" { totalVariation += scenario.ImpactOnNPV variationCount++ } } if variationCount == 0 { return 0.8 // Default confidence } avgVariation := totalVariation / float64(variationCount) // Lower confidence if NPV is highly sensitive to key parameters if avgVariation > 0.3 { // More than 30% variation return 0.6 } else if avgVariation > 0.1 { // More than 10% variation return 0.8 } return 0.9 } // calculateRiskAdjustedNPV adjusts NPV for risk func (fc *FinancialCalculator) calculateRiskAdjustedNPV(npv float64, risk *RiskAssessment) float64 { riskAdjustment := 1.0 switch risk.RiskLevel { case "low": riskAdjustment = 0.95 case "medium": riskAdjustment = 0.85 case "high": riskAdjustment = 0.7 case "critical": riskAdjustment = 0.5 } return npv * riskAdjustment } // calculateRiskAdjustedIRR adjusts IRR for risk func (fc *FinancialCalculator) calculateRiskAdjustedIRR(irr float64, risk *RiskAssessment) float64 { riskAdjustment := 0.0 switch risk.RiskLevel { case "low": riskAdjustment = 0.02 // +2% case "medium": riskAdjustment = -0.01 // -1% case "high": riskAdjustment = -0.05 // -5% case "critical": riskAdjustment = -0.10 // -10% } return irr - riskAdjustment } // assessImplementationComplexity evaluates how complex it would be to implement this match func (fc *FinancialCalculator) assessImplementationComplexity(data *ResourceFlowData) ImplementationComplexity { complexity := ImplementationComplexity{ Score: 1.0, // Base score TechnicalChallenges: []string{}, ResourceRequirements: []string{}, TimelineEstimate: "3-6 months", } // Distance affects complexity if data.DistanceKm > 100 { complexity.Score += 0.3 complexity.TechnicalChallenges = append(complexity.TechnicalChallenges, "Long-distance transport logistics") } if data.DistanceKm > 500 { complexity.Score += 0.5 complexity.TechnicalChallenges = append(complexity.TechnicalChallenges, "International/cross-border requirements") complexity.TimelineEstimate = "6-12 months" } // Volume affects scale if data.AnnualVolume > 10000 { complexity.Score += 0.2 complexity.ResourceRequirements = append(complexity.ResourceRequirements, "Large-scale infrastructure investment") } // Resource type affects technical requirements switch data.ResourceType { case "heat", "steam": complexity.TechnicalChallenges = append(complexity.TechnicalChallenges, "Thermal energy transfer systems") case "wastewater": complexity.TechnicalChallenges = append(complexity.TechnicalChallenges, "Water quality monitoring and treatment") case "biowaste": complexity.TechnicalChallenges = append(complexity.TechnicalChallenges, "Organic waste processing equipment") } return complexity } // identifyRegulatoryRequirements identifies applicable regulations for the resource type func (fc *FinancialCalculator) identifyRegulatoryRequirements(resourceType string) []RegulatoryRequirement { var requirements []RegulatoryRequirement switch resourceType { case "heat", "steam": requirements = append(requirements, RegulatoryRequirement{ Type: "Energy Trading License", Description: "Required for commercial energy exchange", EstimatedCost: 50000, TimeToObtain: "3-6 months", }) case "wastewater": requirements = append(requirements, RegulatoryRequirement{ Type: "Environmental Permit", Description: "Wastewater discharge and quality standards", EstimatedCost: 25000, TimeToObtain: "2-4 months", }) case "biowaste": requirements = append(requirements, RegulatoryRequirement{ Type: "Waste Management License", Description: "Organic waste processing authorization", EstimatedCost: 15000, TimeToObtain: "1-3 months", }) } // Common requirements requirements = append(requirements, RegulatoryRequirement{ Type: "Contract Registration", Description: "Legal contract registration with local authorities", EstimatedCost: 5000, TimeToObtain: "1 month", }) return requirements } // calculateCO2ReductionBreakdown provides detailed CO2 reduction analysis func (fc *FinancialCalculator) calculateCO2ReductionBreakdown(resourceType string, annualVolume, distanceKm float64) CO2ReductionBreakdown { breakdown := CO2ReductionBreakdown{ TotalTonnes: 0, Categories: []CO2Category{}, } switch resourceType { case "heat", "steam": // Avoided grid electricity for heating gridAvoided := annualVolume * fc.config.GridEmissionFactor * fc.config.HeatEfficiency breakdown.Categories = append(breakdown.Categories, CO2Category{ Name: "Grid Electricity Avoidance", Tonnes: gridAvoided, Percentage: 0, }) // Transport emissions (diesel trucks vs pipeline) transportAvoided := fc.calculateTransportEmissionAvoidance(resourceType, annualVolume, distanceKm) breakdown.Categories = append(breakdown.Categories, CO2Category{ Name: "Transport Emission Reduction", Tonnes: transportAvoided, Percentage: 0, }) case "wastewater": // Water treatment energy savings treatmentEnergy := annualVolume * fc.config.WaterTreatmentKwh / 1000 // Convert to MWh treatmentAvoided := treatmentEnergy * fc.config.GridEmissionFactor breakdown.Categories = append(breakdown.Categories, CO2Category{ Name: "Water Treatment Energy Savings", Tonnes: treatmentAvoided, Percentage: 0, }) case "biowaste": // Landfill methane avoidance methaneAvoided := annualVolume * fc.config.WasteDiversionCo2 breakdown.Categories = append(breakdown.Categories, CO2Category{ Name: "Landfill Methane Reduction", Tonnes: methaneAvoided, Percentage: 0, }) // Compost benefits (carbon sequestration) sequestration := annualVolume * fc.config.CompostCarbonSeq // Assume 0.1 t CO2/tonne compost breakdown.Categories = append(breakdown.Categories, CO2Category{ Name: "Carbon Sequestration", Tonnes: sequestration, Percentage: 0, }) } // Calculate total and percentages for i := range breakdown.Categories { breakdown.TotalTonnes += breakdown.Categories[i].Tonnes } for i := range breakdown.Categories { if breakdown.TotalTonnes > 0 { breakdown.Categories[i].Percentage = breakdown.Categories[i].Tonnes / breakdown.TotalTonnes * 100 } } return breakdown } // calculateTransportEmissionAvoidance calculates CO2 savings from reduced transport func (fc *FinancialCalculator) calculateTransportEmissionAvoidance(resourceType string, annualVolume, distanceKm float64) float64 { // Assume traditional transport would be by truck // Estimate truck trips needed for traditional transport var truckEfficiency float64 switch resourceType { case "heat", "steam": truckEfficiency = 0.1 // 10% of energy lost in transport default: truckEfficiency = 0.05 // 5% loss for other resources } // CO2 per km for truck transport (assume diesel truck: 0.0005 tonnes CO2 per km) truckEmissionFactor := 0.0005 traditionalTransport := annualVolume * truckEfficiency * distanceKm * truckEmissionFactor * 365 // Daily transport return traditionalTransport } // generateMitigationStrategies provides risk mitigation recommendations func (fc *FinancialCalculator) generateMitigationStrategies(risk *RiskAssessment) []string { strategies := []string{} switch risk.RiskLevel { case "high", "critical": strategies = append(strategies, "Implement comprehensive monitoring and control systems", "Establish contingency budgets (15-20% of CAPEX)", "Develop detailed risk management plan with regular reviews", "Consider phased implementation to reduce exposure", ) case "medium": strategies = append(strategies, "Regular performance monitoring and reporting", "Maintain contingency reserves", "Establish clear success metrics and KPIs", ) default: strategies = append(strategies, "Standard project management practices", "Regular progress reviews", ) } // Add resource-type specific strategies if risk.PrimaryRisks != nil { for _, riskFactor := range risk.PrimaryRisks { switch riskFactor { case "technical_failure": strategies = append(strategies, "Redundant system design with backup capacity") case "regulatory_changes": strategies = append(strategies, "Legal consultation and regulatory monitoring service") case "market_volatility": strategies = append(strategies, "Flexible contract terms with price adjustment clauses") } } } return strategies } // generateOptimizationRecommendations provides actionable optimization suggestions func (fc *FinancialCalculator) generateOptimizationRecommendations( basic *EconomicAnalysis, sensitivityScenarios []SensitivityScenario, risk *RiskAssessment, ) []RecommendedAction { recommendations := []RecommendedAction{} // NPV optimization if basic.NPV > 0 { recommendations = append(recommendations, RecommendedAction{ Priority: "high", Action: "Maximize NPV through operational efficiency improvements", ExpectedImpact: "5-15% NPV increase", Timeframe: "6-12 months", }) } // Payback period optimization if basic.PaybackYears > 5 { recommendations = append(recommendations, RecommendedAction{ Priority: "high", Action: "Accelerate payback through phased implementation", ExpectedImpact: "20-30% faster payback", Timeframe: "3-6 months", }) } // Risk mitigation if risk.RiskLevel == "high" || risk.RiskLevel == "critical" { recommendations = append(recommendations, RecommendedAction{ Priority: "high", Action: "Implement comprehensive risk mitigation plan", ExpectedImpact: "30-50% risk reduction", Timeframe: "1-3 months", }) } // Cost reduction based on sensitivity analysis for _, scenario := range sensitivityScenarios { if scenario.VariableName == "capex" && scenario.RiskLevel == "high" { recommendations = append(recommendations, RecommendedAction{ Priority: "medium", Action: "Explore alternative technology options to reduce CAPEX", ExpectedImpact: "10-25% CAPEX reduction", Timeframe: "2-4 months", }) } } return recommendations } // calculateAdvancedConfidenceScore provides more sophisticated confidence calculation func (fc *FinancialCalculator) calculateAdvancedConfidenceScore( baseConfidence float64, sensitivityScenarios []SensitivityScenario, risk *RiskAssessment, ) float64 { confidence := baseConfidence // Reduce confidence based on sensitivity volatility var totalSensitivityImpact float64 var highImpactCount int for _, scenario := range sensitivityScenarios { totalSensitivityImpact += math.Abs(scenario.ImpactOnNPV) if scenario.RiskLevel == "high" || scenario.RiskLevel == "critical" { highImpactCount++ } } if len(sensitivityScenarios) > 0 { avgSensitivityImpact := totalSensitivityImpact / float64(len(sensitivityScenarios)) confidence *= (1 - avgSensitivityImpact*0.5) // Reduce confidence by sensitivity impact } // Further reduce confidence based on high-impact scenarios if highImpactCount > 0 { confidence *= (1 - float64(highImpactCount)/float64(len(sensitivityScenarios))*0.3) } // Adjust for risk level switch risk.RiskLevel { case "medium": confidence *= 0.9 case "high": confidence *= 0.7 case "critical": confidence *= 0.5 } // Ensure confidence doesn't go below 0.1 if confidence < 0.1 { confidence = 0.1 } return confidence } // getDefaultAssumptions returns default economic assumptions func (fc *FinancialCalculator) getDefaultAssumptions() *EconomicAssumptions { return &EconomicAssumptions{ DiscountRate: fc.config.DefaultDiscountRate, ProjectLifeYears: fc.config.DefaultProjectLife, OperatingHoursYear: 8760, // 24/7 operation } }