use std::time::{SystemTime, UNIX_EPOCH};

use dashmap::DashMap;
use uuid::Uuid;

use crate::registry::Role;

pub const COOKIE_NAME: &str = "ontoref-session";
const SESSION_SECS: u64 = 30 * 24 * 3600;

#[derive(Clone)]
pub struct SessionEntry {
    pub slug: String,
    pub role: Role,
    pub expires: u64,
}

pub struct SessionStore {
    sessions: DashMap<String, SessionEntry>,
}

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

impl SessionStore {
    pub fn new() -> Self {
        Self {
            sessions: DashMap::new(),
        }
    }

    pub fn create(&self, slug: String, role: Role) -> String {
        let token = Uuid::new_v4().to_string();
        let expires = now_secs() + SESSION_SECS;
        self.sessions.insert(
            token.clone(),
            SessionEntry {
                slug,
                role,
                expires,
            },
        );
        token
    }

    pub fn get(&self, token: &str) -> Option<SessionEntry> {
        let entry = self.sessions.get(token)?;
        if entry.expires < now_secs() {
            drop(entry);
            self.sessions.remove(token);
            return None;
        }
        Some(entry.clone())
    }

    pub fn revoke(&self, token: &str) {
        self.sessions.remove(token);
    }
}

/// Extract the value of a named cookie from a raw `Cookie:` header string.
pub fn extract_cookie(header: &str, name: &str) -> Option<String> {
    for part in header.split(';') {
        if let Some((k, v)) = part.trim().split_once('=') {
            if k.trim() == name {
                return Some(v.trim().to_string());
            }
        }
    }
    None
}

fn now_secs() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs()
}
