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