//! Generic sliding-window rate limiter.
//!
//! Key pattern: `rate:{namespace}:{identifier}`
//! Returns `Ok(true)` if the request is allowed, `Ok(false)` if rate-limited.

use redis::AsyncCommands;
use crate::RedisPool;

/// Check + increment a rate-limit counter.
///
/// * `namespace`   – e.g. `"login"`, `"register"`, `"lead"`
/// * `identifier`  – e.g. email, IP, user_id
/// * `max`         – maximum requests allowed in `window_secs`
/// * `window_secs` – sliding window length in seconds
///
/// Returns `Ok(true)` = allowed, `Ok(false)` = blocked.
pub async fn check(
    redis: &mut RedisPool,
    namespace: &str,
    identifier: &str,
    max: i64,
    window_secs: i64,
) -> Result<bool, redis::RedisError> {
    let key = format!("rate:{namespace}:{identifier}");
    let count: i64 = redis.incr(&key, 1i64).await?;
    if count == 1 {
        redis.expire::<_, ()>(&key, window_secs).await?;
    }
    Ok(count <= max)
}

/// Convenience wrappers ───────────────────────────────────────────────────────

/// Register: max 10 per hour per email
pub async fn check_register(redis: &mut RedisPool, email: &str) -> Result<bool, redis::RedisError> {
    check(redis, "register", email, 10, 3_600).await
}

/// Login: max 10 attempts per 15 min per email
pub async fn check_login(redis: &mut RedisPool, email: &str) -> Result<bool, redis::RedisError> {
    check(redis, "login", email, 10, 900).await
}

/// Admin/employee login: max 5 attempts per 15 min per email (tighter than regular
/// user login because these accounts hold internal/super-admin privileges).
pub async fn check_admin_login(redis: &mut RedisPool, email: &str) -> Result<bool, redis::RedisError> {
    check(redis, "admin_login", email, 5, 900).await
}

/// Lead request: max 5 per hour per professional
pub async fn check_lead(redis: &mut RedisPool, professional_id: &str) -> Result<bool, redis::RedisError> {
    check(redis, "lead", professional_id, 5, 3_600).await
}

/// Job post: max 20 per hour per company
pub async fn check_job_post(redis: &mut RedisPool, company_id: &str) -> Result<bool, redis::RedisError> {
    check(redis, "job_post", company_id, 20, 3_600).await
}
