use std::{collections::HashMap, sync::Arc}; use chrono::{DateTime, Duration as ChronoDuration, Utc}; use serde::{Deserialize, Serialize}; use tokio::sync::RwLock; use uuid::Uuid; use crate::events::{MarketSnapshot, OutcomeSnapshot}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct OutcomeOddsSnapshot { pub id: Uuid, pub odds_version_id: Uuid, pub outcome_id: Uuid, pub decimal_odds: f64, pub fractional_num: i32, pub fractional_den: i32, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct OddsVersionSnapshot { pub id: Uuid, pub market_id: Uuid, pub version_no: i32, pub created_at: DateTime, pub is_current: bool, pub odds: Vec, } #[derive(Clone, Default)] pub struct MarketsStore { inner: Arc>, } #[derive(Default)] struct MarketsState { markets_by_event_id: HashMap>, markets_by_id: HashMap, current_odds_by_market_id: HashMap, } impl MarketsStore { pub fn new() -> Self { Self::with_sample_data() } pub fn with_sample_data() -> Self { let event_id = Uuid::parse_str("11111111-1111-1111-1111-111111111111").expect("valid uuid"); let market_id = Uuid::parse_str("22222222-2222-2222-2222-222222222222").expect("valid uuid"); let odds_version_id = Uuid::parse_str("66666666-6666-6666-6666-666666666666").expect("valid uuid"); let preview_lock_at = Utc::now() + ChronoDuration::minutes(10); let created_at = Utc::now(); let home_outcome_id = Uuid::parse_str("44444444-4444-4444-4444-444444444444").expect("valid uuid"); let away_outcome_id = Uuid::parse_str("55555555-5555-5555-5555-555555555555").expect("valid uuid"); let market = MarketSnapshot { id: market_id, event_id, question_key: "market.sample.winner".to_string(), market_type: "winner".to_string(), status: "open".to_string(), lock_at: preview_lock_at, settlement_rule_key: "settle_on_match_winner".to_string(), outcomes: vec![ OutcomeSnapshot { id: home_outcome_id, market_id, outcome_code: "home".to_string(), label_key: "outcome.home".to_string(), sort_order: 1, }, OutcomeSnapshot { id: away_outcome_id, market_id, outcome_code: "away".to_string(), label_key: "outcome.away".to_string(), sort_order: 2, }, ], }; let odds_version = OddsVersionSnapshot { id: odds_version_id, market_id, version_no: 1, created_at, is_current: true, odds: vec![ OutcomeOddsSnapshot { id: Uuid::parse_str("77777777-7777-7777-7777-777777777777").expect("valid uuid"), odds_version_id, outcome_id: home_outcome_id, decimal_odds: 1.85, fractional_num: 17, fractional_den: 20, }, OutcomeOddsSnapshot { id: Uuid::parse_str("88888888-8888-8888-8888-888888888888").expect("valid uuid"), odds_version_id, outcome_id: away_outcome_id, decimal_odds: 2.05, fractional_num: 21, fractional_den: 20, }, ], }; let mut markets_by_event_id = HashMap::new(); markets_by_event_id.insert(event_id, vec![market.clone()]); let mut markets_by_id = HashMap::new(); markets_by_id.insert(market_id, market); let mut current_odds_by_market_id = HashMap::new(); current_odds_by_market_id.insert(market_id, odds_version); Self { inner: Arc::new(RwLock::new(MarketsState { markets_by_event_id, markets_by_id, current_odds_by_market_id, })), } } pub async fn markets_for_event(&self, event_id: Uuid) -> Option> { let state = self.inner.read().await; let Some(markets) = state.markets_by_event_id.get(&event_id) else { return None; }; Some(markets.clone()) } pub async fn market(&self, market_id: Uuid) -> Option { let state = self.inner.read().await; let Some(market) = state.markets_by_id.get(&market_id) else { return None; }; Some(market.clone()) } pub async fn current_odds(&self, market_id: Uuid) -> Option { let state = self.inner.read().await; let Some(odds) = state.current_odds_by_market_id.get(&market_id) else { return None; }; Some(odds.clone()) } }