//! Authentication and authorization for Database Gateway.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use parking_lot::RwLock;

/// API key for authentication.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ApiKey {
    /// Key identifier (public).
    pub key_id: String,
    /// Secret key (hashed).
    pub secret_hash: [u8; 32],
    /// Owner address.
    pub owner: [u8; 32],
    /// Database access permissions.
    pub databases: Vec<String>,
    /// Permission level.
    pub permissions: Permissions,
    /// Rate limit (requests per minute).
    pub rate_limit: u32,
    /// Created timestamp.
    pub created_at: u64,
    /// Expiry timestamp (0 = no expiry).
    pub expires_at: u64,
    /// Whether the key is active.
    pub active: bool,
}

impl ApiKey {
    /// Creates a new API key.
    pub fn new(owner: [u8; 32], secret: &str) -> Self {
        let key_id = generate_key_id();
        let secret_hash = *blake3::hash(secret.as_bytes()).as_bytes();
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();

        Self {
            key_id,
            secret_hash,
            owner,
            databases: Vec::new(),
            permissions: Permissions::default(),
            rate_limit: 1000,
            created_at: now,
            expires_at: 0,
            active: true,
        }
    }

    /// Validates a secret against this key.
    pub fn validate_secret(&self, secret: &str) -> bool {
        let hash = *blake3::hash(secret.as_bytes()).as_bytes();
        self.secret_hash == hash && self.active && !self.is_expired()
    }

    /// Checks if the key has expired.
    pub fn is_expired(&self) -> bool {
        if self.expires_at == 0 {
            return false;
        }
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();
        now > self.expires_at
    }

    /// Checks if the key can access a database.
    pub fn can_access_database(&self, database: &str) -> bool {
        self.databases.is_empty() || self.databases.contains(&database.to_string())
    }

    /// Checks if the key can perform an operation.
    pub fn can_perform(&self, op: Operation) -> bool {
        match op {
            Operation::Read => self.permissions.read,
            Operation::Write => self.permissions.write,
            Operation::Delete => self.permissions.delete,
            Operation::Admin => self.permissions.admin,
        }
    }
}

/// Permission levels.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Permissions {
    /// Can read data.
    pub read: bool,
    /// Can write data.
    pub write: bool,
    /// Can delete data.
    pub delete: bool,
    /// Admin operations (create/drop databases).
    pub admin: bool,
}

impl Default for Permissions {
    fn default() -> Self {
        Self {
            read: true,
            write: true,
            delete: false,
            admin: false,
        }
    }
}

impl Permissions {
    /// Full permissions.
    pub fn full() -> Self {
        Self {
            read: true,
            write: true,
            delete: true,
            admin: true,
        }
    }

    /// Read-only permissions.
    pub fn read_only() -> Self {
        Self {
            read: true,
            write: false,
            delete: false,
            admin: false,
        }
    }
}

/// Operation types for permission checking.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Operation {
    Read,
    Write,
    Delete,
    Admin,
}

/// Authentication configuration.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthConfig {
    /// Whether authentication is required.
    pub required: bool,
    /// API key header name.
    pub header_name: String,
    /// Allow anonymous read access.
    pub allow_anonymous_read: bool,
}

impl Default for AuthConfig {
    fn default() -> Self {
        Self {
            required: true,
            header_name: "X-API-Key".to_string(),
            allow_anonymous_read: false,
        }
    }
}

/// Authentication middleware state.
pub struct AuthMiddleware {
    /// Configuration.
    config: AuthConfig,
    /// API keys store.
    keys: Arc<RwLock<HashMap<String, ApiKey>>>,
    /// Rate limit tracking (key_id -> (count, window_start)).
    rate_limits: Arc<RwLock<HashMap<String, (u32, u64)>>>,
}

