//! Encryption Contract Parser
//!
//! Extracts encryption metadata from Nickel contracts.
//!
//! Supported syntax:
//! - `field | Sensitive` - Mark as sensitive (uses default backend)
//! - `field | Sensitive Backend="age"` - Specify encryption backend
//! - `field | Sensitive Backend="age" Key="/path/to/key"` - Specify backend and key path
//! - `field | Sensitive Backend="rustyvault" Vault="http://vault:8200"` - RustyVault config

use super::schema_ir::EncryptionMetadata;
use std::collections::HashMap;

/// Parser for encryption-related contract annotations
pub struct EncryptionContractParser;

impl EncryptionContractParser {
    /// Parse encryption metadata from a Nickel contract string
    ///
    /// # Arguments
    ///
    /// * `contract` - The Nickel contract string (e.g., "String | Sensitive Backend=\"age\"")
    ///
    /// # Returns
    ///
    /// `Some(EncryptionMetadata)` if the contract contains Sensitive annotation,
    /// `None` otherwise
    pub fn parse_encryption_metadata(contract: &str) -> Option<EncryptionMetadata> {
        // Check if contract contains Sensitive annotation
        if !contract.contains("Sensitive") {
            return None;
        }

        // Extract the part after Sensitive
        let sensitive_part = Self::extract_sensitive_part(contract)?;

        // Parse backend and attributes
        let backend = Self::extract_attribute(&sensitive_part, "Backend");
        let key = Self::extract_attribute(&sensitive_part, "Key");

        Some(EncryptionMetadata {
            sensitive: true,
            backend,
            key,
        })
    }

    /// Extract the part of contract after "Sensitive" keyword
    fn extract_sensitive_part(contract: &str) -> Option<String> {
        // Find "Sensitive" keyword
        let start = contract.find("Sensitive")?;
        // Get everything from "Sensitive" onwards
        Some(contract[start..].to_string())
    }

    /// Extract a quoted attribute value from the sensitive part
    ///
    /// # Examples
    ///
    /// - `Backend="age"` → Some("age")
    /// - `Key="/path/to/key"` → Some("/path/to/key")
    /// - `Vault="http://vault:8200"` → Some("http://vault:8200")
    fn extract_attribute(sensitive_part: &str, attr_name: &str) -> Option<String> {
        // Pattern: attr_name="value" (with double quotes)
        let pattern = format!("{}=\"", attr_name);

        // Find the pattern
        let start = sensitive_part.find(&pattern)?;
        let value_start = start + pattern.len();

        // Find the closing quote
        let value_end = sensitive_part[value_start..].find('"')?;

        Some(sensitive_part[value_start..value_start + value_end].to_string())
    }

    /// Extract all attributes from the Sensitive annotation
    pub fn extract_attributes(contract: &str) -> HashMap<String, String> {
        let mut attrs = HashMap::new();

        if !contract.contains("Sensitive") {
            return attrs;
        }

        let sensitive_part = match Self::extract_sensitive_part(contract) {
            Some(part) => part,
            None => return attrs,
        };

        // Extract known attributes
        if let Some(backend) = Self::extract_attribute(&sensitive_part, "Backend") {
            attrs.insert("Backend".to_string(), backend);
        }
        if let Some(key) = Self::extract_attribute(&sensitive_part, "Key") {
            attrs.insert("Key".to_string(), key);
        }
        if let Some(vault) = Self::extract_attribute(&sensitive_part, "Vault") {
            attrs.insert("Vault".to_string(), vault);
        }
        if let Some(token) = Self::extract_attribute(&sensitive_part, "Token") {
            attrs.insert("Token".to_string(), token);
        }

        attrs
    }
}

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

    #[test]
    fn test_parse_sensitive_no_backend() {
        let contract = "String | Sensitive";
        let metadata = EncryptionContractParser::parse_encryption_metadata(contract).unwrap();

        assert!(metadata.sensitive);
        assert_eq!(metadata.backend, None);
        assert_eq!(metadata.key, None);
    }

    #[test]
    fn test_parse_sensitive_with_age_backend() {
        let contract = "String | Sensitive Backend=\"age\"";
        let metadata = EncryptionContractParser::parse_encryption_metadata(contract).unwrap();

        assert!(metadata.sensitive);
        assert_eq!(metadata.backend, Some("age".to_string()));
        assert_eq!(metadata.key, None);
    }

    #[test]
    fn test_parse_sensitive_with_backend_and_key() {
        let contract = "String | Sensitive Backend=\"age\" Key=\"/home/user/.age/key.txt\"";
        let metadata = EncryptionContractParser::parse_encryption_metadata(contract).unwrap();

        assert!(metadata.sensitive);
        assert_eq!(metadata.backend, Some("age".to_string()));
        assert_eq!(metadata.key, Some("/home/user/.age/key.txt".to_string()));
    }

    #[test]
    fn test_parse_rustyvault_backend() {
        let contract = "String | Sensitive Backend=\"rustyvault\" Vault=\"http://vault:8200\"";
        let metadata = EncryptionContractParser::parse_encryption_metadata(contract).unwrap();

        assert!(metadata.sensitive);
        assert_eq!(metadata.backend, Some("rustyvault".to_string()));
    }

    #[test]
    fn test_non_sensitive_contract() {
        let contract = "String | std.string.NonEmpty";
        let metadata = EncryptionContractParser::parse_encryption_metadata(contract);

        assert!(metadata.is_none());
    }

    #[test]
    fn test_extract_attributes() {
        let contract =
            "String | Sensitive Backend=\"age\" Key=\"/path/to/key\" Vault=\"http://vault\"";
        let attrs = EncryptionContractParser::extract_attributes(contract);

        assert_eq!(attrs.get("Backend"), Some(&"age".to_string()));
        assert_eq!(attrs.get("Key"), Some(&"/path/to/key".to_string()));
        assert_eq!(attrs.get("Vault"), Some(&"http://vault".to_string()));
    }

    #[test]
    fn test_multiple_pipes_in_contract() {
        let contract = "String | std.string.NonEmpty | Sensitive Backend=\"age\"";
        let metadata = EncryptionContractParser::parse_encryption_metadata(contract).unwrap();

        assert!(metadata.sensitive);
        assert_eq!(metadata.backend, Some("age".to_string()));
    }

    #[test]
    fn test_special_characters_in_value() {
        let contract = "String | Sensitive Key=\"/home/user/.age/key-backup_v2.txt\"";
        let metadata = EncryptionContractParser::parse_encryption_metadata(contract).unwrap();

        assert_eq!(
            metadata.key,
            Some("/home/user/.age/key-backup_v2.txt".to_string())
        );
    }

    #[test]
    fn test_whitespace_handling() {
        let contract = "String | Sensitive   Backend=\"age\"   Key=\"/path\"";
        let metadata = EncryptionContractParser::parse_encryption_metadata(contract).unwrap();

        assert_eq!(metadata.backend, Some("age".to_string()));
        assert_eq!(metadata.key, Some("/path".to_string()));
    }

    #[test]
    fn test_no_sensitive_in_contract() {
        let contract = "String | std.string.length.min 5";
        let metadata = EncryptionContractParser::parse_encryption_metadata(contract);

        assert!(metadata.is_none());
    }
}
