//! Test account utilities for contract testing.
//!
//! Provides pre-funded test accounts with keypairs for simulating
//! different users interacting with contracts.

use ed25519_dalek::{Signer, SigningKey, VerifyingKey};
use rand::rngs::OsRng;
use synor_types::{Address, Hash256, Network};

/// A keypair for signing transactions in tests.
#[derive(Clone)]
pub struct TestKeypair {
    /// The signing key (private key).
    signing_key: SigningKey,
    /// The verifying key (public key).
    verifying_key: VerifyingKey,
}

impl TestKeypair {
    /// Generates a new random keypair.
    pub fn generate() -> Self {
        let signing_key = SigningKey::generate(&mut OsRng);
        let verifying_key = signing_key.verifying_key();
        TestKeypair {
            signing_key,
            verifying_key,
        }
    }

    /// Creates a keypair from a seed (deterministic).
    pub fn from_seed(seed: &[u8; 32]) -> Self {
        let signing_key = SigningKey::from_bytes(seed);
        let verifying_key = signing_key.verifying_key();
        TestKeypair {
            signing_key,
            verifying_key,
        }
    }

    /// Creates a keypair from an index (deterministic, useful for tests).
    pub fn from_index(index: u64) -> Self {
        let mut seed = [0u8; 32];
        seed[..8].copy_from_slice(&index.to_le_bytes());
        // Add some entropy to make keys distinct
        let hash: [u8; 32] = blake3::hash(&seed).into();
        Self::from_seed(&hash)
    }

    /// Returns the public key bytes.
    pub fn public_key(&self) -> [u8; 32] {
        self.verifying_key.to_bytes()
    }

    /// Returns the public key as a reference.
    pub fn public_key_ref(&self) -> &[u8; 32] {
        self.verifying_key.as_bytes()
    }

    /// Signs a message.
    pub fn sign(&self, message: &[u8]) -> [u8; 64] {
        self.signing_key.sign(message).to_bytes()
    }

    /// Signs a hash.
    pub fn sign_hash(&self, hash: &Hash256) -> [u8; 64] {
        self.sign(hash.as_bytes())
    }

    /// Returns the address derived from this keypair.
    pub fn address(&self, network: Network) -> Address {
        Address::from_ed25519_pubkey(network, &self.public_key())
    }

    /// Returns the address for devnet (most common in tests).
    pub fn devnet_address(&self) -> Address {
        self.address(Network::Devnet)
    }
}

impl std::fmt::Debug for TestKeypair {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TestKeypair")
            .field("public_key", &hex::encode(self.public_key()))
            .finish()
    }
}

/// A test account with address, balance, and optional keypair.
#[derive(Clone, Debug)]
pub struct TestAccount {
    /// The account's keypair.
    keypair: TestKeypair,
    /// The account's balance (in smallest units).
    balance: u64,
    /// Nonce for transaction ordering.
    nonce: u64,
    /// Optional label for debugging.
    label: Option<String>,
}

impl TestAccount {
    /// Creates a new test account with a random keypair and zero balance.
    pub fn new() -> Self {
        TestAccount {
            keypair: TestKeypair::generate(),
            balance: 0,
            nonce: 0,
            label: None,
        }
    }

    /// Creates a test account with a specific balance.
    pub fn with_balance(balance: u64) -> Self {
        TestAccount {
            keypair: TestKeypair::generate(),
            balance,
            nonce: 0,
            label: None,
        }
    }

    /// Creates a test account from an index (deterministic).
    pub fn from_index(index: u64) -> Self {
        TestAccount {
            keypair: TestKeypair::from_index(index),
            balance: 0,
            nonce: 0,
            label: Some(format!("Account#{}", index)),
        }
    }

    /// Creates a test account from an index with a specific balance.
    pub fn from_index_with_balance(index: u64, balance: u64) -> Self {
        TestAccount {
            keypair: TestKeypair::from_index(index),
            balance,
            nonce: 0,
            label: Some(format!("Account#{}", index)),
        }
    }

    /// Creates a test account from a seed.
    pub fn from_seed(seed: &[u8; 32]) -> Self {
        TestAccount {
            keypair: TestKeypair::from_seed(seed),
            balance: 0,
            nonce: 0,
            label: None,
        }
    }

    /// Sets the account's label.
    pub fn with_label(mut self, label: impl Into<String>) -> Self {
        self.label = Some(label.into());
        self
    }

