//! [`TokenPair`] — the value returned by `login!` and `refresh_tokens!`.
//!
//! Also houses the [`RefreshTokenTracker`] for rotation / reuse detection.

use std::collections::HashSet;
use std::sync::Mutex;

use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};

/// An access/refresh token pair issued after a successful login or token exchange.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenPair {
    /// Short-lived JWT access token.
    pub access_token: String,
    /// Long-lived opaque refresh token (also a JWT in this implementation).
    pub refresh_token: String,
}

/// Global refresh token reuse detector.
pub static REFRESH_TRACKER: Lazy<RefreshTokenTracker> = Lazy::new(RefreshTokenTracker::new);

/// Detects refresh token reuse by tracking SHA-256 hashes of used tokens.
///
/// When a refresh token is exchanged for a new pair, the old token's hash is
/// stored.  If the same token is presented again, it indicates a stolen token
/// and the exchange is rejected.
///
/// # Memory
///
/// This is an in-memory store — hashes accumulate until process restart.
/// Production deployments should periodically prune or use a TTL-backed store.
pub struct RefreshTokenTracker {
    used: Mutex<HashSet<String>>,
}

impl RefreshTokenTracker {
    /// Create an empty tracker.
    pub fn new() -> Self {
        Self {
            used: Mutex::new(HashSet::new()),
        }
    }

    /// Mark a token hash as used.
    pub fn mark_used(&self, token_hash: &str) {
        self.used.lock().unwrap().insert(token_hash.to_string());
    }

    /// Check if a token hash has already been used.
    pub fn is_used(&self, token_hash: &str) -> bool {
        self.used.lock().unwrap().contains(token_hash)
    }

    /// Clear all stored hashes.
    pub fn clear(&self) {
        self.used.lock().unwrap().clear();
    }
}

impl Default for RefreshTokenTracker {
    fn default() -> Self {
        Self::new()
    }
}
