// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Core traits and types for keyring management.
//!
//! This module provides the fundamental structures for managing cryptographic
//! signing keys on the Rialo blockchain. It defines the core types that enable
//! secure key management and transaction signing.
//!
//! # Terminology
//!
//! - **Keyring**: A collection of derived keypairs, optionally backed by a mnemonic
//! - **DerivedKeypair**: A single Ed25519 keypair with its derivation metadata
//! - **KeyringProvider**: A trait for keyring storage and lifecycle management
//!
//! # Note
//!
//! These types manage cryptographic keys, NOT assets. On-chain accounts hold tokens;
//! keyrings hold the keys needed to sign transactions that interact with those accounts.

use std::collections::HashMap;

use async_trait::async_trait;
use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};

use crate::{
    error::{Result, RialoError},
    rpc::types::Pubkey,
};

// Type aliases for cleaner code
type Keypair = SigningKey;
type PublicKey = VerifyingKey;

/// A keypair derived from a mnemonic seed using HD derivation.
///
/// This struct represents a single signing keypair along with its derivation
/// metadata. It is NOT an on-chain account—it's the cryptographic material
/// used to sign transactions that interact with on-chain accounts.
///
/// # Fields
///
/// - `index`: The account index used in the BIP44 derivation path
/// - `keypair`: The actual Ed25519 signing key
/// - `derivation_path`: The full derivation path (e.g., "m/44'/756'/0'/0'")
///
/// # Relationship to On-Chain Accounts
///
/// The public key from this keypair corresponds to an on-chain account address.
/// That on-chain account may hold tokens, but this struct only holds the key
/// material needed to authorize transactions for that account.
#[derive(Debug, PartialEq, Eq)]
pub struct DerivedKeypair {
    /// The keypair's index within the keyring
    pub index: u32,
    /// The keypair's public key
    pub pubkey: PublicKey,
    /// The Ed25519 keypair for signing
    keypair: Keypair,
    /// Optional derivation path if created from an HD keyring
    derivation_path: Option<String>,
}

impl Clone for DerivedKeypair {
    fn clone(&self) -> Self {
        // Since SigningKey doesn't implement Clone, we need to deserialize it from bytes
        let keypair_bytes = self.keypair.to_bytes();
        let keypair = Keypair::from_bytes(&keypair_bytes);

        Self {
            index: self.index,
            pubkey: self.pubkey,
            keypair,
            derivation_path: self.derivation_path.clone(),
        }
    }
}

impl DerivedKeypair {
    /// Creates a new derived keypair with the given index, keypair, and optional derivation path.
    ///
    /// # Arguments
    ///
    /// * `index` - The keypair index within the keyring
    /// * `keypair` - The Ed25519 keypair
    /// * `derivation_path` - Optional HD wallet derivation path
    ///
    /// # Returns
    ///
    /// A new `DerivedKeypair` instance
    pub fn new(index: u32, keypair: Keypair, derivation_path: Option<String>) -> Self {
        Self {
            index,
            pubkey: keypair.verifying_key(),
            keypair,
            derivation_path,
        }
    }

    /// Signs a message using this keypair.
    ///
    /// # Arguments
    ///
    /// * `message` - The message bytes to sign
    ///
    /// # Returns
    ///
    /// An Ed25519 signature
    pub fn sign(&self, message: &[u8]) -> Signature {
        self.keypair.sign(message)
    }

    /// Verifies a signature for the specified message.
    pub fn verify_signature(&self, message: &[u8], signature: &Signature) -> bool {
        self.pubkey.verify_strict(message, signature).is_ok()
    }

    /// Returns the public key as a base58-encoded string.
    ///
    /// # Returns
    ///
    /// The public key as a base58-encoded string
    pub fn pubkey_string(&self) -> String {
        bs58::encode(self.pubkey.as_bytes()).into_string()
    }

    /// Returns the derivation path for this keypair, if available.
    ///
    /// # Returns
    ///
    /// The derivation path as a string slice, or None if not available
    pub fn derivation_path(&self) -> Option<&str> {
        self.derivation_path.as_deref()
    }

