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

//! Base functionality for keyring provider implementations.

use async_trait::async_trait;

use crate::{
    error::{Result, RialoError},
    keyring::traits::{Keyring, KeyringProvider},
};

/// `BaseKeyringProvider` defines common utility methods for keyring provider implementations.
///
/// This trait extends the core `KeyringProvider` trait by adding reusable methods
/// for common operations. Implementations of `KeyringProvider` should also implement
/// this trait to gain access to these helper methods.
///
/// # Example
///
/// ```rust,no_run
/// use rialo_cdk::keyring::provider_base::BaseKeyringProvider;
/// use rialo_cdk::keyring::traits::KeyringProvider;
/// use rialo_cdk::Result;
///
/// async fn use_keyring_provider<T: BaseKeyringProvider>(provider: &T) -> Result<()> {
///     // Check if keyring exists before creating
///     provider.check_keyring_exists("my_keyring").await?;
///     
///     // Get public key without loading full keyring
///     let pubkey = provider.get_keyring_public_key("my_keyring").await?;
///     
///     Ok(())
/// }
/// ```
#[async_trait]
pub trait BaseKeyringProvider: KeyringProvider {
    /// Checks if a keyring exists and returns a descriptive error if not.
    ///
    /// # Arguments
    ///
    /// * `name` - The name of the keyring to check
    ///
    /// # Returns
    ///
    /// `Ok(())` if the keyring exists, or an error if it doesn't
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # use rialo_cdk::keyring::provider_base::BaseKeyringProvider;
    /// # use rialo_cdk::Result;
    /// async fn create_keyring<T: BaseKeyringProvider>(provider: &T, name: &str) -> Result<()> {
    ///     // Will return an error if keyring already exists
    ///     provider.check_keyring_exists(name).await?;
    ///     // ... proceed with creation
    ///     Ok(())
    /// }
    /// ```
    async fn check_keyring_exists(&self, name: &str) -> Result<()> {
        if !self.exists(name).await? {
            return Err(RialoError::Keyring(format!("Keyring not found: {name}")));
        }
        Ok(())
    }

    /// Gets the public key of a keyring's primary keypair without loading the full keyring.
    ///
    /// This is useful when you only need the public key and don't want to decrypt
    /// the private key material.
    ///
    /// # Arguments
    ///
    /// * `name` - The name of the keyring
    ///
    /// # Returns
    ///
    /// The public key as a base58-encoded string
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # use rialo_cdk::keyring::provider_base::BaseKeyringProvider;
    /// # use rialo_cdk::Result;
    /// async fn display_keyring_info<T: BaseKeyringProvider>(provider: &T, name: &str) -> Result<()> {
    ///     let pubkey = provider.get_keyring_public_key(name).await?;
    ///     println!("Keyring {} has public key: {}", name, pubkey);
    ///     Ok(())
    /// }
    /// ```
    async fn get_keyring_public_key(&self, name: &str) -> Result<String> {
        let pubkey = self.get_public_key(name).await?;
        Ok(pubkey.to_string())
    }

    /// Validates that a source keyring has a mnemonic before attempting derivation operations.
    ///
    /// # Arguments
    ///
    /// * `source_keyring_name` - The name of the source keyring
    /// * `new_keyring_name` - The name for the new keyring to be derived
    ///
    /// # Returns
    ///
    /// `Ok(())` if validation passes, or an error if:
    /// - The source keyring doesn't exist
    /// - The new keyring name already exists
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # use rialo_cdk::keyring::provider_base::BaseKeyringProvider;
    /// # use rialo_cdk::Result;
    /// async fn clone_keyring<T: BaseKeyringProvider>(
    ///     provider: &T,
    ///     source: &str,
    ///     target: &str,
    /// ) -> Result<()> {
    ///     provider.validate_mnemonic_operation(source, target).await?;
    ///     // ... proceed with derivation
    ///     Ok(())
    /// }
    /// ```
    async fn validate_mnemonic_operation(
        &self,
        source_keyring_name: &str,
        new_keyring_name: &str,
    ) -> Result<()> {
        // Check source keyring exists
        if !self.exists(source_keyring_name).await? {
            return Err(RialoError::Keyring(format!(
                "Source keyring '{source_keyring_name}' not found"
            )));
        }

        // Check target keyring doesn't exist
        if self.exists(new_keyring_name).await? {
            return Err(RialoError::Keyring(format!(
                "Keyring '{new_keyring_name}' already exists"
            )));
        }

        Ok(())
    }

    /// Loads and validates a keyring, ensuring it has the required mnemonic for derivation.
    ///
    /// # Arguments
    ///
    /// * `name` - The name of the keyring
    /// * `password` - The password to decrypt the keyring
    ///
    /// # Returns
    ///
    /// A tuple of (Keyring, mnemonic) if successful
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - The keyring doesn't exist
    /// - The password is incorrect
    /// - The keyring doesn't have a mnemonic
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # use rialo_cdk::keyring::provider_base::BaseKeyringProvider;
    /// # use rialo_cdk::Result;
    /// async fn sign_transaction<T: BaseKeyringProvider>(
    ///     provider: &T,
    ///     name: &str,
    ///     password: &str,
    /// ) -> Result<()> {
    ///     let (keyring, _mnemonic) = provider.load_keyring_with_validation(name, password).await?;
    ///     // ... use keyring for signing
    ///     Ok(())
    /// }
    /// ```
    async fn load_keyring_with_validation(
        &self,
        name: &str,
        password: &str,
    ) -> Result<(Keyring, String)> {
        let keyring = self.load(name, password).await?;

        let mnemonic = keyring
            .mnemonic()
            .ok_or_else(|| {
                RialoError::Keyring("Keyring does not have a mnemonic phrase".to_string())
            })?
            .to_string();

        Ok((keyring, mnemonic))
    }
}

// === Backward compatibility ===

/// `BaseWalletProvider` defines common utility methods for wallet provider implementations.
///
/// # Deprecated
///
/// Use `BaseKeyringProvider` instead. This type alias exists for backward compatibility.
#[deprecated(since = "0.2.0", note = "Use BaseKeyringProvider instead")]
pub trait BaseWalletProvider: BaseKeyringProvider {}

#[allow(deprecated)]
impl<T: BaseKeyringProvider> BaseWalletProvider for T {}
