use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};

use axum_extra::extract::cookie::{Cookie, SameSite};
use rand::Rng;
use rand::distributions::Alphanumeric;
use tokio::sync::RwLock;

pub struct WebSession {
    pub slug: String,
    pub role: String,
    pub expires_at: Instant,
    /// CSRF token — bound to the session for its full lifetime. Minted
    /// alongside the session cookie via [`generate_token`] and validated
    /// by the `CsrfForm` extractor on every state-changing POST under
    /// `/web/`. Not rotated per request (would break multi-tab use).
    pub csrf_token: String,
}

pub type SessionStore = Arc<RwLock<HashMap<String, WebSession>>>;

pub fn new_store() -> SessionStore {
    Arc::new(RwLock::new(HashMap::new()))
}

/// Cookie name shared by login and magic-link flows.
pub const SESSION_COOKIE: &str = "session";

/// 43 alphanumeric chars ≈ 256 bits of entropy — enough for a session
/// or short-lived magic-link token.
pub const TOKEN_LEN: usize = 43;

/// CSPRNG-backed token. Used for both long-lived session cookies and
/// short-lived magic-link tokens; both need the same entropy.
pub fn generate_token() -> String {
    rand::thread_rng()
        .sample_iter(&Alphanumeric)
        .take(TOKEN_LEN)
        .map(char::from)
        .collect()
}

/// Builds a `HttpOnly + SameSite=Strict + Path=/` cookie for the session
/// token. `secure` is wired from `Config::cookie_secure` so local HTTP dev
/// can opt out — browsers refuse `Secure` cookies on non-HTTPS origins.
pub fn build_session_cookie(value: String, max_age: Duration, secure: bool) -> Cookie<'static> {
    Cookie::build((SESSION_COOKIE, value))
        .http_only(true)
        .secure(secure)
        .same_site(SameSite::Strict)
        .path("/")
        .max_age(time::Duration::seconds(max_age.as_secs() as i64))
        .build()
}
