#![cfg_attr(not(feature = "std"), no_std)]

pub mod weights;

pub use self::currency::CSPR;

/// Monetary constants.
pub mod currency {
    use primitives::Balance;

    /// Constant values used within runtime.
    pub const FTSO: Balance = 1_000_000_000; // 10^9
    pub const STNK: Balance = 1_000 * FTSO; // 10^12
    pub const STRH: Balance = 1_000 * STNK; // 10^15
    pub const CSPR: Balance = 1_000 * STRH; // 10^18

    /// The existential deposit.
    pub const EXISTENTIAL_DEPOSIT: Balance = STNK;

    pub const fn deposit(items: u32, bytes: u32) -> Balance {
        (items as Balance) * 20 * STRH + (bytes as Balance) * 200 * STNK
    }
}

/// Time and blocks.
pub mod time {
    use primitives::{BlockNumber, Moment};
    use runtime_common::prod_or_fast;

    pub const MILLISECS_PER_BLOCK: Moment = 12_000;
    pub const SLOT_DURATION: Moment = MILLISECS_PER_BLOCK;
    pub const EPOCH_DURATION_IN_SLOTS: BlockNumber = prod_or_fast!(4 * HOURS, 1 * MINUTES);

    // These time units are defined in number of blocks.
    pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
    pub const HOURS: BlockNumber = MINUTES * 60;
    pub const DAYS: BlockNumber = HOURS * 24;
    pub const WEEKS: BlockNumber = DAYS * 7;

    // 1 in 4 blocks (on average, not counting collisions) woll be primary babe
    // blocks.The choice of is done on accordance to the slot duration and expected
    // target block time, for safely resisting network delays of maximum two
    // seconds.
    pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4);
}

/// Fee-related.
pub mod fee {
    use frame_support::weights::{
        WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial,
    };
    use primitives::Balance;
    use smallvec::smallvec;
    pub use sp_runtime::Perbill;

    /// The block saturation level. Fees will be updates based on this value.
    pub const TARGET_BLOCK_FULLNESS: Perbill = Perbill::from_percent(25);

    /// Handles converting a weight scalar to a fee value, based on the scale
    /// and granularity of the node's balance type.
    ///
    /// This should typically create a mapping between the following ranges:
    ///     - [0, `MAXIMUM_BLOCK_WEIGHT`]
    ///     - [Balance::min, Balance::max]
    ///
    /// Yet, it can be used for any other sort of change to weight-fee.
    pub struct WeightToFee;
    impl WeightToFeePolynomial for WeightToFee {
        type Balance = Balance;

        fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
            let fee_per_nanosecond: Balance = (10 * super::currency::FTSO) / 1_000;

            smallvec![WeightToFeeCoefficient {
                degree: 1,
                negative: false,
                coeff_frac: Perbill::zero(),
                coeff_integer: fee_per_nanosecond,
            }]
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{
        currency::{CSPR, STRH},
        fee::WeightToFee,
    };
    use crate::weights::ExtrinsicBaseWeight;
    use frame_support::weights::WeightToFee as WeightToFeeT;
    use runtime_common::MAXIMUM_BLOCK_WEIGHT;

    #[test]
    fn full_block_fee_is_correct() {
        let full_block = WeightToFee::weight_to_fee(&MAXIMUM_BLOCK_WEIGHT);
        assert!(full_block >= 30 * CSPR);
        assert!(full_block <= 50 * CSPR);
    }

    #[test]
    fn extrinsic_base_fee_is_correct() {
        let x = WeightToFee::weight_to_fee(&ExtrinsicBaseWeight::get());

        let lower_bound = STRH / 2;
        let upper_bound = 6 * STRH;

        assert!(x >= lower_bound);
        assert!(x <= upper_bound);
    }
}