    /// Sets the account's balance.
    pub fn set_balance(mut self, balance: u64) -> Self {
        self.balance = balance;
        self
    }

    /// Returns the account's address.
    pub fn address(&self) -> Address {
        self.keypair.devnet_address()
    }

    /// Returns the account's address for a specific network.
    pub fn address_for_network(&self, network: Network) -> Address {
        self.keypair.address(network)
    }

    /// Returns the account's balance.
    pub fn balance(&self) -> u64 {
        self.balance
    }

    /// Returns a mutable reference to the balance.
    pub fn balance_mut(&mut self) -> &mut u64 {
        &mut self.balance
    }

    /// Returns the account's nonce.
    pub fn nonce(&self) -> u64 {
        self.nonce
    }

    /// Increments and returns the nonce.
    pub fn next_nonce(&mut self) -> u64 {
        let n = self.nonce;
        self.nonce += 1;
        n
    }

    /// Returns the account's keypair.
    pub fn keypair(&self) -> &TestKeypair {
        &self.keypair
    }

    /// Returns the account's public key.
    pub fn public_key(&self) -> [u8; 32] {
        self.keypair.public_key()
    }

    /// Signs a message with this account's keypair.
    pub fn sign(&self, message: &[u8]) -> [u8; 64] {
        self.keypair.sign(message)
    }

    /// Signs a hash with this account's keypair.
    pub fn sign_hash(&self, hash: &Hash256) -> [u8; 64] {
        self.keypair.sign_hash(hash)
    }

    /// Returns the account's label.
    pub fn label(&self) -> Option<&str> {
        self.label.as_deref()
    }

    /// Adds to the balance, returning the new balance.
    pub fn credit(&mut self, amount: u64) -> u64 {
        self.balance = self.balance.saturating_add(amount);
        self.balance
    }

    /// Subtracts from the balance if sufficient, returning true on success.
    pub fn debit(&mut self, amount: u64) -> bool {
        if self.balance >= amount {
            self.balance -= amount;
            true
        } else {
            false
        }
    }

    /// Checks if the account can afford an amount.
    pub fn can_afford(&self, amount: u64) -> bool {
        self.balance >= amount
    }
}

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

/// Builder for creating multiple test accounts.
#[derive(Debug, Default)]
pub struct TestAccountBuilder {
    accounts: Vec<TestAccount>,
}

impl TestAccountBuilder {
    /// Creates a new account builder.
    pub fn new() -> Self {
        TestAccountBuilder {
            accounts: Vec::new(),
        }
    }

    /// Adds a new account with a specific balance.
    pub fn add_account(mut self, balance: u64) -> Self {
        let account = TestAccount::from_index(self.accounts.len() as u64).set_balance(balance);
        self.accounts.push(account);
        self
    }

    /// Adds multiple accounts with the same balance.
    pub fn add_accounts(mut self, count: usize, balance: u64) -> Self {
        for _ in 0..count {
            let account = TestAccount::from_index(self.accounts.len() as u64).set_balance(balance);
            self.accounts.push(account);
        }
        self
    }

    /// Adds a labeled account.
    pub fn add_labeled_account(mut self, label: &str, balance: u64) -> Self {
        let account = TestAccount::from_index(self.accounts.len() as u64)
            .set_balance(balance)
            .with_label(label);
        self.accounts.push(account);
        self
    }

    /// Returns the built accounts.
    pub fn build(self) -> Vec<TestAccount> {
        self.accounts
    }
}

/// Pre-defined test accounts for common scenarios.
pub mod predefined {
    use super::*;

    /// Alice - commonly used as the main actor in tests.
    pub fn alice() -> TestAccount {
        TestAccount::from_index(0).with_label("Alice")
    }

    /// Bob - commonly used as a secondary actor.
    pub fn bob() -> TestAccount {
        TestAccount::from_index(1).with_label("Bob")
    }

    /// Charlie - third actor for multi-party tests.
    pub fn charlie() -> TestAccount {
        TestAccount::from_index(2).with_label("Charlie")
    }

    /// Dave - fourth actor.
    pub fn dave() -> TestAccount {
        TestAccount::from_index(3).with_label("Dave")
    }

    /// Eve - commonly used as an attacker/malicious actor.
    pub fn eve() -> TestAccount {
        TestAccount::from_index(666).with_label("Eve")
    }