impl AuthMiddleware {
    /// Creates a new auth middleware.
    pub fn new(config: AuthConfig) -> Self {
        Self {
            config,
            keys: Arc::new(RwLock::new(HashMap::new())),
            rate_limits: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Registers an API key.
    pub fn register_key(&self, key: ApiKey) {
        self.keys.write().insert(key.key_id.clone(), key);
    }

    /// Revokes an API key.
    pub fn revoke_key(&self, key_id: &str) -> bool {
        self.keys.write().remove(key_id).is_some()
    }

    /// Validates an API key string (format: key_id:secret).
    pub fn validate(&self, api_key: &str) -> Result<ApiKey, AuthError> {
        let parts: Vec<&str> = api_key.splitn(2, ':').collect();
        if parts.len() != 2 {
            return Err(AuthError::InvalidFormat);
        }

        let key_id = parts[0];
        let secret = parts[1];

        let keys = self.keys.read();
        let key = keys.get(key_id).ok_or(AuthError::KeyNotFound)?;

        if !key.validate_secret(secret) {
            return Err(AuthError::InvalidSecret);
        }

        // Check rate limit
        self.check_rate_limit(key)?;

        Ok(key.clone())
    }

    /// Checks rate limit for a key.
    fn check_rate_limit(&self, key: &ApiKey) -> Result<(), AuthError> {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();

        let mut limits = self.rate_limits.write();
        let entry = limits.entry(key.key_id.clone()).or_insert((0, now));

        // Reset if window expired (1 minute)
        if now - entry.1 > 60 {
            *entry = (1, now);
            return Ok(());
        }

        // Check if limit exceeded
        if entry.0 >= key.rate_limit {
            return Err(AuthError::RateLimitExceeded);
        }

        entry.0 += 1;
        Ok(())
    }

    /// Gets the auth config.
    pub fn config(&self) -> &AuthConfig {
        &self.config
    }
}

/// Authentication errors.
#[derive(Debug, Clone)]
pub enum AuthError {
    /// Missing API key.
    MissingKey,
    /// Invalid key format.
    InvalidFormat,
    /// Key not found.
    KeyNotFound,
    /// Invalid secret.
    InvalidSecret,
    /// Key expired.
    Expired,
    /// Rate limit exceeded.
    RateLimitExceeded,
    /// Insufficient permissions.
    InsufficientPermissions,
    /// Access denied to database.
    AccessDenied,
}

impl std::fmt::Display for AuthError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AuthError::MissingKey => write!(f, "Missing API key"),
            AuthError::InvalidFormat => write!(f, "Invalid API key format"),
            AuthError::KeyNotFound => write!(f, "API key not found"),
            AuthError::InvalidSecret => write!(f, "Invalid API secret"),
            AuthError::Expired => write!(f, "API key expired"),
            AuthError::RateLimitExceeded => write!(f, "Rate limit exceeded"),
            AuthError::InsufficientPermissions => write!(f, "Insufficient permissions"),
            AuthError::AccessDenied => write!(f, "Access denied to database"),
        }
    }
}

impl std::error::Error for AuthError {}

/// Generates a random key ID.
fn generate_key_id() -> String {
    let timestamp = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_nanos();

    let hash = blake3::hash(&timestamp.to_le_bytes());
    format!("sdb_{}", hex::encode(&hash.as_bytes()[..12]))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_api_key_creation() {
        let key = ApiKey::new([1u8; 32], "secret123");
        assert!(key.key_id.starts_with("sdb_"));
        assert!(key.active);
    }

    #[test]
    fn test_secret_validation() {
        let key = ApiKey::new([1u8; 32], "secret123");
        assert!(key.validate_secret("secret123"));
        assert!(!key.validate_secret("wrong"));
    }

    #[test]
    fn test_auth_middleware() {
        let auth = AuthMiddleware::new(AuthConfig::default());
        let key = ApiKey::new([1u8; 32], "secret123");
        let key_id = key.key_id.clone();
        auth.register_key(key);

        let api_key = format!("{}:secret123", key_id);
        let result = auth.validate(&api_key);
        assert!(result.is_ok());
    }

    #[test]
    fn test_permissions() {
        let mut key = ApiKey::new([1u8; 32], "test");
        key.permissions = Permissions::read_only();

        assert!(key.can_perform(Operation::Read));
        assert!(!key.can_perform(Operation::Write));
        assert!(!key.can_perform(Operation::Admin));
    }
}
