//! OS Keychain integration for secure credential storage
//!
//! Provides cross-platform secure storage using:
//! - macOS: Keychain Services (TouchID/FaceID support)
//! - Windows: Credential Manager (Windows Hello support)
//! - Linux: Secret Service (GNOME Keyring, KDE Wallet)

use keyring::Entry;
use serde::{Deserialize, Serialize};
use sha2::{Sha256, Digest};
use zeroize::Zeroize;

use crate::{Error, Result};

/// Service name for keychain entries
const SERVICE_NAME: &str = "com.synor.wallet";

/// Username prefix for keychain entries
const KEY_PREFIX: &str = "synor-wallet";

/// Keychain key types
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeychainKeyType {
    /// Master unlock key (derived from password, stored for biometric unlock)
    MasterUnlock,
    /// Session key (temporary, for "remember me" during session)
    Session,
    /// Backup encryption key (for encrypted backups)
    Backup,
}

impl KeychainKeyType {
    fn as_str(&self) -> &'static str {
        match self {
            Self::MasterUnlock => "master-unlock",
            Self::Session => "session",
            Self::Backup => "backup",
        }
    }
}

/// Stored keychain data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeychainData {
    /// Key hash for verification
    pub key_hash: String,
    /// Encrypted payload (depends on key type)
    pub payload: String,
    /// Creation timestamp
    pub created_at: i64,
    /// Wallet identifier (for multi-wallet support)
    pub wallet_id: String,
}

/// Keychain service for the desktop wallet
pub struct KeychainService {
    /// Wallet identifier (derived from first address)
    wallet_id: String,
}

impl KeychainService {
    /// Create a new keychain service for a wallet
    pub fn new(wallet_id: &str) -> Self {
        Self {
            wallet_id: wallet_id.to_string(),
        }
    }

    /// Create from wallet address (uses hash of address as ID)
    pub fn from_address(address: &str) -> Self {
        let mut hasher = Sha256::new();
        hasher.update(address.as_bytes());
        let hash = hasher.finalize();
        let wallet_id = hex::encode(&hash[..8]);
        Self::new(&wallet_id)
    }

    /// Get the keychain entry name for a key type
    fn entry_name(&self, key_type: KeychainKeyType) -> String {
        format!("{}-{}-{}", KEY_PREFIX, self.wallet_id, key_type.as_str())
    }

    /// Check if the OS keychain is available
    pub fn is_available() -> bool {
        let test_entry = Entry::new(SERVICE_NAME, "availability-test");
        match test_entry {
            Ok(_) => true,
            Err(_) => false,
        }
    }

    /// Store a key in the OS keychain
    pub fn store_key(&self, key_type: KeychainKeyType, password_hash: &[u8]) -> Result<()> {
        let entry_name = self.entry_name(key_type);
        let entry = Entry::new(SERVICE_NAME, &entry_name)
            .map_err(|e| Error::Keychain(format!("Failed to create keychain entry: {}", e)))?;

        // Create keychain data
        let data = KeychainData {
            key_hash: hex::encode(Sha256::digest(password_hash)),
            payload: hex::encode(password_hash),
            created_at: current_timestamp(),
            wallet_id: self.wallet_id.clone(),
        };

        let json = serde_json::to_string(&data)
            .map_err(|e| Error::Serialization(e.to_string()))?;

        entry.set_password(&json)
            .map_err(|e| Error::Keychain(format!("Failed to store in keychain: {}", e)))?;

        Ok(())
    }

    /// Retrieve a key from the OS keychain
    pub fn retrieve_key(&self, key_type: KeychainKeyType) -> Result<Vec<u8>> {
        let entry_name = self.entry_name(key_type);
        let entry = Entry::new(SERVICE_NAME, &entry_name)
            .map_err(|e| Error::Keychain(format!("Failed to access keychain entry: {}", e)))?;

        let json = entry.get_password()
            .map_err(|e| Error::Keychain(format!("Key not found in keychain: {}", e)))?;

        let data: KeychainData = serde_json::from_str(&json)
            .map_err(|e| Error::Serialization(e.to_string()))?;

        // Verify wallet ID matches
        if data.wallet_id != self.wallet_id {
            return Err(Error::Keychain("Wallet ID mismatch".to_string()));
        }

        let key = hex::decode(&data.payload)
            .map_err(|e| Error::Keychain(format!("Invalid key format: {}", e)))?;

        Ok(key)
    }

    /// Check if a key exists in the keychain
    pub fn has_key(&self, key_type: KeychainKeyType) -> bool {
        let entry_name = self.entry_name(key_type);
        if let Ok(entry) = Entry::new(SERVICE_NAME, &entry_name) {
            entry.get_password().is_ok()
        } else {
            false
        }
    }

