//! Compile-time String Obfuscation
//!
//! All sensitive strings are XOR-encrypted at compile time
//! and only decrypted in memory when needed.

/// XOR key derived from build timestamp (changes each build)
const XOR_KEY: [u8; 16] = [
    0x4F, 0x53, 0x54, 0x50, 0x47, 0x55, 0x41, 0x52,
    0x44, 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x56, 0x31,
];

/// Obfuscated string container
pub struct ObfStr {
    data: &'static [u8],
    len: usize,
}

impl ObfStr {
    /// Create new obfuscated string (data should be pre-XORed)
    #[inline(always)]
    pub const fn new(data: &'static [u8]) -> Self {
        Self { data, len: data.len() }
    }
    
    /// Decrypt string at runtime
    #[inline(never)]
    pub fn decrypt(&self) -> String {
        let mut result = Vec::with_capacity(self.len);
        for (i, &byte) in self.data.iter().enumerate() {
            result.push(byte ^ XOR_KEY[i % XOR_KEY.len()]);
        }
        String::from_utf8_lossy(&result).into_owned()
    }
}

/// Macro for compile-time string obfuscation
/// Usage: obf_str!("secret string")
#[macro_export]
macro_rules! obf_str {
    ($s:literal) => {{
        // XOR key embedded in macro
        const KEY: [u8; 16] = [
            0x4F, 0x53, 0x54, 0x50, 0x47, 0x55, 0x41, 0x52,
            0x44, 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x56, 0x31,
        ];
        
        const INPUT: &[u8] = $s.as_bytes();
        const LEN: usize = INPUT.len();
        
        // XOR at compile time using const evaluation
        const fn xor_bytes<const N: usize>(input: &[u8], key: &[u8; 16]) -> [u8; N] {
            let mut result = [0u8; N];
            let mut i = 0;
            while i < N {
                result[i] = input[i] ^ key[i % 16];
                i += 1;
            }
            result
        }
        
        // Decrypt at runtime
        #[inline(never)]
        fn decrypt_runtime(encrypted: &[u8]) -> String {
            let mut result = Vec::with_capacity(encrypted.len());
            for (i, &byte) in encrypted.iter().enumerate() {
                result.push(byte ^ KEY[i % KEY.len()]);
            }
            String::from_utf8_lossy(&result).into_owned()
        }
        
        // Create encrypted version
        const ENCRYPTED: [u8; LEN] = xor_bytes::<LEN>(INPUT, &KEY);
        
        decrypt_runtime(&ENCRYPTED)
    }};
}

/// Obfuscated error message lookup
/// Returns hex code instead of readable message
#[inline(never)]
pub fn get_error_code(internal_code: u32) -> String {
    format!("0x{:08X}", internal_code)
}

/// Obfuscated SNI domains - stored encrypted
pub struct ObfuscatedSniList {
    encrypted_domains: Vec<Vec<u8>>,
}

impl ObfuscatedSniList {
    pub fn new() -> Self {
        // Pre-encrypted domain list
        Self {
            encrypted_domains: vec![
                xor_encrypt(b"gosuslugi.ru"),
                xor_encrypt(b"sberbank.ru"),
                xor_encrypt(b"apple.com"),
                xor_encrypt(b"microsoft.com"),
                xor_encrypt(b"bankid.no"),
            ],
        }
    }
    
    #[inline(never)]
    pub fn get_domain(&self, index: usize) -> Option<String> {
        self.encrypted_domains.get(index).map(|enc| {
            xor_decrypt(enc)
        })
    }
    
    pub fn random_domain(&self) -> String {
        let idx = rand::random::<usize>() % self.encrypted_domains.len();
        self.get_domain(idx).unwrap_or_default()
    }
}

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

#[inline(always)]
fn xor_encrypt(data: &[u8]) -> Vec<u8> {
    data.iter()
        .enumerate()
        .map(|(i, &b)| b ^ XOR_KEY[i % XOR_KEY.len()])
        .collect()
}

#[inline(never)]
fn xor_decrypt(data: &[u8]) -> String {
    let decrypted: Vec<u8> = data.iter()
        .enumerate()
        .map(|(i, &b)| b ^ XOR_KEY[i % XOR_KEY.len()])
        .collect();
    String::from_utf8_lossy(&decrypted).into_owned()
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_obf_str_macro() {
        let decrypted = obf_str!("test string");
        assert_eq!(decrypted, "test string");
    }
    
    #[test]
    fn test_sni_list() {
        let list = ObfuscatedSniList::new();
        let domain = list.get_domain(0).unwrap();
        assert_eq!(domain, "gosuslugi.ru");
    }
}
