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

use chrono::{DateTime, Utc};
use uuid::Uuid;
use zeroize::Zeroize;

#[cfg(feature = "nats")]
pub mod nats_handler;

/// A credential value held in memory for the lease's lifetime.
#[derive(Debug, Clone)]
pub struct Credential {
    /// The plaintext credential (e.g. HCLOUD_TOKEN value).
    pub value: String,
    /// Human-readable description (provider, resource type).
    pub description: String,
}

impl Drop for Credential {
    fn drop(&mut self) {
        self.value.zeroize();
    }
}

/// An active lease record.
struct Lease {
    lease_id: String,
    secret_path: String,
    session_id: String,
    credential: Credential,
    issued_at: DateTime<Utc>,
    expires_at: DateTime<Utc>,
}

/// In-memory lease store. Thread-safe.
pub struct LeaseStore {
    inner: Arc<Mutex<HashMap<String, Lease>>>,
}

impl Default for LeaseStore {
    fn default() -> Self {
        Self {
            inner: Arc::new(Mutex::new(HashMap::new())),
        }
    }
}

impl LeaseStore {
    pub fn new() -> Self {
        Self::default()
    }

    /// Issue a new lease for a given secret path.
    ///
    /// Returns `(lease_id, ttl_secs)`.
    pub fn issue(
        &self,
        secret_path: String,
        session_id: String,
        credential: Credential,
        ttl: Duration,
    ) -> (String, u64) {
        let lease_id = Uuid::new_v4().to_string();
        let now = Utc::now();
        let ttl_secs = ttl.as_secs();
        let expires_at = now + chrono::Duration::seconds(ttl_secs as i64);

        let lease = Lease {
            lease_id: lease_id.clone(),
            secret_path,
            session_id,
            credential,
            issued_at: now,
            expires_at,
        };

        self.inner.lock().unwrap().insert(lease_id.clone(), lease);
        (lease_id, ttl_secs)
    }

    /// Retrieve the credential for a lease by ID.
    ///
    /// Returns `None` if the lease doesn't exist or has expired.
    pub fn get_credential(&self, lease_id: &str) -> Option<String> {
        let store = self.inner.lock().unwrap();
        let lease = store.get(lease_id)?;
        if Utc::now() > lease.expires_at {
            return None;
        }
        Some(lease.credential.value.clone())
    }

    /// Revoke a lease, removing it (and dropping its zeroized credential).
    pub fn revoke(&self, lease_id: &str) {
        self.inner.lock().unwrap().remove(lease_id);
    }

    /// Purge all expired leases. Call periodically.
    pub fn purge_expired(&self) {
        let now = Utc::now();
        self.inner.lock().unwrap().retain(|_, l| l.expires_at > now);
    }
}
