//! Lazy value wrapper for point-in-time value sources.

use brk_traversable::Traversable;
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
use derive_more::{Deref, DerefMut};
use vecdb::UnaryTransform;

use crate::internal::{
    Identity, LazyValue, LazyValueDerivedResolutions, SatsToBitcoin, SpotValuePerBlock,
};

/// Lazy value wrapper with height + all derived last transforms.
#[derive(Clone, Deref, DerefMut, Traversable)]
#[traversable(merge)]
pub struct LazyValuePerBlock {
    #[traversable(flatten)]
    pub height: LazyValue<Height>,
    #[deref]
    #[deref_mut]
    #[traversable(flatten)]
    pub resolutions: Box<LazyValueDerivedResolutions>,
}

impl LazyValuePerBlock {
    pub(crate) fn spot_identity(name: &str, source: &SpotValuePerBlock, version: Version) -> Self {
        Self::from_spot_block_source::<
            Identity<Sats>,
            SatsToBitcoin,
            Identity<Cents>,
            Identity<Dollars>,
        >(name, source, version)
    }

    pub(crate) fn from_spot_block_source<
        SatsTransform,
        BitcoinTransform,
        CentsTransform,
        DollarsTransform,
    >(
        name: &str,
        source: &SpotValuePerBlock,
        version: Version,
    ) -> Self
    where
        SatsTransform: UnaryTransform<Sats, Sats>,
        BitcoinTransform: UnaryTransform<Sats, Bitcoin>,
        CentsTransform: UnaryTransform<Cents, Cents>,
        DollarsTransform: UnaryTransform<Dollars, Dollars>,
    {
        let height = LazyValue::from_spot_block_source::<
            SatsTransform,
            BitcoinTransform,
            CentsTransform,
            DollarsTransform,
        >(name, source, version);

        let resolutions = LazyValueDerivedResolutions::from_spot_block_source::<
            SatsTransform,
            BitcoinTransform,
            CentsTransform,
            DollarsTransform,
        >(name, source, version);

        Self {
            height,
            resolutions: Box::new(resolutions),
        }
    }
}