    /// Returns the keypair bytes.
    ///
    /// # Returns
    ///
    /// The keypair as a byte vector
    pub fn keypair_bytes(&self) -> Vec<u8> {
        self.keypair.as_bytes().to_vec()
    }
}

/// A keyring containing one or more derived keypairs.
///
/// A keyring manages cryptographic keys used for signing blockchain transactions.
/// It maintains multiple derived keypairs and tracks which one is currently active
/// for signing operations.
///
/// # Terminology
///
/// - **Keyring**: A collection of signing keys (this struct)
/// - **DerivedKeypair**: A single keypair derived from the keyring's mnemonic
/// - **Active Keypair**: The default keypair used for signing when no index is specified
///
/// # Note
///
/// A keyring does NOT store assets or balances. Balances exist on-chain and are
/// queried via RPC using the public keys from the keyring's derived keypairs.
#[derive(Debug, PartialEq, Eq)]
pub struct Keyring {
    /// The keyring's name
    pub name: String,
    /// Map of indices to DerivedKeypair instances
    pub keypairs: HashMap<u32, DerivedKeypair>,
    /// The currently active keypair index
    active_keypair_index: u32,
    /// Optional BIP39 mnemonic phrase for key derivation
    mnemonic: Option<String>,
}

impl Clone for Keyring {
    fn clone(&self) -> Self {
        Self {
            name: self.name.clone(),
            keypairs: self.keypairs.clone(),
            active_keypair_index: self.active_keypair_index,
            mnemonic: self.mnemonic.clone(),
        }
    }
}

impl Keyring {
    /// Creates a new keyring with a single keypair.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name
    /// * `keypair` - The Ed25519 keypair for the initial derived keypair
    /// * `mnemonic` - Optional BIP39 mnemonic phrase for key derivation
    /// * `derivation_path` - Optional derivation path for the initial keypair
    ///
    /// # Returns
    ///
    /// A new `Keyring` instance with a single derived keypair
    pub fn new(
        name: String,
        keypair: Keypair,
        mnemonic: Option<String>,
        derivation_path: Option<String>,
    ) -> Self {
        let mut keypairs = HashMap::new();
        let default_keypair = DerivedKeypair::new(0, keypair, derivation_path);
        keypairs.insert(0, default_keypair);

        Self {
            name,
            keypairs,
            active_keypair_index: 0,
            mnemonic,
        }
    }

    /// Creates a new empty keyring for testing.
    pub fn new_empty(name: impl ToString) -> Self {
        let keypair = Keypair::generate(&mut rand::thread_rng());
        Self::new(name.to_string(), keypair, None, None)
    }

    /// Signs a message using the active keypair.
    ///
    /// # Arguments
    ///
    /// * `message` - The message bytes to sign
    ///
    /// # Returns
    ///
    /// An Ed25519 signature
    pub fn sign(&self, message: &[u8]) -> Signature {
        self.active_keypair().sign(message)
    }

    /// Verifies a signature on the specified message.
    pub fn verify_signature(&self, message: &[u8], signature: &Signature) -> bool {
        self.active_keypair().verify_signature(message, signature)
    }

    /// Returns the active keypair's public key as a base58-encoded string.
    ///
    /// # Returns
    ///
    /// The active keypair's public key as a base58-encoded string
    pub fn pubkey_string(&self) -> String {
        self.active_keypair().pubkey_string()
    }

    /// Returns the active keypair's public key.
    ///
    /// # Returns
    ///
    /// The active keypair's Ed25519 public key
    pub fn pubkey(&self) -> Pubkey {
        Pubkey::new_from_array(self.active_keypair().pubkey.to_bytes())
    }

    /// Returns the active keypair's bytes.
    ///
    /// # Returns
    ///
    /// The keypair as a byte vector
    pub fn keypair_bytes(&self) -> Vec<u8> {
        self.active_keypair().keypair_bytes()
    }

