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