//! Mock types for use in examples.
//!
//! These represent APIs from crates that themselves depend on this crate, and
//! which are useful for illustrating the examples for APIs in this crate.
//!
//! Directly depending on these crates though would cause problematic circular
//! dependencies, so instead they are mocked out here in a way that allows
//! examples to appear to use crates that this crate must not depend on.
//!
//! Each mod here has the name of a crate, so that examples can be structured to
//! appear to import from that crate.

#![doc(hidden)]
#![allow(clippy::new_without_default)]

pub mod solana_rpc_client {
    pub mod rpc_client {
        use std::{cell::RefCell, collections::HashMap, rc::Rc};

        use super::super::rialo_s_sdk::{
            account::Account, pubkey::Pubkey, signature::Signature, transaction::Transaction,
        };

        type ClientResult<T> = std::result::Result<T, ClientError>;

        #[derive(thiserror::Error, Debug)]
        #[error("mock-error")]
        pub struct ClientError;

        #[derive(Default)]
        pub struct RpcClient {
            get_account_responses: Rc<RefCell<HashMap<Pubkey, Account>>>,
        }

        impl RpcClient {
            pub fn new(_url: String) -> Self {
                RpcClient::default()
            }

            pub fn send_and_confirm_transaction(
                &self,
                _transaction: &Transaction,
            ) -> ClientResult<Signature> {
                Ok(Signature)
            }

            pub fn get_minimum_balance_for_rent_exemption(
                &self,
                _data_len: usize,
            ) -> ClientResult<u64> {
                Ok(0)
            }

            pub fn get_account(&self, pubkey: &Pubkey) -> ClientResult<Account> {
                Ok(self
                    .get_account_responses
                    .borrow()
                    .get(pubkey)
                    .cloned()
                    .unwrap())
            }

            pub fn set_get_account_response(&self, pubkey: Pubkey, account: Account) {
                self.get_account_responses
                    .borrow_mut()
                    .insert(pubkey, account);
            }

            pub fn get_balance(&self, _pubkey: &Pubkey) -> ClientResult<u64> {
                Ok(0)
            }
        }
    }
}

pub mod solana_rpc_client_nonce_utils {
    use rialo_s_nonce::{
        state::{Data, DurableNonce},
        versions::Versions,
    };

    use super::rialo_s_sdk::{account::ReadableAccount, account_utils::StateMut, pubkey::Pubkey};

    #[derive(thiserror::Error, Debug)]
    #[error("mock-error")]
    pub struct Error;

    pub fn data_from_account<T: ReadableAccount + StateMut<Versions>>(
        _account: &T,
    ) -> Result<Data, Error> {
        Ok(Data::new(
            Pubkey::new_unique(),
            DurableNonce::default(),
            5000,
        ))
    }
}

pub mod rialo_s_account {
    use rialo_s_clock::Epoch;
    use rialo_s_pubkey::Pubkey;
    #[derive(Clone)]
    pub struct Account {
        pub kelvins: u64,
        pub data: Vec<u8>,
        pub owner: Pubkey,
        pub executable: bool,
        pub rent_epoch: Epoch,
    }

    pub trait ReadableAccount: Sized {
        fn data(&self) -> &[u8];
    }

    impl ReadableAccount for Account {
        fn data(&self) -> &[u8] {
            &self.data
        }
    }

    pub mod state_traits {
        use super::Account;

        pub trait StateMut<T> {}

        impl<T> StateMut<T> for Account {}
    }
}

pub mod rialo_s_signature {
    #[derive(Default, Debug)]
    pub struct Signature;
}

pub mod rialo_s_signer {
    use rialo_s_pubkey::Pubkey;
    use thiserror::Error;

    #[derive(Error, Debug)]
    #[error("mock-error")]
    pub struct SignerError;
    pub trait Signer {
        fn pubkey(&self) -> Pubkey;
    }

    pub mod signers {
        use super::Signer;

        pub trait Signers {}

        impl<T: Signer> Signers for [&T] {}
        impl<T: Signer> Signers for [&T; 1] {}
        impl<T: Signer> Signers for [&T; 2] {}
    }
}

