use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{BasisPoints16, Height, Indexes, PoolSlug, StoredU32, StoredU64};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode, Version};

use crate::{
    blocks, indexes,
    internal::{CachedWindowStarts, PerBlockCumulativeRolling, PercentPerBlock, RatioU64Bp16},
};

#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
    #[traversable(skip)]
    slug: PoolSlug,

    pub blocks_mined: PerBlockCumulativeRolling<StoredU32, StoredU64, M>,
    pub dominance: PercentPerBlock<BasisPoints16, M>,
}

impl Vecs {
    pub(crate) fn forced_import(
        db: &Database,
        slug: PoolSlug,
        version: Version,
        indexes: &indexes::Vecs,
        cached_starts: &CachedWindowStarts,
    ) -> Result<Self> {
        let suffix = |s: &str| format!("{}_{s}", slug);

        let blocks_mined = PerBlockCumulativeRolling::forced_import(
            db,
            &suffix("blocks_mined"),
            version + Version::ONE,
            indexes,
            cached_starts,
        )?;

        let dominance = PercentPerBlock::forced_import(db, &suffix("dominance"), version, indexes)?;

        Ok(Self {
            slug,
            blocks_mined,
            dominance,
        })
    }

    pub(crate) fn compute(
        &mut self,
        starting_indexes: &Indexes,
        pool: &impl ReadableVec<Height, PoolSlug>,
        blocks: &blocks::Vecs,
        exit: &Exit,
    ) -> Result<()> {
        self.blocks_mined
            .compute(starting_indexes.height, exit, |vec| {
                vec.compute_transform(
                    starting_indexes.height,
                    pool,
                    |(h, id, ..)| {
                        (
                            h,
                            if id == self.slug {
                                StoredU32::ONE
                            } else {
                                StoredU32::ZERO
                            },
                        )
                    },
                    exit,
                )?;
                Ok(())
            })?;

        self.dominance
            .compute_binary::<StoredU64, StoredU64, RatioU64Bp16>(
                starting_indexes.height,
                &self.blocks_mined.cumulative.height,
                &blocks.count.total.cumulative.height,
                exit,
            )?;

        Ok(())
    }
}
