use std::collections::HashMap; use std::sync::Mutex; use anyhow::Result; use super::models::{DailyStats, DownloadRecord, ForceChannel, User}; pub struct Database { config: Mutex>, users: Mutex>, daily_counts: Mutex>, records: Mutex>, channels: Mutex>, next_record_id: Mutex, admin_actions: Mutex>, } impl Database { pub fn open(_db_path: &std::path::Path) -> Result { let mut config = HashMap::new(); config.insert("default_daily_limit".into(), "3".into()); config.insert("welcome_message".into(), "".into()); config.insert("global_mute".into(), "false".into()); Ok(Self { config: Mutex::new(config), users: Mutex::new(HashMap::new()), daily_counts: Mutex::new(HashMap::new()), records: Mutex::new(Vec::new()), channels: Mutex::new(Vec::new()), next_record_id: Mutex::new(1), admin_actions: Mutex::new(HashMap::new()), }) } pub fn run_migrations(&self) -> Result<()> { Ok(()) } pub fn get_config(&self, key: &str) -> Option { self.config.lock().unwrap().get(key).cloned() } pub fn set_config(&self, key: &str, value: &str) -> Result<()> { self.config.lock().unwrap().insert(key.to_string(), value.to_string()); Ok(()) } pub fn register_user(&self, user_id: i64, username: Option<&str>, first_name: Option<&str>, last_name: Option<&str>) -> Result { let mut users = self.users.lock().unwrap(); if users.contains_key(&user_id) { return Ok(users[&user_id].clone()); } let default_limit: i64 = self.get_config("default_daily_limit").and_then(|v| v.parse().ok()).unwrap_or(3); let now = chrono::Utc::now().to_rfc3339(); let user = User { user_id, username: username.map(|s| s.to_string()), first_name: first_name.map(|s| s.to_string()), last_name: last_name.map(|s| s.to_string()), role: "user".to_string(), daily_limit: default_limit, format_preference: "epub".to_string(), created_at: now.clone(), last_active_at: now, }; users.insert(user_id, user.clone()); Ok(user) } pub fn get_user(&self, user_id: i64) -> Option { self.users.lock().unwrap().get(&user_id).cloned() } pub fn update_last_active(&self, user_id: i64) { if let Ok(mut users) = self.users.lock() { if let Some(u) = users.get_mut(&user_id) { u.last_active_at = chrono::Utc::now().to_rfc3339(); } } } pub fn update_username(&self, user_id: i64, username: Option<&str>) { if let Ok(mut users) = self.users.lock() { if let Some(u) = users.get_mut(&user_id) { u.username = username.map(|s| s.to_string()); } } } pub fn set_user_role(&self, user_id: i64, role: &str) -> Result<()> { if let Ok(mut users) = self.users.lock() { if let Some(u) = users.get_mut(&user_id) { u.role = role.to_string(); } } Ok(()) } pub fn set_user_daily_limit(&self, user_id: i64, limit: i64) -> Result<()> { if let Ok(mut users) = self.users.lock() { if let Some(u) = users.get_mut(&user_id) { u.daily_limit = limit; } } Ok(()) } pub fn set_user_format(&self, user_id: i64, format: &str) -> Result<()> { if let Ok(mut users) = self.users.lock() { if let Some(u) = users.get_mut(&user_id) { u.format_preference = format.to_string(); } } Ok(()) } pub fn count_users(&self) -> i64 { self.users.lock().unwrap().len() as i64 } pub fn get_today_download_count(&self, user_id: i64, date: &str) -> i64 { let key = format!("{}:{}", user_id, date); *self.daily_counts.lock().unwrap().get(&key).unwrap_or(&0) } pub fn increment_today_download(&self, user_id: i64, date: &str) -> Result<()> { let key = format!("{}:{}", user_id, date); *self.daily_counts.lock().unwrap().entry(key).or_insert(0) += 1; Ok(()) } pub fn count_today_downloads_total(&self, date: &str) -> i64 { self.daily_counts.lock().unwrap().iter().filter(|(k, _)| k.ends_with(&format!(":{}", date))).map(|(_, v)| *v).sum() } pub fn add_download_record(&self, user_id: i64, book_id: &str, book_name: Option<&str>, format: Option<&str>, status: &str) -> Result { let id = *self.next_record_id.lock().unwrap(); *self.next_record_id.lock().unwrap() += 1; let record = DownloadRecord { id, user_id, book_id: book_id.to_string(), book_name: book_name.map(|s| s.to_string()), format: format.map(|s| s.to_string()), status: status.to_string(), file_size: None, started_at: chrono::Utc::now().to_rfc3339(), finished_at: None, }; self.records.lock().unwrap().push(record); Ok(id) } pub fn finish_download_record(&self, record_id: i64, status: &str, file_size: Option) -> Result<()> { if let Ok(mut records) = self.records.lock() { if let Some(r) = records.iter_mut().find(|r| r.id == record_id) { r.status = status.to_string(); r.file_size = file_size; r.finished_at = Some(chrono::Utc::now().to_rfc3339()); } } Ok(()) } pub fn count_user_downloads(&self, user_id: i64) -> i64 { self.records.lock().unwrap().iter().filter(|r| r.user_id == user_id && r.status == "completed").count() as i64 } pub fn get_force_channels(&self) -> Vec { self.channels.lock().unwrap().clone() } pub fn add_force_channel(&self, channel_id: &str, channel_name: Option<&str>, channel_type: &str, invite_link: Option<&str>, added_by: i64) -> Result<()> { let id = self.channels.lock().unwrap().len() as i64 + 1; self.channels.lock().unwrap().push(ForceChannel { id, channel_id: channel_id.to_string(), channel_name: channel_name.map(|s| s.to_string()), channel_type: channel_type.to_string(), invite_link: invite_link.map(|s| s.to_string()), added_at: chrono::Utc::now().to_rfc3339(), added_by, }); Ok(()) } pub fn remove_force_channel(&self, channel_id: &str) -> Result<()> { self.channels.lock().unwrap().retain(|c| c.channel_id != channel_id); Ok(()) } pub fn is_admin(&self, user_id: i64) -> bool { self.get_admin_ids().contains(&user_id) } pub fn get_admin_ids(&self) -> Vec { self.get_config("admin_ids").and_then(|v| serde_json::from_str(&v).ok()).unwrap_or_default() } pub fn set_admin_ids(&self, ids: &[i64]) -> Result<()> { let json = serde_json::to_string(ids).unwrap(); self.set_config("admin_ids", &json) } pub fn is_global_muted(&self) -> bool { self.get_config("global_mute").as_deref() == Some("true") } pub fn toggle_global_mute(&self) -> Result { let current = self.is_global_muted(); self.set_config("global_mute", if current { "false" } else { "true" })?; Ok(!current) } pub fn get_default_daily_limit(&self) -> i64 { self.get_config("default_daily_limit").and_then(|v| v.parse().ok()).unwrap_or(3) } pub fn set_default_daily_limit(&self, limit: i64) -> Result<()> { self.set_config("default_daily_limit", &limit.to_string()) } pub fn clean_old_stats(&self) -> Result<()> { Ok(()) } pub fn total_completed_downloads(&self) -> i64 { self.records.lock().unwrap().iter().filter(|r| r.status == "completed").count() as i64 } pub fn get_all_admin_users(&self) -> Vec { let ids = self.get_admin_ids(); let users = self.users.lock().unwrap(); ids.iter().filter_map(|id| users.get(id).cloned()).collect() } pub fn set_action(&self, user_id: i64, action: &str) { self.admin_actions.lock().unwrap().insert(user_id, action.to_string()); } pub fn take_action(&self, user_id: i64) -> Option { self.admin_actions.lock().unwrap().remove(&user_id) } }