    /// Delete a key from the keychain
    pub fn delete_key(&self, key_type: KeychainKeyType) -> Result<()> {
        let entry_name = self.entry_name(key_type);
        let entry = Entry::new(SERVICE_NAME, &entry_name)
            .map_err(|e| Error::Keychain(format!("Failed to access keychain entry: {}", e)))?;

        entry.delete_credential()
            .map_err(|e| Error::Keychain(format!("Failed to delete from keychain: {}", e)))?;

        Ok(())
    }

    /// Delete all keys for this wallet
    pub fn delete_all_keys(&self) -> Result<()> {
        let key_types = [
            KeychainKeyType::MasterUnlock,
            KeychainKeyType::Session,
            KeychainKeyType::Backup,
        ];

        for key_type in key_types {
            // Ignore errors for keys that don't exist
            let _ = self.delete_key(key_type);
        }

        Ok(())
    }

    /// Store master unlock key for biometric access
    /// This allows unlocking the wallet using TouchID/FaceID/Windows Hello
    pub fn enable_biometric_unlock(&self, password: &str) -> Result<()> {
        // Derive a key from the password
        let mut hasher = Sha256::new();
        hasher.update(password.as_bytes());
        hasher.update(self.wallet_id.as_bytes());
        let key = hasher.finalize();

        self.store_key(KeychainKeyType::MasterUnlock, &key)?;

        Ok(())
    }

    /// Retrieve master unlock key for biometric access
    pub fn get_biometric_unlock_key(&self) -> Result<Vec<u8>> {
        self.retrieve_key(KeychainKeyType::MasterUnlock)
    }

    /// Verify if a password matches the stored biometric key
    pub fn verify_biometric_key(&self, password: &str) -> Result<bool> {
        let stored_key = self.retrieve_key(KeychainKeyType::MasterUnlock)?;

        // Derive key from password and compare
        let mut hasher = Sha256::new();
        hasher.update(password.as_bytes());
        hasher.update(self.wallet_id.as_bytes());
        let derived_key = hasher.finalize();

        Ok(stored_key == derived_key.as_slice())
    }

    /// Disable biometric unlock
    pub fn disable_biometric_unlock(&self) -> Result<()> {
        self.delete_key(KeychainKeyType::MasterUnlock)
    }

    /// Check if biometric unlock is enabled
    pub fn is_biometric_enabled(&self) -> bool {
        self.has_key(KeychainKeyType::MasterUnlock)
    }
}

/// Password-derived key for additional security layer
#[derive(Zeroize)]
#[zeroize(drop)]
pub struct DerivedKey {
    key: [u8; 32],
}

impl DerivedKey {
    /// Derive a key from password and salt using SHA-256
    /// For Argon2-based derivation, use the crypto module's encrypt_seed
    pub fn derive(password: &str, salt: &[u8]) -> Self {
        // Use HMAC-SHA256 for simple key derivation
        use hmac::{Hmac, Mac};
        type HmacSha256 = Hmac<Sha256>;

        let mut mac = HmacSha256::new_from_slice(salt)
            .expect("HMAC can take key of any size");
        mac.update(password.as_bytes());
        let result = mac.finalize();

        let mut key = [0u8; 32];
        key.copy_from_slice(&result.into_bytes());

        Self { key }
    }

    /// Get the key bytes
    pub fn as_bytes(&self) -> &[u8; 32] {
        &self.key
    }
}

/// Get current Unix timestamp
fn current_timestamp() -> i64 {
    std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs() as i64)
        .unwrap_or(0)
}

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

    #[test]
    fn test_keychain_service_creation() {
        let service = KeychainService::new("test-wallet");
        assert_eq!(service.wallet_id, "test-wallet");
    }

    #[test]
    fn test_from_address() {
        let service = KeychainService::from_address("synor1abc123");
        assert!(!service.wallet_id.is_empty());
        assert_eq!(service.wallet_id.len(), 16); // 8 bytes = 16 hex chars
    }

    #[test]
    fn test_entry_name() {
        let service = KeychainService::new("test");
        let name = service.entry_name(KeychainKeyType::MasterUnlock);
        assert_eq!(name, "synor-wallet-test-master-unlock");
    }

    #[test]
    fn test_derived_key() {
        let key1 = DerivedKey::derive("password123", b"salt");
        let key2 = DerivedKey::derive("password123", b"salt");
        let key3 = DerivedKey::derive("different", b"salt");

        // Same password + salt = same key
        assert_eq!(key1.as_bytes(), key2.as_bytes());
        // Different password = different key
        assert_ne!(key1.as_bytes(), key3.as_bytes());
    }
}
