use crate::achievement::AchievementContext;
use crate::bloop::{Bloop, BloopProvider};
use crate::evaluator::registration_number::RegistrationNumberProvider;
use crate::nfc_uid::NfcUid;
use crate::player::{PlayerInfo, PlayerMutator};
use crate::trigger::TriggerRegistry;
use chrono::DateTime;
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc, OnceLock, RwLock};
use std::time::Duration;
use tokio::time::Instant;
use uuid::Uuid;
pub struct Utc;
static BASE_INSTANT: OnceLock<Instant> = OnceLock::new();
static BASE_UTC: OnceLock<DateTime<chrono::Utc>> = OnceLock::new();
impl Utc {
pub fn now() -> DateTime<chrono::Utc> {
let base_instant = *BASE_INSTANT.get_or_init(Instant::now);
let base_utc = *BASE_UTC.get_or_init(|| DateTime::from_timestamp(0, 0).unwrap());
let elapsed = Instant::now()
.checked_duration_since(base_instant)
.unwrap_or(Duration::ZERO);
base_utc + chrono::Duration::from_std(elapsed).unwrap()
}
}
#[derive(Default, Debug)]
pub struct MockPlayerBuilder {
nfc_uid: NfcUid,
bloops_count: usize,
registration_number: usize,
name: String,
}
impl MockPlayerBuilder {
pub fn new() -> Self {
Self {
nfc_uid: NfcUid::default(),
bloops_count: 0,
registration_number: 0,
name: "test".to_string(),
}
}
pub fn nfc_uid(mut self, nfc_uid: NfcUid) -> Self {
self.nfc_uid = nfc_uid;
self
}
pub fn bloops_count(mut self, bloops_count: usize) -> Self {
self.bloops_count = bloops_count;
self
}
pub fn registration_number(mut self, registration_number: usize) -> Self {
self.registration_number = registration_number;
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}
pub fn build(self) -> (Arc<RwLock<MockPlayer>>, Uuid) {
let id = Uuid::new_v4();
(
Arc::new(RwLock::new(MockPlayer {
id,
nfc_uid: self.nfc_uid,
name: self.name,
bloops_count: self.bloops_count,
awarded: HashMap::new(),
registration_number: self.registration_number,
})),
id,
)
}
}
#[derive(Debug)]
pub struct MockPlayer {
pub id: Uuid,
pub nfc_uid: NfcUid,
pub name: String,
pub bloops_count: usize,
pub awarded: HashMap<Uuid, DateTime<chrono::Utc>>,
pub registration_number: usize,
}
impl MockPlayer {
pub fn builder() -> MockPlayerBuilder {
MockPlayerBuilder::new()
}
}
impl PlayerInfo for MockPlayer {
fn id(&self) -> Uuid {
self.id
}
fn nfc_uid(&self) -> NfcUid {
self.nfc_uid
}
fn total_bloops(&self) -> usize {
self.bloops_count
}
fn awarded_achievements(&self) -> &HashMap<Uuid, DateTime<chrono::Utc>> {
&self.awarded
}
}
impl PlayerMutator for MockPlayer {
fn increment_bloops(&mut self) {
self.bloops_count += 1;
}
fn add_awarded_achievement(&mut self, achievement_id: Uuid, awarded_at: DateTime<chrono::Utc>) {
self.awarded.insert(achievement_id, awarded_at);
}
}
impl RegistrationNumberProvider for MockPlayer {
fn registration_number(&self) -> usize {
self.registration_number
}
}
#[derive(Debug)]
pub struct TestCtxBuilder<Player, State = (), Trigger = ()> {
pub current_bloop: Bloop<Player>,
pub state: State,
pub bloop_provider: BloopProvider<Player>,
pub trigger_registry: TriggerRegistry<Trigger>,
}
impl<Player> TestCtxBuilder<Player, (), ()> {
pub fn new(current_bloop: Bloop<Player>) -> Self {
Self {
current_bloop,
state: (),
trigger_registry: TriggerRegistry::new(HashMap::new()),
bloop_provider: BloopProvider::new(Duration::from_secs(1800)),
}
}
}
impl<Player, State, Trigger> TestCtxBuilder<Player, State, Trigger> {
pub fn bloops(mut self, bloops: Vec<Bloop<Player>>) -> Self {
self.bloop_provider = BloopProvider::with_bloops(Duration::from_secs(1800), bloops);
self
}
pub fn state<T: Default>(self, state: T) -> TestCtxBuilder<Player, T, Trigger> {
TestCtxBuilder {
current_bloop: self.current_bloop,
state,
bloop_provider: self.bloop_provider,
trigger_registry: self.trigger_registry,
}
}
pub fn trigger_registry<T>(
self,
trigger_registry: TriggerRegistry<T>,
) -> TestCtxBuilder<Player, State, T> {
TestCtxBuilder {
current_bloop: self.current_bloop,
state: self.state,
bloop_provider: self.bloop_provider,
trigger_registry,
}
}
pub fn build(&mut self) -> AchievementContext<'_, Player, State, Trigger> {
AchievementContext::new(
&self.current_bloop,
&self.bloop_provider,
&self.state,
&mut self.trigger_registry,
)
}
}