    /// Returns the keyring's mnemonic phrase, if available.
    ///
    /// # Returns
    ///
    /// The BIP39 mnemonic phrase as a string slice, or None if not available
    pub fn mnemonic(&self) -> Option<&str> {
        self.mnemonic.as_deref()
    }

    /// Returns a reference to the currently active keypair.
    ///
    /// # Returns
    ///
    /// A reference to the active keypair
    ///
    /// # Panics
    ///
    /// Panics if the active keypair doesn't exist, which should never happen
    /// in normal operation as the active keypair index is managed internally.
    pub fn active_keypair(&self) -> &DerivedKeypair {
        self.keypairs
            .get(&self.active_keypair_index)
            .expect("Active keypair must exist")
    }

    /// Sets the active keypair to the specified index.
    ///
    /// # Arguments
    ///
    /// * `index` - The keypair index to set as active
    ///
    /// # Returns
    ///
    /// `Ok(())` if successful, or an error if the keypair doesn't exist
    pub fn set_active_keypair(&mut self, index: u32) -> Result<()> {
        if self.keypairs.contains_key(&index) {
            self.active_keypair_index = index;
            Ok(())
        } else {
            Err(RialoError::Keyring("Keypair not found".to_string()))
        }
    }

    /// Gets a keypair by its index.
    ///
    /// # Arguments
    ///
    /// * `index` - The keypair index to retrieve
    ///
    /// # Returns
    ///
    /// Some reference to the keypair if found, or None if not found
    pub fn get_keypair(&self, index: u32) -> Option<&DerivedKeypair> {
        self.keypairs.get(&index)
    }

    /// Adds a new keypair to the keyring.
    ///
    /// # Arguments
    ///
    /// * `keypair` - The derived keypair to add
    pub fn add_keypair(&mut self, keypair: DerivedKeypair) {
        self.keypairs.insert(keypair.index, keypair);
    }

    /// Returns a list of all keypair indices in this keyring.
    ///
    /// # Returns
    ///
    /// A vector of keypair indices
    pub fn list_keypairs(&self) -> Vec<u32> {
        self.keypairs.keys().cloned().collect()
    }

    /// Returns all keypairs' public keys as base58-encoded strings.
    ///
    /// # Returns
    ///
    /// A vector of base58-encoded public key strings
    pub fn get_keypairs(&self) -> Vec<String> {
        self.keypairs
            .values()
            .map(|keypair| keypair.pubkey_string())
            .collect()
    }

    /// Returns the derivation path of the active keypair, if available.
    ///
    /// # Returns
    ///
    /// The derivation path as a string slice, or None if not available
    pub fn derivation_path(&self) -> Option<&str> {
        self.active_keypair().derivation_path()
    }

    /// Signs a message using a specific keypair.
    ///
    /// # Arguments
    ///
    /// * `message` - The message bytes to sign
    /// * `keypair_index` - The index of the keypair to use for signing
    ///
    /// # Returns
    ///
    /// The signature if successful, or an error if the keypair doesn't exist
    pub fn sign_with_keypair(&self, message: &[u8], keypair_index: u32) -> Result<Signature> {
        if let Some(keypair) = self.get_keypair(keypair_index) {
            Ok(keypair.sign(message))
        } else {
            Err(RialoError::Keyring(format!(
                "Keypair index {keypair_index} not found"
            )))
        }
    }

    // === Backward compatibility methods (delegate to new names) ===

    /// Returns a reference to the currently active keypair.
    ///
    /// # Deprecated
    ///
    /// Use `active_keypair()` instead.
    #[deprecated(since = "0.2.0", note = "Use active_keypair() instead")]
    pub fn active_account(&self) -> &DerivedKeypair {
        self.active_keypair()
    }

    /// Sets the active keypair to the specified index.
    ///
    /// # Deprecated
    ///
    /// Use `set_active_keypair()` instead.
    #[deprecated(since = "0.2.0", note = "Use set_active_keypair() instead")]
    pub fn set_active_account(&mut self, index: u32) -> Result<()> {
        self.set_active_keypair(index)
    }