pub mod rialo_s_keypair {
    use rialo_s_pubkey::Pubkey;

    use crate::rialo_s_signer::Signer;
    pub struct Keypair;

    impl Keypair {
        pub fn new() -> Keypair {
            Keypair
        }
    }

    impl Signer for Keypair {
        fn pubkey(&self) -> Pubkey {
            Pubkey::default()
        }
    }
}

pub mod rialo_s_transaction {
    use rialo_s_instruction::Instruction;
    use rialo_s_message::{ConfigHashPrefix, Message};
    use rialo_s_pubkey::Pubkey;
    use serde::Serialize;

    use crate::rialo_s_signer::{signers::Signers, SignerError};

    pub mod versioned {
        use rialo_s_message::VersionedMessage;

        use crate::{
            rialo_s_signature::Signature,
            rialo_s_signer::{signers::Signers, SignerError},
        };
        pub struct VersionedTransaction {
            pub signatures: Vec<Signature>,
            pub message: VersionedMessage,
        }

        impl VersionedTransaction {
            pub fn try_new<T: Signers + ?Sized>(
                message: VersionedMessage,
                _keypairs: &T,
            ) -> std::result::Result<Self, SignerError> {
                Ok(VersionedTransaction {
                    signatures: vec![],
                    message,
                })
            }
        }
    }

    #[derive(Serialize)]
    pub struct Transaction {
        pub message: Message,
    }

    impl Transaction {
        pub fn new<T: Signers + ?Sized>(
            _from_keypairs: &T,
            _message: Message,
            _valid_from: i64,
        ) -> Transaction {
            Transaction {
                message: Message::new(&[], None, ConfigHashPrefix::new(1), false),
            }
        }

        pub fn new_unsigned(_message: Message) -> Self {
            Transaction {
                message: Message::new(&[], None, ConfigHashPrefix::new(1), false),
            }
        }

        pub fn new_with_payer(
            _instructions: &[Instruction],
            _payer: Option<&Pubkey>,
            _config_hash_prefix: ConfigHashPrefix,
        ) -> Self {
            Transaction {
                message: Message::new(&[], None, ConfigHashPrefix::new(1), false),
            }
        }

        pub fn new_signed_with_payer<T: Signers + ?Sized>(
            instructions: &[Instruction],
            payer: Option<&Pubkey>,
            signing_keypairs: &T,
            valid_from: i64,
            config_hash_prefix: ConfigHashPrefix,
            occ: bool,
        ) -> Self {
            let message = Message::new(instructions, payer, config_hash_prefix, occ);
            Self::new(signing_keypairs, message, valid_from)
        }

        pub fn sign<T: Signers + ?Sized>(&mut self, _keypairs: &T, _valid_from: i64) {}

        pub fn try_sign<T: Signers + ?Sized>(
            &mut self,
            _keypairs: &T,
            _valid_from: i64,
        ) -> Result<(), SignerError> {
            Ok(())
        }
    }
}

/// Re-exports and mocks of solana-program modules that mirror those from
/// solana-program.
///
/// This lets examples in solana-program appear to be written as client
/// programs.
pub mod rialo_s_sdk {
    pub use rialo_hash as hash;
    pub use rialo_s_clock::Clock;
    pub use rialo_s_instruction as instruction;
    pub use rialo_s_keccak_hasher as keccak;
    pub use rialo_s_message as message;
    pub use rialo_s_nonce as nonce;
    pub use rialo_s_pubkey::{self as pubkey, Pubkey};
    pub use rialo_s_sdk_ids::{
        system_program,
        sysvar::{self, clock},
    };
    pub use rialo_s_system_interface::instruction as system_instruction;

    pub use crate::{
        rialo_s_account::{self as account, state_traits as account_utils},
        rialo_s_signer::{self as signer, signers},
    };

    pub mod signature {
        pub use crate::{
            rialo_s_keypair::Keypair, rialo_s_signature::Signature, rialo_s_signer::Signer,
        };
    }

    pub mod transaction {
        pub use crate::rialo_s_transaction::{versioned::VersionedTransaction, Transaction};
    }
}
