mod cache;
mod commands;
mod conf;
mod data;
mod indexer;
mod keys;
mod runtime_config;
mod utils;

use anyhow::anyhow;
use clap::Parser;
use codec::Encode;
use data::*;
use keys::*;
use runtime_config::*;
use serde::Deserialize;
use sp_core::{sr25519::Pair, Pair as _};
use subxt::blocks::ExtrinsicEvents;
use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner, TxStatus};
use utils::*;

/// define command line arguments
#[derive(Clone, clap::Parser, Debug, Default)]
#[clap(author, version, about, long_about = None)]
pub struct Args {
	/// Subcommands
	#[clap(subcommand)]
	pub subcommand: Subcommand,
	/// Overwrite indexer endpoint
	#[clap(short, long)]
	indexer: Option<String>,
	/// Do not use indexer
	#[clap(long)]
	no_indexer: bool,
	/// Secret key or BIP39 mnemonic
	/// (eventually followed by derivation path)
	#[clap(short, long)]
	secret: Option<String>,
	/// Secret key format (seed, substrate)
	#[clap(short = 'S', long, default_value = SecretFormat::Substrate)]
	secret_format: SecretFormat,
	/// Address
	#[clap(short, long)]
	address: Option<String>,
	/// Overwrite duniter websocket RPC endpoint
	#[clap(short, long)]
	url: Option<String>,
	/// Chose target network
	#[clap(short, long)]
	network: Option<String>,
}

/// define subcommands
#[derive(Clone, Debug, clap::Subcommand, Default)]
pub enum Subcommand {
	#[clap(hide = true)]
	Repart {
		// Number of transactions per block to target
		target: u32,
		#[clap(short = 'o', long = "old-repart")]
		// Old/actual repartition
		actual_repart: Option<u32>,
	},
	#[clap(hide = true)]
	SpamRoll { actual_repart: usize },
	/// Get information about runtime
	RuntimeInfo,
	#[default]
	/// Check current block
	CurrentBlock,
	/// Account subcommands
	#[clap(subcommand)]
	Account(commands::account::Subcommand),
	/// Identity subcommands
	#[clap(subcommand)]
	Identity(commands::identity::Subcommand),
	/// Smith subcommands
	#[clap(subcommand)]
	Smith(commands::smith::Subcommand),
	/// Tech subcommands
	#[clap(subcommand)]
	Tech(commands::collective::Subcommand),
	/// Universal Dividend subcommands
	#[clap(subcommand)]
	Ud(commands::ud::Subcommand),
	/// Oneshot account subcommands
	#[clap(subcommand)]
	Oneshot(commands::oneshot::Subcommand),
	/// Indexer subcommands
	#[clap(subcommand)]
	Indexer(indexer::Subcommand),
	/// Config subcommands
	#[clap(subcommand)]
	Config(conf::Subcommand),
}

/// maint function
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), GcliError> {
	// init logger
	env_logger::init();

	// parse argument and initialize data
	let args = Args::parse();
	let mut data = Data::new(args.clone());

	// match subcommands
	match args.subcommand {
		Subcommand::Repart {
			target,
			actual_repart,
		} => {
			data = data.build_client().await?;
			commands::net_test::repart(
				get_keys(
					args.secret_format,
					&args.address,
					&args.secret,
					NeededKeys::Secret,
				)?
				.1
				.unwrap(),
				data.client(),
				target,
				actual_repart,
			)
			.await?
		}
		Subcommand::SpamRoll { actual_repart } => {
			data = data.build_client().await?;
			commands::net_test::spam_roll(
				get_keys(
					args.secret_format,
					&args.address,
					&args.secret,
					NeededKeys::Secret,
				)?
				.1
				.unwrap(),
				data.client(),
				actual_repart,
			)
			.await?
		}
		Subcommand::RuntimeInfo => {
			data = data.build_client().await?.fetch_system_properties().await?;
			commands::runtime::runtime_info(data).await;
		}
		Subcommand::CurrentBlock => {
			data = data.build_client().await?;
			println!(
				"current block on {}: {}",
				data.cfg.duniter_endpoint,
				data.client()
					.storage()
					.fetch(&runtime::storage().system().number(), None)
					.await?
					.unwrap()
			);
		}
		Subcommand::Account(subcommand) => {
			commands::account::handle_command(data, subcommand).await?
		}
		Subcommand::Identity(subcommand) => {
			commands::identity::handle_command(data, subcommand).await?
		}
		Subcommand::Smith(subcommand) => commands::smith::handle_command(data, subcommand).await?,
		Subcommand::Tech(subcommand) => {
			commands::collective::handle_command(data, subcommand).await?
		}
		Subcommand::Ud(subcommand) => commands::ud::handle_command(data, subcommand).await?,
		Subcommand::Oneshot(subcommand) => {
			commands::oneshot::handle_command(data, subcommand).await?
		}
		Subcommand::Indexer(subcommand) => indexer::handle_command(data, subcommand).await?,
		Subcommand::Config(subcommand) => conf::handle_command(data, subcommand)?,
	}

	Ok(())
}