    /// Deployer - account used for contract deployment.
    pub fn deployer() -> TestAccount {
        TestAccount::from_index(100)
            .with_label("Deployer")
            .set_balance(1_000_000_000_000) // Large balance for gas
    }

    /// Treasury - account for protocol treasury.
    pub fn treasury() -> TestAccount {
        TestAccount::from_index(200).with_label("Treasury")
    }

    /// Gets a predefined account with a specific balance.
    pub fn with_balance(who: &str, balance: u64) -> TestAccount {
        match who.to_lowercase().as_str() {
            "alice" => alice().set_balance(balance),
            "bob" => bob().set_balance(balance),
            "charlie" => charlie().set_balance(balance),
            "dave" => dave().set_balance(balance),
            "eve" => eve().set_balance(balance),
            "deployer" => deployer().set_balance(balance),
            "treasury" => treasury().set_balance(balance),
            _ => TestAccount::new().with_label(who).set_balance(balance),
        }
    }
}

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

    #[test]
    fn test_keypair_generation() {
        let kp1 = TestKeypair::generate();
        let kp2 = TestKeypair::generate();

        // Different keypairs should have different public keys
        assert_ne!(kp1.public_key(), kp2.public_key());
    }

    #[test]
    fn test_keypair_from_index() {
        let kp1 = TestKeypair::from_index(0);
        let kp2 = TestKeypair::from_index(0);
        let kp3 = TestKeypair::from_index(1);

        // Same index = same keypair
        assert_eq!(kp1.public_key(), kp2.public_key());

        // Different index = different keypair
        assert_ne!(kp1.public_key(), kp3.public_key());
    }

    #[test]
    fn test_keypair_signing() {
        let kp = TestKeypair::generate();
        let message = b"test message";

        let sig1 = kp.sign(message);
        let sig2 = kp.sign(message);

        // Same message = same signature (Ed25519 is deterministic)
        assert_eq!(sig1, sig2);

        // Different message = different signature
        let sig3 = kp.sign(b"different message");
        assert_ne!(sig1, sig3);
    }

    #[test]
    fn test_account_balance() {
        let mut account = TestAccount::with_balance(1000);

        assert_eq!(account.balance(), 1000);
        assert!(account.can_afford(500));
        assert!(!account.can_afford(1500));

        assert!(account.debit(300));
        assert_eq!(account.balance(), 700);

        assert!(!account.debit(800));
        assert_eq!(account.balance(), 700);

        account.credit(500);
        assert_eq!(account.balance(), 1200);
    }

    #[test]
    fn test_account_nonce() {
        let mut account = TestAccount::new();

        assert_eq!(account.nonce(), 0);
        assert_eq!(account.next_nonce(), 0);
        assert_eq!(account.nonce(), 1);
        assert_eq!(account.next_nonce(), 1);
        assert_eq!(account.nonce(), 2);
    }

    #[test]
    fn test_account_builder() {
        let accounts = TestAccountBuilder::new()
            .add_account(1000)
            .add_account(2000)
            .add_labeled_account("Treasury", 1_000_000)
            .build();

        assert_eq!(accounts.len(), 3);
        assert_eq!(accounts[0].balance(), 1000);
        assert_eq!(accounts[1].balance(), 2000);
        assert_eq!(accounts[2].balance(), 1_000_000);
        assert_eq!(accounts[2].label(), Some("Treasury"));
    }

    #[test]
    fn test_predefined_accounts() {
        let alice = predefined::alice();
        let bob = predefined::bob();

        // Predefined accounts should be deterministic
        let alice2 = predefined::alice();
        assert_eq!(alice.public_key(), alice2.public_key());

        // Different predefined accounts should be different
        assert_ne!(alice.public_key(), bob.public_key());

        assert_eq!(alice.label(), Some("Alice"));
        assert_eq!(bob.label(), Some("Bob"));
    }

    #[test]
    fn test_account_addresses() {
        let account = TestAccount::from_index(0);

        let devnet_addr = account.address();
        let testnet_addr = account.address_for_network(Network::Testnet);
        let mainnet_addr = account.address_for_network(Network::Mainnet);

        // Same account, different networks = different address strings
        assert!(devnet_addr.to_string().starts_with("dsynor1"));
        assert!(testnet_addr.to_string().starts_with("tsynor1"));
        assert!(mainnet_addr.to_string().starts_with("synor1"));

        // But same underlying payload
        assert_eq!(devnet_addr.payload(), testnet_addr.payload());
    }
}
