package com.rosetta.messenger.crypto

import org.junit.Test
import org.junit.Assert.*
import org.junit.Before

/**
 * Unit tests for CryptoManager
 * Tests critical cryptographic functions
 */
class CryptoManagerTest {
    
    @Before
    fun setup() {
        // Initialize BouncyCastle provider
        java.security.Security.addProvider(org.bouncycastle.jce.provider.BouncyCastleProvider())
    }
    
    @Test
    fun `generateSeedPhrase should return 12 words`() {
        val seedPhrase = CryptoManager.generateSeedPhrase()
        
        assertEquals("Seed phrase should contain 12 words", 12, seedPhrase.size)
        assertTrue("All words should be non-empty", seedPhrase.all { it.isNotEmpty() })
    }
    
    @Test
    fun `generateSeedPhrase should return unique phrases`() {
        val phrase1 = CryptoManager.generateSeedPhrase()
        val phrase2 = CryptoManager.generateSeedPhrase()
        
        assertNotEquals("Two generated seed phrases should be different", phrase1, phrase2)
    }
    
    @Test
    fun `generateKeyPairFromSeed should return valid key pair`() {
        val seedPhrase = CryptoManager.generateSeedPhrase()
        val keyPair = CryptoManager.generateKeyPairFromSeed(seedPhrase)
        
        // Compressed public key: starts with "02" or "03", 66 hex chars (33 bytes)
        assertTrue("Public key should start with 02 or 03 (compressed)", 
            keyPair.publicKey.startsWith("02") || keyPair.publicKey.startsWith("03"))
        assertEquals("Public key should be 66 chars (33 bytes hex, compressed)", 66, keyPair.publicKey.length)
        assertTrue("Private key should be 64 chars (32 bytes hex)", keyPair.privateKey.length == 64)
    }
    
    @Test
    fun `generateKeyPairFromSeed should be deterministic`() {
        val seedPhrase = listOf("abandon", "abandon", "abandon", "abandon", "abandon", 
                                 "abandon", "abandon", "abandon", "abandon", "abandon", 
                                 "abandon", "about")
        
        val keyPair1 = CryptoManager.generateKeyPairFromSeed(seedPhrase)
        val keyPair2 = CryptoManager.generateKeyPairFromSeed(seedPhrase)
        
        assertEquals("Same seed phrase should produce same public key", keyPair1.publicKey, keyPair2.publicKey)
        assertEquals("Same seed phrase should produce same private key", keyPair1.privateKey, keyPair2.privateKey)
    }
    
    @Test
    fun `validateSeedPhrase should accept valid phrase`() {
        val validPhrase = listOf("abandon", "abandon", "abandon", "abandon", "abandon", 
                                 "abandon", "abandon", "abandon", "abandon", "abandon", 
                                 "abandon", "about")
        
        assertTrue("Valid seed phrase should be accepted", CryptoManager.validateSeedPhrase(validPhrase))
    }
    
    @Test
    fun `validateSeedPhrase should reject invalid phrase`() {
        val invalidPhrase = listOf("invalid", "invalid", "invalid", "invalid", "invalid", 
                                   "invalid", "invalid", "invalid", "invalid", "invalid", 
                                   "invalid", "invalid")
        
        assertFalse("Invalid seed phrase should be rejected", CryptoManager.validateSeedPhrase(invalidPhrase))
    }
    
    // Note: Encryption tests commented out due to Android API dependencies (Deflater/Inflater)
    // These require instrumentation tests or Robolectric configuration
    
    /*
    @Test
    fun `encryptWithPassword should encrypt data`() {
        val originalData = "Hello, World! This is a secret message."
        val password = "testPassword123"
        
        val encrypted = CryptoManager.encryptWithPassword(originalData, password)
        
        assertNotNull("Encrypted data should not be null", encrypted)
        assertTrue("Encrypted data should not be empty", encrypted.isNotEmpty())
        assertFalse("Encrypted data should not contain original text", 
                    encrypted.contains("Hello"))
        assertTrue("Encrypted data should contain iv:ciphertext format", encrypted.contains(":"))
    }
    
    @Test
    fun `decryptWithPassword should decrypt correctly`() {
        val originalData = "Test data for encryption 12345 !@#$%"
        val password = "testPassword123"
        
        val encrypted = CryptoManager.encryptWithPassword(originalData, password)
        val decrypted = CryptoManager.decryptWithPassword(encrypted, password)
        
        assertEquals("Decrypted data should match original", originalData, decrypted)
    }
    
    @Test
    fun `decryptWithPassword with wrong password should return null`() {
        val originalData = "Secret message"
        val correctPassword = "correctPassword"
        val wrongPassword = "wrongPassword"
        
        val encrypted = CryptoManager.encryptWithPassword(originalData, correctPassword)
        val decrypted = CryptoManager.decryptWithPassword(encrypted, wrongPassword)
        
        assertNull("Should return null with wrong password", decrypted)
    }
    
    @Test
    fun `encryptWithPassword with different passwords should produce different results`() {
        val data = "Same data"
        
        val encrypted1 = CryptoManager.encryptWithPassword(data, "password1")
        val encrypted2 = CryptoManager.encryptWithPassword(data, "password2")
        
        assertNotEquals("Different passwords should produce different encrypted data", 
                       encrypted1, encrypted2)
    }
    
    @Test
    fun `encryption should handle empty string`() {
        val emptyData = ""
        val password = "password"
        
        val encrypted = CryptoManager.encryptWithPassword(emptyData, password)
        val decrypted = CryptoManager.decryptWithPassword(encrypted, password)
        
        assertEquals("Empty data should encrypt and decrypt correctly", emptyData, decrypted)
    }
    
    @Test
    fun `encryption should handle special characters`() {
        val specialData = "P@ssw0rd!#$%^&*()_+{}[]|\\:;<>?,./"
        val password = "password"
        
        val encrypted = CryptoManager.encryptWithPassword(specialData, password)
        val decrypted = CryptoManager.decryptWithPassword(encrypted, password)
        
        assertEquals("Should handle special characters", specialData, decrypted)
    }
    
    @Test
    fun `encryption should handle unicode characters`() {
        val unicodeData = "Hello 世界 🌍 مرحبا Привет"
        val password = "password"
        
        val encrypted = CryptoManager.encryptWithPassword(unicodeData, password)
        val decrypted = CryptoManager.decryptWithPassword(encrypted, password)
        
        assertEquals("Should handle unicode characters", unicodeData, decrypted)
    }
    */
    
