1use chrono::{DateTime, TimeZone};
2use serde::Serialize;
3
4use crate::{Cost, CostPeriods, CostPeriodsSimple, Language, Money, currency::Currency};
5
6#[derive(Debug, Clone, Copy, Serialize)]
7#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
8pub enum TransferFee {
9 Unlisted,
11 Unverified,
13 Simple(Cost),
15 SpotPriceVariable {
18 base_cost: Money,
19 spot_price_multiplier: f64,
20 approximated: bool,
21 },
22 Periods(CostPeriods),
23}
24
25impl TransferFee {
26 pub const fn new_periods(periods: CostPeriods) -> Self {
27 Self::Periods(periods)
28 }
29
30 pub const fn is_unverified(&self) -> bool {
31 matches!(self, Self::Unverified)
32 }
33
34 pub const fn simple_cost(&self) -> Option<Cost> {
35 match self {
36 Self::Simple(cost) => Some(*cost),
37 _ => None,
38 }
39 }
40
41 pub const fn spot_price_variable(
42 base_cost_subunit: f64,
43 spot_price_multiplier: f64,
44 approximated: bool,
45 ) -> Self {
46 if spot_price_multiplier < 0.0 || spot_price_multiplier > 1.0 {
47 panic!("`spot_price_multiplier` too low/high");
48 }
49 Self::SpotPriceVariable {
50 base_cost: Money::new_subunit(base_cost_subunit),
51 spot_price_multiplier,
52 approximated,
53 }
54 }
55
56 pub fn simplified(
57 &self,
58 fuse_size: u16,
59 yearly_consumption: u32,
60 language: Language,
61 ) -> TransferFeeSimplified {
62 TransferFeeSimplified::new(self, fuse_size, yearly_consumption, language)
63 }
64
65 pub(crate) const fn fixed(int: i64, fract: u8) -> Self {
66 Self::Simple(Cost::fixed(int, fract))
67 }
68
69 pub(crate) const fn fixed_subunit(subunit: f64) -> Self {
70 Self::Simple(Cost::fixed_subunit(subunit))
71 }
72
73 pub fn kwh_cost<Tz: TimeZone>(
74 &self,
75 timestamp: DateTime<Tz>,
76 spotprice: Money,
77 fuse_size: u16,
78 yearly_consumption: u32,
79 ) -> Money
80 where
81 DateTime<Tz>: Copy,
82 {
83 match *self {
84 TransferFee::Unlisted | TransferFee::Unverified => Money::ZERO,
85 TransferFee::Simple(cost) => cost
86 .cost_for(fuse_size, yearly_consumption)
87 .unwrap_or_default(),
88 TransferFee::SpotPriceVariable {
89 base_cost,
90 spot_price_multiplier,
91 approximated: _,
92 } => base_cost + (spotprice * spot_price_multiplier),
93 TransferFee::Periods(cost_periods) => cost_periods
94 .matching_periods(timestamp)
95 .into_iter()
96 .flat_map(|p| p.cost().cost_for(fuse_size, yearly_consumption))
97 .sum(),
98 }
99 }
100
101 pub(crate) fn is_yearly_consumption_based(&self, fuse_size: u16) -> bool {
102 match self {
103 TransferFee::Unlisted
104 | TransferFee::Unverified
105 | TransferFee::SpotPriceVariable { .. } => false,
106 TransferFee::Simple(cost) => cost.is_yearly_consumption_based(fuse_size),
107 TransferFee::Periods(periods) => periods.is_yearly_consumption_based(fuse_size),
108 }
109 }
110
111 pub(crate) const fn spot_price_variable_placeholder() -> TransferFee {
113 Self::spot_price_variable(2.0, 0.06, true)
114 }
115}
116
117#[derive(Debug, Clone, Serialize)]
119#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
120pub enum TransferFeeSimplified {
121 Unlisted,
123 Unverified,
125 Simple(Money),
127 SpotPriceVariable {
130 base_cost: Money,
131 spot_price_multiplier: f64,
132 approximated: bool,
133 info: String,
134 },
135 Periods(CostPeriodsSimple),
136}
137
138impl TransferFeeSimplified {
139 fn new(fee: &TransferFee, fuse_size: u16, yearly_consumption: u32, language: Language) -> Self {
140 match *fee {
141 TransferFee::Unlisted => TransferFeeSimplified::Unlisted,
142 TransferFee::Unverified => TransferFeeSimplified::Unverified,
143 TransferFee::Simple(cost) => TransferFeeSimplified::Simple(
144 cost.cost_for(fuse_size, yearly_consumption)
145 .unwrap_or(Money::ZERO),
146 ),
147 TransferFee::SpotPriceVariable {
148 base_cost,
149 spot_price_multiplier,
150 approximated,
151 } => TransferFeeSimplified::SpotPriceVariable {
152 base_cost,
153 spot_price_multiplier,
154 approximated,
155 info: Default::default(),
156 },
157 TransferFee::Periods(periods) => TransferFeeSimplified::Periods(
158 CostPeriodsSimple::new(periods, fuse_size, yearly_consumption, language),
159 ),
160 }
161 .add_info(language)
162 }
163
164 fn add_info(self, language: Language) -> Self {
165 match self {
166 TransferFeeSimplified::Unlisted => self,
167 TransferFeeSimplified::Unverified => self,
168 TransferFeeSimplified::Simple(_) => self,
169 TransferFeeSimplified::SpotPriceVariable {
170 base_cost,
171 spot_price_multiplier,
172 approximated,
173 info: _,
174 } => {
175 let percentage = spot_price_multiplier * 100.;
176 let mut info = match language {
177 Language::En => format!(
178 "The grid operator bases its transfer fee on a fixed part of {} and {}% of the current spot price.",
179 base_cost.display(Currency::SEK),
180 percentage
181 ),
182 Language::Sv => format!(
183 "Nätbolaget baserar sin överföringsavgift på en fast del om {} samt {}% av spotpriset.",
184 base_cost.display(Currency::SEK),
185 percentage
186 ),
187 };
188 if approximated {
189 info.push_str(match language {
190 Language::En => " The base fee and percentage are estimated, as the grid operator doesn't list them on their website.",
191 Language::Sv => " Basavgift och procentsats är uppskattade, eftersom nätbolaget inte skriver ut exakt vad de är på sin webbplats.",
192 })
193 }
194 TransferFeeSimplified::SpotPriceVariable {
195 base_cost,
196 spot_price_multiplier,
197 approximated,
198 info,
199 }
200 }
201 TransferFeeSimplified::Periods(_) => self,
202 }
203 }
204}