#![deny(missing_docs)]
use std::{future::Future, ops::Range, time::Duration};
use essential_types::{
    contract::{Contract, SignedContract},
    predicate::Predicate,
    solution::Solution,
    Block, ContentAddress, Hash, Key, PredicateAddress, Word,
};
use failed_solution::{FailedSolution, SolutionFailReason, SolutionOutcomes};
pub mod failed_solution;
pub mod streams;
pub struct CommitData<'a> {
    pub failed: &'a [(Hash, SolutionFailReason)],
    pub solved: &'a [Hash],
    pub state_updates: Box<dyn Iterator<Item = (ContentAddress, Key, Vec<Word>)> + 'a>,
}
pub trait Storage: StateStorage {
    fn insert_contract(
        &self,
        predicate: SignedContract,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;
    fn insert_solution_into_pool(
        &self,
        solution: Solution,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;
    fn move_solutions_to_solved(
        &self,
        solutions: &[Hash],
    ) -> impl Future<Output = anyhow::Result<()>> + Send;
    fn move_solutions_to_failed(
        &self,
        solutions: &[(Hash, SolutionFailReason)],
    ) -> impl std::future::Future<Output = anyhow::Result<()>> + Send;
    fn get_predicate(
        &self,
        address: &PredicateAddress,
    ) -> impl Future<Output = anyhow::Result<Option<Predicate>>> + Send;
    fn get_contract(
        &self,
        address: &ContentAddress,
    ) -> impl Future<Output = anyhow::Result<Option<SignedContract>>> + Send;
    fn list_contracts(
        &self,
        time_range: Option<Range<Duration>>,
        page: Option<usize>,
    ) -> impl Future<Output = anyhow::Result<Vec<Contract>>> + Send;
    fn subscribe_contracts(
        self,
        start_time: Option<Duration>,
        start_page: Option<usize>,
    ) -> impl futures::Stream<Item = anyhow::Result<Contract>> + Send + 'static;
    fn list_solutions_pool(
        &self,
        page: Option<usize>,
    ) -> impl Future<Output = anyhow::Result<Vec<Solution>>> + Send;
    fn list_failed_solutions_pool(
        &self,
        page: Option<usize>,
    ) -> impl std::future::Future<Output = anyhow::Result<Vec<FailedSolution>>> + Send;
    fn list_blocks(
        &self,
        time_range: Option<Range<Duration>>,
        block_number: Option<u64>,
        page: Option<usize>,
    ) -> impl Future<Output = anyhow::Result<Vec<Block>>> + Send;
    fn subscribe_blocks(
        self,
        start_time: Option<Duration>,
        block_number: Option<u64>,
        start_page: Option<usize>,
    ) -> impl futures::Stream<Item = anyhow::Result<Block>> + Send + 'static;
    fn get_solution(
        &self,
        solution_hash: Hash,
    ) -> impl std::future::Future<Output = anyhow::Result<Option<SolutionOutcomes>>> + Send;
    fn prune_failed_solutions(
        &self,
        older_than: Duration,
    ) -> impl std::future::Future<Output = anyhow::Result<()>> + Send;
    fn commit_block(
        &self,
        data: CommitData,
    ) -> impl std::future::Future<Output = anyhow::Result<()>> + Send;
}
pub trait StateStorage: QueryState {
    fn update_state(
        &self,
        address: &ContentAddress,
        key: &Key,
        value: Vec<Word>,
    ) -> impl std::future::Future<Output = anyhow::Result<Vec<Word>>> + Send;
    fn update_state_batch<U>(
        &self,
        updates: U,
    ) -> impl std::future::Future<Output = anyhow::Result<Vec<Vec<Word>>>> + Send
    where
        U: IntoIterator<Item = (ContentAddress, Key, Vec<Word>)> + Send;
}
pub trait QueryState {
    fn query_state(
        &self,
        address: &ContentAddress,
        key: &Key,
    ) -> impl std::future::Future<Output = anyhow::Result<Vec<Word>>> + Send;
}
pub async fn key_range<S, E>(
    storage: &S,
    contract_addr: ContentAddress,
    mut key: Key,
    num_words: usize,
) -> Result<Vec<Vec<Word>>, E>
where
    S: QueryState + Send,
    E: From<anyhow::Error>,
{
    let mut words = vec![];
    for _ in 0..num_words {
        let slot = storage.query_state(&contract_addr, &key).await?;
        words.push(slot);
        key = next_key(key).ok_or_else(|| anyhow::anyhow!("Failed to find next key"))?
    }
    Ok(words)
}
pub fn next_key(mut key: Key) -> Option<Key> {
    for w in key.iter_mut().rev() {
        match *w {
            Word::MAX => *w = Word::MIN,
            _ => {
                *w += 1;
                return Some(key);
            }
        }
    }
    None
}