//! Runtime API definition for amm module.

#![cfg_attr(not(feature = "std"), no_std)]
// The `too_many_arguments` warning originates from `decl_runtime_apis` macro.
#![allow(clippy::too_many_arguments)]
#![allow(clippy::unnecessary_mut_passed)]

use codec::{Codec, Decode, Encode};
#[cfg(feature = "std")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sp_runtime::traits::{MaybeDisplay, MaybeFromStr};
use sp_std::prelude::*;

#[derive(Eq, PartialEq, Encode, Decode, Default)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct BalanceInfo<AssetId, Balance> {
	#[cfg_attr(feature = "std", serde(bound(serialize = "Balance: std::fmt::Display")))]
	#[cfg_attr(feature = "std", serde(serialize_with = "serialize_as_string"))]
	#[cfg_attr(feature = "std", serde(bound(deserialize = "Balance: std::str::FromStr")))]
	#[cfg_attr(feature = "std", serde(deserialize_with = "deserialize_from_string"))]
	pub amount: Balance,

	pub asset: Option<AssetId>,
}

#[cfg(feature = "std")]
fn serialize_as_string<S: Serializer, T: std::fmt::Display>(t: &T, serializer: S) -> Result<S::Ok, S::Error> {
	serializer.serialize_str(&t.to_string())
}

#[cfg(feature = "std")]
fn deserialize_from_string<'de, D: Deserializer<'de>, T: std::str::FromStr>(deserializer: D) -> Result<T, D::Error> {
	let s = String::deserialize(deserializer)?;
	s.parse::<T>()
		.map_err(|_| serde::de::Error::custom("Parse from string failed"))
}

sp_api::decl_runtime_apis! {
	pub trait AMMApi<AccountId, AssetId, Balance> where
		AccountId: Codec,
		AssetId: Codec,
		Balance: Codec + MaybeDisplay + MaybeFromStr,
	{
		fn get_spot_price(
			asset_a: AssetId,
			asset_b: AssetId,
			amount: Balance,
		) -> BalanceInfo<AssetId, Balance>;

		fn get_buy_price(
			asset_a: AssetId,
			asset_b: AssetId,
			amount: Balance,
		) -> BalanceInfo<AssetId, Balance>;

		fn get_sell_price(
			asset_a: AssetId,
			asset_b: AssetId,
			amount: Balance,
		) -> BalanceInfo<AssetId, Balance>;

		fn get_pool_balances(
			pool_address: AccountId,
		) -> Vec<BalanceInfo<AssetId, Balance>>;
	}
}
