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

//! Mock keyring provider for testing.
//!
//! Note: This file is named wallet.rs for backward compatibility but implements
//! the keyring module types.

use std::{
    collections::HashMap,
    str::FromStr,
    sync::{Arc, RwLock},
};

use async_trait::async_trait;
use ed25519_dalek::SigningKey as Keypair;
use rialo_cdk::{
    error::{Result, RialoError},
    keyring::{Keyring, KeyringProvider},
    rpc::types::Pubkey,
};

pub struct MockKeyringProvider {
    keyrings: Arc<RwLock<HashMap<String, (Keyring, String)>>>,
}

impl MockKeyringProvider {
    #[allow(dead_code)]
    pub fn new() -> Self {
        Self {
            keyrings: Arc::new(RwLock::new(HashMap::new())),
        }
    }
}

#[async_trait]
impl KeyringProvider for MockKeyringProvider {
    async fn create(&self, name: &str, password: &str) -> Result<Keyring> {
        let mut keyrings = self.keyrings.write().unwrap();

        // Use SigningKey::generate for mock implementation
        let keypair = Keypair::generate(&mut rand::rngs::OsRng);
        let keyring = Keyring::new(name.to_string(), keypair, None, None);

        // Store keyring by value, no clone needed
        keyrings.insert(name.to_string(), (keyring, password.to_string()));

        // Recreate keyring for return
        let keypair = Keypair::generate(&mut rand::rngs::OsRng);
        Ok(Keyring::new(name.to_string(), keypair, None, None))
    }

    async fn create_with_mnemonic(&self, name: &str, password: &str) -> Result<Keyring> {
        // For mock implementation, just delegate to create
        self.create(name, password).await
    }

    async fn recover_from_mnemonic(
        &self,
        name: &str,
        _mnemonic: &str,
        password: &str,
    ) -> Result<Keyring> {
        // In mock implementation, ignore mnemonic and just create a new keyring
        self.create(name, password).await
    }

    async fn derive_keyring(
        &self,
        _source_keyring_name: &str,
        new_keyring_name: &str,
        _keypair_index: u32,
        password: &str,
    ) -> Result<Keyring> {
        // For mock implementation, just create a new keyring ignoring derivation parameters
        self.create(new_keyring_name, password).await
    }

    async fn load(&self, name: &str, password: &str) -> Result<Keyring> {
        let keyrings = self.keyrings.read().unwrap();

        match keyrings.get(name) {
            // Return a copy of the keyring with the same keypair from storage
            Some((stored_keyring, stored_pass)) if stored_pass == password => {
                // Clone the keyring to ensure we're returning a keyring with the SAME keypair
                // that was originally stored - not a new random one
                Ok(stored_keyring.clone())
            }
            Some(_) => Err(RialoError::Password("Invalid password".to_string())),
            None => Err(RialoError::Keyring(format!("Keyring not found: {name}"))),
        }
    }

    async fn list(&self) -> Result<Vec<String>> {
        let keyrings = self.keyrings.read().unwrap();
        Ok(keyrings.keys().cloned().collect())
    }

    async fn list_public_keys(&self) -> Result<Vec<(String, Pubkey)>> {
        let keyrings = self.keyrings.read().unwrap();
        // Mock implementation now returns valid base58 encoded public keys
        Ok(keyrings
            .keys()
            .map(|name| {
                (
                    name.clone(),
                    Pubkey::from_str("11111111111111111111111111111111").unwrap(),
                )
            })
            .collect())
    }

    async fn list_keypairs(&self, keyring_name: &str) -> Result<Vec<(u32, Pubkey)>> {
        let keyrings = self.keyrings.read().unwrap();

        if keyrings.contains_key(keyring_name) {
            // Return a valid base58 address for testing
            Ok(vec![(
                0,
                Pubkey::from_str("11111111111111111111111111111111").unwrap(),
            )])
        } else {
            Err(RialoError::Keyring(format!(
                "Keyring not found: {keyring_name}"
            )))
        }
    }

    async fn get_keypair_balance(&self, keyring_name: &str, _keypair_index: u32) -> Result<u64> {
        let keyrings = self.keyrings.read().unwrap();

        if keyrings.contains_key(keyring_name) {
            // Return mock balance for testing
            Ok(1000)
        } else {
            Err(RialoError::Keyring(format!(
                "Keyring not found: {keyring_name}"
            )))
        }
    }

    async fn derive_keypair(
        &self,
        keyring_name: &str,
        index: u32,
        password: &str,
    ) -> Result<(u32, Pubkey)> {
        let keyrings = self.keyrings.read().unwrap();

        if let Some((_, stored_pass)) = keyrings.get(keyring_name) {
            if stored_pass == password {
                // Return a valid base58 address
                Ok((
                    index,
                    Pubkey::from_str("11111111111111111111111111111111").unwrap(),
                ))
            } else {
                Err(RialoError::Password("Invalid password".to_string()))
            }
        } else {
            Err(RialoError::Keyring(format!(
                "Keyring not found: {keyring_name}"
            )))
        }
    }

    async fn get_public_key(&self, keyring_name: &str) -> Result<Pubkey> {
        let keyrings = self.keyrings.read().unwrap();

        if keyrings.contains_key(keyring_name) {
            // Return a valid base58 public key
            Ok(Pubkey::from_str("11111111111111111111111111111111").unwrap())
        } else {
            Err(RialoError::Keyring(format!(
                "Keyring not found: {keyring_name}"
            )))
        }
    }

    async fn get_keypairs_info(&self, keyring_name: &str) -> Result<Vec<(u32, Pubkey)>> {
        // This can be the same implementation as list_keypairs for the mock
        self.list_keypairs(keyring_name).await
    }

    async fn get_keypair_public_key(
        &self,
        keyring_name: &str,
        _keypair_index: u32,
    ) -> Result<Pubkey> {
        let keyrings = self.keyrings.read().unwrap();

        if keyrings.contains_key(keyring_name) {
            // Return a valid base58 public key
            Ok(Pubkey::from_str("11111111111111111111111111111111").unwrap())
        } else {
            Err(RialoError::Keyring(format!(
                "Keyring not found: {keyring_name}"
            )))
        }
    }

    async fn next_keypair_index(&self, keyring_name: &str) -> Result<u32> {
        let keyrings = self.keyrings.read().unwrap();

        if keyrings.contains_key(keyring_name) {
            // Return next mock keypair index for testing
            Ok(1) // Always return 1 for simplicity in mock
        } else {
            Err(RialoError::Keyring(format!(
                "Keyring not found: {keyring_name}"
            )))
        }
    }

    async fn exists(&self, name: &str) -> Result<bool> {
        let keyrings = self.keyrings.read().unwrap();
        Ok(keyrings.contains_key(name))
    }
}

// Backward compatibility alias
#[allow(dead_code)]
#[deprecated(since = "0.2.0", note = "Use MockKeyringProvider instead")]
pub type MockWalletProvider = MockKeyringProvider;
