1use chrono::DateTime;
2use chrono_tz::Tz;
3use serde::Serialize;
4
5use crate::{Cost, CostPeriods, CostPeriodsSimple, Language, Money, currency::Currency};
6
7#[derive(Debug, Clone, Copy, Serialize)]
10#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
11pub enum FeedInRevenue {
12 Simple(Cost),
13 Unverified,
15 Unlisted,
17 SpotPriceVariable {
19 base_cost: Money,
20 spot_price_multiplier: f64,
21 approximated: bool,
23 },
24 Periods(CostPeriods),
25}
26
27impl FeedInRevenue {
28 pub const fn is_unverified(&self) -> bool {
29 matches!(self, Self::Unverified)
30 }
31
32 pub(super) const fn new_periods(periods: CostPeriods) -> Self {
33 Self::Periods(periods)
34 }
35
36 pub(super) const fn fixed_subunit(subunit: f64) -> Self {
37 Self::Simple(Cost::fixed_subunit(subunit))
38 }
39
40 pub const fn spot_price_variable(
41 base_cost_subunit: f64,
42 spot_price_multiplier: f64,
43 approximated: bool,
44 ) -> Self {
45 Self::SpotPriceVariable {
46 base_cost: Money::new_subunit(base_cost_subunit),
47 spot_price_multiplier,
48 approximated,
49 }
50 }
51
52 pub fn simplified(
53 &self,
54 fuse_size: u16,
55 yearly_consumption: u32,
56 language: Language,
57 ) -> FeedInRevenueSimplified {
58 FeedInRevenueSimplified::new(self, fuse_size, yearly_consumption, language)
59 }
60
61 pub fn kwh_revenue(
62 &self,
63 timestamp: DateTime<Tz>,
64 spotprice: Money,
65 fuse_size: u16,
66 yearly_consumption: u32,
67 ) -> Money {
68 match *self {
69 FeedInRevenue::Unverified | FeedInRevenue::Unlisted => Money::ZERO,
70 FeedInRevenue::Simple(cost) => cost
71 .cost_for(fuse_size, yearly_consumption)
72 .unwrap_or_default(),
73 FeedInRevenue::SpotPriceVariable {
74 base_cost,
75 spot_price_multiplier,
76 approximated: _,
77 } => base_cost + (spotprice * spot_price_multiplier),
78 FeedInRevenue::Periods(cost_periods) => cost_periods
79 .matching_periods(timestamp)
80 .into_iter()
81 .flat_map(|period| period.cost().cost_for(fuse_size, yearly_consumption))
82 .sum(),
83 }
84 }
85}
86
87#[derive(Debug, Clone, Serialize)]
90#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
91pub enum FeedInRevenueSimplified {
92 Simple(Option<Money>),
93 Unverified,
95 Unlisted,
97 SpotPriceVariable {
99 base_cost: Money,
100 spot_price_multiplier: f64,
101 approximated: bool,
103 info: String,
104 },
105 Periods(CostPeriodsSimple),
106}
107
108impl FeedInRevenueSimplified {
109 fn new(
110 revenue: &FeedInRevenue,
111 fuse_size: u16,
112 yearly_consumption: u32,
113 language: Language,
114 ) -> Self {
115 match *revenue {
116 FeedInRevenue::Unlisted => Self::Unlisted,
117 FeedInRevenue::Unverified => Self::Unverified,
118 FeedInRevenue::Simple(cost) => {
119 Self::Simple(cost.cost_for(fuse_size, yearly_consumption))
120 }
121 FeedInRevenue::SpotPriceVariable {
122 base_cost,
123 spot_price_multiplier,
124 approximated,
125 } => Self::SpotPriceVariable {
126 base_cost,
127 spot_price_multiplier,
128 approximated,
129 info: Default::default(),
130 },
131 FeedInRevenue::Periods(periods) => Self::Periods(CostPeriodsSimple::new(
132 periods,
133 fuse_size,
134 yearly_consumption,
135 language,
136 )),
137 }
138 .add_info(language)
139 }
140
141 fn add_info(self, language: Language) -> Self {
142 match self {
143 FeedInRevenueSimplified::SpotPriceVariable {
144 base_cost,
145 spot_price_multiplier,
146 approximated,
147 info: _,
148 } => {
149 let percentage = spot_price_multiplier * 100.;
150 let mut info = match language {
151 Language::En => format!(
152 "The grid operator bases its feed-in revenue on a fixed part of {} and {}% of the current spot price.",
153 base_cost.display(Currency::SEK),
154 percentage
155 ),
156 Language::Sv => format!(
157 "Nätbolaget baserar sin nätnytta på en fast del om {} samt {}% av spotpriset.",
158 base_cost.display(Currency::SEK),
159 percentage
160 ),
161 };
162 if approximated {
163 info.push_str(match language {
164 Language::En => " The base fee and percentage are estimated, as the grid operator doesn't list them on their website.",
165 Language::Sv => " Basavgift och procentsats är uppskattade, eftersom nätbolaget inte skriver ut exakt vad de är på sin webbplats.",
166 })
167 }
168 FeedInRevenueSimplified::SpotPriceVariable {
169 base_cost,
170 spot_price_multiplier,
171 approximated,
172 info,
173 }
174 }
175 _ => self,
176 }
177 }
178}