use chrono::{DateTime, Duration, Utc};
use rust_decimal::Decimal;
use crate::db::Database;
use crate::{order::Order, UserId};
use crate::{Result, User};
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct Credits {
pub available: Decimal,
pub history: CreditsHistory,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct CreditsHistory {
pub last_three: Vec<(DateTime<Utc>, Decimal)>,
pub last_day: Vec<Decimal>,
pub last_five_days: Vec<Decimal>,
pub last_month: Vec<Decimal>,
pub last_six_months: Vec<Decimal>,
pub last_year: Vec<Decimal>,
}
pub fn calculate_history(user_id: UserId, db: &Database) -> Result<CreditsHistory> {
let mut user = db.get::<User>(user_id)?;
let mut orders = db.get_collection::<Order>()?;
orders.retain(|p| p.user == user_id);
let history = user.credits.calculate_history(orders);
Ok(history)
}
impl Credits {
pub fn calculate_history(&mut self, mut orders: Vec<Order>) -> CreditsHistory {
orders.sort_by(|a, b| b.time.cmp(&a.time));
let mut last_three = Vec::new();
for order in orders.iter().take(3) {
let delta = -order.total_cost();
last_three.push((order.time, delta));
}
let mut _last_day = Vec::new();
for order in orders {
if order.time.signed_duration_since(Utc::now()) < Duration::days(1) {
_last_day.push(order);
}
}
_last_day.sort_by(|a, b| a.time.timestamp().cmp(&b.time.timestamp()));
let mut last_day = Vec::new();
let mut credits = self.available;
let mut hours = 24;
for delta_hour in 1..hours + 1 {
let mut hour_delta = Decimal::ZERO;
for order in &_last_day {
let order_delta_hours = order
.time
.signed_duration_since(Utc::now())
.num_hours()
.abs();
if order_delta_hours < delta_hour && order_delta_hours >= delta_hour - 1 {
let delta = -order.total_cost();
hour_delta += delta;
}
}
last_day.push(credits);
credits -= hour_delta;
}
CreditsHistory {
last_three,
last_day,
last_five_days: vec![],
last_month: vec![],
last_six_months: vec![],
last_year: vec![],
}
}
}