    @Test
    fun `generatePrivateKeyHash should generate consistent hash`() {
        val privateKey = "abcdef1234567890"
        
        val hash1 = CryptoManager.generatePrivateKeyHash(privateKey)
        val hash2 = CryptoManager.generatePrivateKeyHash(privateKey)
        
        assertEquals("Same private key should produce same hash", hash1, hash2)
        assertEquals("Hash should be 64 chars (SHA-256)", 64, hash1.length)
    }
    
    @Test
    fun `generatePrivateKeyHash should generate different hashes for different keys`() {
        val hash1 = CryptoManager.generatePrivateKeyHash("key1")
        val hash2 = CryptoManager.generatePrivateKeyHash("key2")
        
        assertNotEquals("Different keys should produce different hashes", hash1, hash2)
    }
    
    @Test
    fun `seedPhraseToPrivateKey should be deterministic`() {
        val seedPhrase = listOf("abandon", "abandon", "abandon", "abandon", "abandon", 
                                "abandon", "abandon", "abandon", "abandon", "abandon", 
                                "abandon", "about")
        
        val privateKey1 = CryptoManager.seedPhraseToPrivateKey(seedPhrase)
        val privateKey2 = CryptoManager.seedPhraseToPrivateKey(seedPhrase)
        
        assertEquals("Same seed should produce same private key", privateKey1, privateKey2)
        assertTrue("Private key should be hex", privateKey1.all { it in '0'..'9' || it in 'a'..'f' })
    }
    
    /**
     * Cross-platform compatibility test.
     * These test vectors were generated using the Desktop (Electron) app's derivation:
     *   1. mnemonicToSeed(phrase) → BIP39 PBKDF2-SHA512 (2048 iterations) → 64 bytes
     *   2. hex(seed) → 128-char hex string
     *   3. SHA256(hexSeed) → 32-byte privateKey
     *   4. SHA256(privateKey + "rosetta") → privateKeyHash
     *
     * Mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
     * Desktop equivalent (SetPassword.tsx):
     *   let seed = await mnemonicToSeed(phrase);
     *   let hex = Buffer.from(seed).toString('hex');
     *   let keypair = await generateKeyPairFromSeed(hex);
     */
    @Test
    fun `seedPhraseToPrivateKey should match Desktop derivation`() {
        val seedPhrase = listOf("abandon", "abandon", "abandon", "abandon", "abandon",
                                "abandon", "abandon", "abandon", "abandon", "abandon",
                                "abandon", "about")
        
        val expectedPrivateKey = "0f57707d9198b6dc74195e972fa4fa214a2986a8bdfea7abf952e86f57715a31"
        val privateKey = CryptoManager.seedPhraseToPrivateKey(seedPhrase)
        
        assertEquals(
            "Private key must match Desktop's BIP39 → hex → SHA256 derivation",
            expectedPrivateKey,
            privateKey
        )
    }
    
    @Test
    fun `generatePrivateKeyHash should match Desktop derivation`() {
        // privateKey derived from "abandon...about" seed via BIP39 → SHA256
        val privateKey = "0f57707d9198b6dc74195e972fa4fa214a2986a8bdfea7abf952e86f57715a31"
        val expectedHash = "4e218a657b3a993f9f1dfcb18ecf39e2f322e89ddd9f98648b64142b376d910b"
        
        val hash = CryptoManager.generatePrivateKeyHash(privateKey)
        
        assertEquals(
            "Private key hash must match Desktop's SHA256(privateKey + 'rosetta')",
            expectedHash,
            hash
        )
    }
    
    @Test
    fun `full key derivation flow should produce Desktop-compatible keys`() {
        val seedPhrase = listOf("abandon", "abandon", "abandon", "abandon", "abandon",
                                "abandon", "abandon", "abandon", "abandon", "abandon",
                                "abandon", "about")
        
        val keyPair = CryptoManager.generateKeyPairFromSeed(seedPhrase)
        
        // Verify privateKey matches Desktop
        assertEquals(
            "0f57707d9198b6dc74195e972fa4fa214a2986a8bdfea7abf952e86f57715a31",
            keyPair.privateKey
        )
        
        // Verify compressed public key format (02 or 03 prefix, 66 hex chars)
        assertTrue("Public key should be compressed (66 hex chars)", keyPair.publicKey.length == 66)
        assertTrue("Public key should start with 02 or 03", 
            keyPair.publicKey.startsWith("02") || keyPair.publicKey.startsWith("03"))
        
        // Verify privateKeyHash matches Desktop
        val hash = CryptoManager.generatePrivateKeyHash(keyPair.privateKey)
        assertEquals(
            "4e218a657b3a993f9f1dfcb18ecf39e2f322e89ddd9f98648b64142b376d910b",
            hash
        )
    }
}