    /// Gets a keypair by its index.
    ///
    /// # Deprecated
    ///
    /// Use `get_keypair()` instead.
    #[deprecated(since = "0.2.0", note = "Use get_keypair() instead")]
    pub fn get_account(&self, index: u32) -> Option<&DerivedKeypair> {
        self.get_keypair(index)
    }

    /// Adds a new keypair to the keyring.
    ///
    /// # Deprecated
    ///
    /// Use `add_keypair()` instead.
    #[deprecated(since = "0.2.0", note = "Use add_keypair() instead")]
    pub fn add_account(&mut self, keypair: DerivedKeypair) {
        self.add_keypair(keypair)
    }

    /// Returns a list of all keypair indices.
    ///
    /// # Deprecated
    ///
    /// Use `list_keypairs()` instead.
    #[deprecated(since = "0.2.0", note = "Use list_keypairs() instead")]
    pub fn list_accounts(&self) -> Vec<u32> {
        self.list_keypairs()
    }

    /// Returns all keypairs' public keys.
    ///
    /// # Deprecated
    ///
    /// Use `get_keypairs()` instead.
    #[deprecated(since = "0.2.0", note = "Use get_keypairs() instead")]
    pub fn get_accounts(&self) -> Vec<String> {
        self.get_keypairs()
    }

    /// Signs a message using a specific keypair.
    ///
    /// # Deprecated
    ///
    /// Use `sign_with_keypair()` instead.
    #[deprecated(since = "0.2.0", note = "Use sign_with_keypair() instead")]
    pub fn sign_with_account(&self, message: &[u8], account_index: u32) -> Result<Signature> {
        self.sign_with_keypair(message, account_index)
    }
}

/// Trait defining the interface for keyring storage and management.
///
/// Implementations of this trait handle the creation, loading, and
/// persistence of keyrings. They abstract away the specific storage
/// backend used (file system, in-memory, etc.).
#[async_trait]
pub trait KeyringProvider: Send + Sync {
    /// Creates a new keyring with the given name and password.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name
    /// * `password` - The password to encrypt the keyring
    ///
    /// # Returns
    ///
    /// The newly created keyring if successful
    async fn create(&self, name: &str, password: &str) -> Result<Keyring>;

    #[cfg(feature = "mnemonic")]
    /// Creates a new keyring with a generated mnemonic phrase.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name
    /// * `password` - The password to encrypt the keyring
    ///
    /// # Returns
    ///
    /// The newly created keyring if successful
    async fn create_with_mnemonic(&self, name: &str, password: &str) -> Result<Keyring>;

    #[cfg(feature = "mnemonic")]
    /// Recovers a keyring from a mnemonic phrase.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name
    /// * `mnemonic` - The BIP39 mnemonic phrase
    /// * `password` - The password to encrypt the keyring
    ///
    /// # Returns
    ///
    /// The recovered keyring if successful
    async fn recover_from_mnemonic(
        &self,
        name: &str,
        mnemonic: &str,
        password: &str,
    ) -> Result<Keyring>;

    /// Loads an existing keyring.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name
    /// * `password` - The keyring password
    ///
    /// # Returns
    ///
    /// The loaded keyring if successful
    async fn load(&self, name: &str, password: &str) -> Result<Keyring>;

    /// Lists all available keyring names.
    ///
    /// # Returns
    ///
    /// A vector of keyring names if successful
    async fn list(&self) -> Result<Vec<String>>;

    /// Checks if a keyring with the given name exists.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name to check
    ///
    /// # Returns
    ///
    /// `true` if the keyring exists, `false` otherwise
    async fn exists(&self, name: &str) -> Result<bool>;

