mod database_params;
mod import_params;
mod keystore_params;
mod message_params;
mod network_params;
mod node_key_params;
mod offchain_worker_params;
mod prometheus_params;
mod pruning_params;
mod runtime_params;
mod shared_params;
mod telemetry_params;
mod transaction_pool_params;
use crate::arg_enums::{CryptoScheme, OutputType};
use clap::Args;
use sp_core::crypto::{Ss58AddressFormat, Ss58AddressFormatRegistry};
use sp_runtime::{
	generic::BlockId,
	traits::{Block as BlockT, NumberFor},
};
use std::{fmt::Debug, str::FromStr};
pub use crate::params::{
	database_params::*, import_params::*, keystore_params::*, message_params::*, network_params::*,
	node_key_params::*, offchain_worker_params::*, prometheus_params::*, pruning_params::*,
	runtime_params::*, shared_params::*, telemetry_params::*, transaction_pool_params::*,
};
pub fn parse_ss58_address_format(x: &str) -> Result<Ss58AddressFormat, String> {
	match Ss58AddressFormatRegistry::try_from(x) {
		Ok(format_registry) => Ok(format_registry.into()),
		Err(_) => Err(format!(
			"Unable to parse variant. Known variants: {:?}",
			Ss58AddressFormat::all_names()
		)),
	}
}
#[derive(Debug, Clone)]
pub struct GenericNumber(String);
impl FromStr for GenericNumber {
	type Err = String;
	fn from_str(block_number: &str) -> Result<Self, Self::Err> {
		if let Some(pos) = block_number.chars().position(|d| !d.is_digit(10)) {
			Err(format!("Expected block number, found illegal digit at position: {}", pos))
		} else {
			Ok(Self(block_number.to_owned()))
		}
	}
}
impl GenericNumber {
	pub fn parse<N>(&self) -> Result<N, String>
	where
		N: FromStr,
		N::Err: std::fmt::Debug,
	{
		FromStr::from_str(&self.0).map_err(|e| format!("Failed to parse block number: {:?}", e))
	}
}
#[derive(Debug, Clone)]
pub struct BlockNumberOrHash(String);
impl FromStr for BlockNumberOrHash {
	type Err = String;
	fn from_str(block_number: &str) -> Result<Self, Self::Err> {
		if let Some(rest) = block_number.strip_prefix("0x") {
			if let Some(pos) = rest.chars().position(|c| !c.is_ascii_hexdigit()) {
				Err(format!(
					"Expected block hash, found illegal hex character at position: {}",
					2 + pos,
				))
			} else {
				Ok(Self(block_number.into()))
			}
		} else {
			GenericNumber::from_str(block_number).map(|v| Self(v.0))
		}
	}
}
impl BlockNumberOrHash {
	pub fn parse<B: BlockT>(&self) -> Result<BlockId<B>, String>
	where
		<B::Hash as FromStr>::Err: std::fmt::Debug,
		NumberFor<B>: FromStr,
		<NumberFor<B> as FromStr>::Err: std::fmt::Debug,
	{
		if self.0.starts_with("0x") {
			Ok(BlockId::Hash(
				FromStr::from_str(&self.0[2..])
					.map_err(|e| format!("Failed to parse block hash: {:?}", e))?,
			))
		} else {
			GenericNumber(self.0.clone()).parse().map(BlockId::Number)
		}
	}
}
#[derive(Debug, Clone, Args)]
pub struct CryptoSchemeFlag {
	#[arg(long, value_name = "SCHEME", value_enum, ignore_case = true, default_value_t = CryptoScheme::Sr25519)]
	pub scheme: CryptoScheme,
}
#[derive(Debug, Clone, Args)]
pub struct OutputTypeFlag {
	#[arg(long, value_name = "FORMAT", value_enum, ignore_case = true, default_value_t = OutputType::Text)]
	pub output_type: OutputType,
}
#[derive(Debug, Clone, Args)]
pub struct NetworkSchemeFlag {
	#[arg(
		short = 'n',
		long,
		value_name = "NETWORK",
		ignore_case = true,
		value_parser = parse_ss58_address_format,
	)]
	pub network: Option<Ss58AddressFormat>,
}
#[cfg(test)]
mod tests {
	use super::*;
	type Header = sp_runtime::generic::Header<u32, sp_runtime::traits::BlakeTwo256>;
	type Block = sp_runtime::generic::Block<Header, sp_runtime::OpaqueExtrinsic>;
	#[test]
	fn parse_block_number() {
		let block_number_or_hash = BlockNumberOrHash::from_str("1234").unwrap();
		let parsed = block_number_or_hash.parse::<Block>().unwrap();
		assert_eq!(BlockId::Number(1234), parsed);
	}
	#[test]
	fn parse_block_hash() {
		let hash = sp_core::H256::default();
		let hash_str = format!("{:?}", hash);
		let block_number_or_hash = BlockNumberOrHash::from_str(&hash_str).unwrap();
		let parsed = block_number_or_hash.parse::<Block>().unwrap();
		assert_eq!(BlockId::Hash(hash), parsed);
	}
	#[test]
	fn parse_block_hash_fails() {
		assert_eq!(
			"Expected block hash, found illegal hex character at position: 2",
			BlockNumberOrHash::from_str("0xHello").unwrap_err(),
		);
	}
	#[test]
	fn parse_block_number_fails() {
		assert_eq!(
			"Expected block number, found illegal digit at position: 3",
			BlockNumberOrHash::from_str("345Hello").unwrap_err(),
		);
	}
}