use std::collections::HashMap;
use std::sync::Arc;
use std::time::Instant;

use irc_now_common::auth::OidcConfig;
use openid::DiscoveredClient;
use sqlx::PgPool;
use tokio::sync::Mutex;

#[derive(Clone)]
pub struct AppState {
    pub db: PgPool,
    pub oidc: OidcConfig,
    pub oidc_client: Arc<DiscoveredClient>,
    pub upload_limiter: Arc<Mutex<HashMap<String, Vec<Instant>>>>,
}

const FREE_PASTE_RATE: usize = 20;
const PRO_PASTE_RATE: usize = 60;
const RATE_WINDOW_SECS: u64 = 3600;

pub fn check_rate_limit(
    entries: &mut Vec<Instant>,
    is_pro: bool,
) -> bool {
    let now = Instant::now();
    let window = std::time::Duration::from_secs(RATE_WINDOW_SECS);
    entries.retain(|t| now.duration_since(*t) < window);
    let limit = if is_pro { PRO_PASTE_RATE } else { FREE_PASTE_RATE };
    if entries.len() >= limit {
        return false;
    }
    entries.push(now);
    true
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn rate_limit_allows_under_limit() {
        let mut entries = Vec::new();
        assert!(check_rate_limit(&mut entries, false));
    }

    #[test]
    fn rate_limit_blocks_at_limit() {
        let mut entries: Vec<Instant> = (0..FREE_PASTE_RATE).map(|_| Instant::now()).collect();
        assert!(!check_rate_limit(&mut entries, false));
    }

    #[test]
    fn rate_limit_pro_higher_limit() {
        let mut entries: Vec<Instant> = (0..FREE_PASTE_RATE).map(|_| Instant::now()).collect();
        assert!(check_rate_limit(&mut entries, true));
    }
}