    #[cfg(feature = "hd-wallet")]
    /// Derives a new keyring from an existing one at the specified index.
    ///
    /// # Arguments
    ///
    /// * `source_keyring_name` - The name of the source keyring
    /// * `new_keyring_name` - The name for the new keyring
    /// * `keypair_index` - The keypair index to derive from
    /// * `password` - The password for the source keyring
    ///
    /// # Returns
    ///
    /// The newly derived keyring if successful
    async fn derive_keyring(
        &self,
        source_keyring_name: &str,
        new_keyring_name: &str,
        keypair_index: u32,
        password: &str,
    ) -> Result<Keyring>;

    /// Lists all keyring public keys without requiring a password.
    ///
    /// # Returns
    ///
    /// A vector of (keyring name, public key) tuples if successful
    async fn list_public_keys(&self) -> Result<Vec<(String, Pubkey)>>;

    /// Lists all keypairs for a specific keyring without requiring a password.
    ///
    /// # Arguments
    ///
    /// * `keyring_name` - The keyring name
    ///
    /// # Returns
    ///
    /// A vector of (keypair index, public key) tuples if successful
    async fn list_keypairs(&self, keyring_name: &str) -> Result<Vec<(u32, Pubkey)>>;

    /// Gets the balance for a specific keypair without requiring a password.
    ///
    /// # Arguments
    ///
    /// * `keyring_name` - The keyring name
    /// * `keypair_index` - The keypair index
    ///
    /// # Returns
    ///
    /// The balance in the smallest denomination if successful
    async fn get_keypair_balance(&self, keyring_name: &str, keypair_index: u32) -> Result<u64>;

    #[cfg(feature = "hd-wallet")]
    /// Derives a new keypair in an existing keyring.
    ///
    /// # Arguments
    ///
    /// * `keyring_name` - The keyring name
    /// * `keypair_index` - The keypair index to derive
    /// * `password` - The keyring password
    ///
    /// # Returns
    ///
    /// The (keypair index, public key) of the newly derived keypair if successful
    async fn derive_keypair(
        &self,
        keyring_name: &str,
        keypair_index: u32,
        password: &str,
    ) -> Result<(u32, Pubkey)>;

    /// Gets the public key of a keyring's active keypair without requiring a password.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name
    ///
    /// # Returns
    ///
    /// The public key if successful
    async fn get_public_key(&self, name: &str) -> Result<Pubkey>;

    /// Gets information about all keypairs in a keyring without requiring a password.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name
    ///
    /// # Returns
    ///
    /// A vector of (keypair index, public key) tuples if successful
    async fn get_keypairs_info(&self, name: &str) -> Result<Vec<(u32, Pubkey)>>;

    /// Gets the public key of a specific keypair without requiring a password.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name
    /// * `keypair_index` - The keypair index
    ///
    /// # Returns
    ///
    /// The public key if successful
    async fn get_keypair_public_key(&self, name: &str, keypair_index: u32) -> Result<Pubkey>;

    /// Gets the next available keypair index for a keyring.
    ///
    /// # Arguments
    ///
    /// * `name` - The keyring name
    ///
    /// # Returns
    ///
    /// The next available keypair index if successful
    async fn next_keypair_index(&self, name: &str) -> Result<u32>;
}

// === Backward compatibility type aliases ===

/// A wallet containing one or more accounts.
///
/// # Deprecated
///
/// Use `Keyring` instead. This type alias exists for backward compatibility.
#[deprecated(since = "0.2.0", note = "Use Keyring instead")]
pub type Wallet = Keyring;

/// Represents a single account within a wallet.
///
/// # Deprecated
///
/// Use `DerivedKeypair` instead. This type alias exists for backward compatibility.
#[deprecated(since = "0.2.0", note = "Use DerivedKeypair instead")]
pub type Account = DerivedKeypair;

/// Trait defining the interface for wallet storage and management.
///
/// # Deprecated
///
/// Use `KeyringProvider` instead. This type alias exists for backward compatibility.
#[deprecated(since = "0.2.0", note = "Use KeyringProvider instead")]
pub trait WalletProvider: KeyringProvider {}

// Blanket implementation: any KeyringProvider is also a WalletProvider
#[allow(deprecated)]
impl<T: KeyringProvider> WalletProvider for T {}
