//! Auth token management in Redis.
//!
//! Refresh tokens
//! ──────────────
//! `refresh:{token}`  → user_id, TTL 30 days
//!
//! Password-reset tokens
//! ─────────────────────
//! `reset:{token}`    → user_id, TTL 1 hour

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

const REFRESH_TTL: u64 = 30 * 24 * 3_600;  // 30 days in seconds
const RESET_TTL: u64   = 900;              // 15 minutes

// ── Refresh tokens ────────────────────────────────────────────────────────────

pub async fn store_refresh(
    redis: &mut RedisPool,
    token: &str,
    user_id: &str,
) -> Result<(), redis::RedisError> {
    let key = format!("refresh:{token}");
    redis.set_ex(key, user_id, REFRESH_TTL).await
}

/// Returns the user_id string associated with this token, or `None` if expired/invalid.
pub async fn get_refresh(
    redis: &mut RedisPool,
    token: &str,
) -> Result<Option<String>, redis::RedisError> {
    let key = format!("refresh:{token}");
    redis.get(key).await
}

/// Delete refresh token (logout / rotation).
pub async fn revoke_refresh(
    redis: &mut RedisPool,
    token: &str,
) -> Result<(), redis::RedisError> {
    let key = format!("refresh:{token}");
    redis.del(key).await
}

// ── Password-reset tokens ─────────────────────────────────────────────────────

pub async fn store_reset(
    redis: &mut RedisPool,
    token: &str,
    user_id: &str,
) -> Result<(), redis::RedisError> {
    let key = format!("reset:{token}");
    let plain_key = format!("otp:plain:{user_id}");
    // Store both: token→user_id (for verification) and plain→token (for dev debugging)
    redis.set_ex::<_, _, ()>(&plain_key, token, RESET_TTL).await?;
    redis.set_ex::<_, _, ()>(key, user_id, RESET_TTL).await
}

/// Atomically fetch and delete the reset token (single-use).
pub async fn consume_reset(
    redis: &mut RedisPool,
    token: &str,
) -> Result<Option<String>, redis::RedisError> {
    let key = format!("reset:{token}");
    redis.get_del(key).await
}

// ── Employee (internal admin) password-reset tokens ──────────────────────────
// Distinct key namespace from the user-facing reset tokens above, so a code
// generated for one identity store can never be consumed against the other.

pub async fn store_employee_reset(
    redis: &mut RedisPool,
    token: &str,
    employee_id: &str,
) -> Result<(), redis::RedisError> {
    let key = format!("reset:employee:{token}");
    redis.set_ex(key, employee_id, RESET_TTL).await
}

/// Atomically fetch and delete the employee reset token (single-use).
pub async fn consume_employee_reset(
    redis: &mut RedisPool,
    token: &str,
) -> Result<Option<String>, redis::RedisError> {
    let key = format!("reset:employee:{token}");
    redis.get_del(key).await
}
