use crate::mempool::MEMPOOL_HEIGHT;
use crate::query::queryutil::height_by_txid;
use crate::{app::App, chaindef::BlockHeader};
use anyhow::{Context, Result};
use bitcoincash::hash_types::Txid;
use std::sync::Arc;

pub struct HeaderQuery {
    app: Arc<App>,
}

impl HeaderQuery {
    pub fn new(app: Arc<App>) -> HeaderQuery {
        HeaderQuery { app }
    }

    /// Get header for the block that given transaction was confirmed in.
    /// Height is optional, but makes Lookup faster.
    pub async fn get_by_txid(
        &self,
        txid: &Txid,
        blockheight: Option<u32>,
    ) -> Result<Option<BlockHeader>> {
        // Lookup in confirmed transactions' index
        let height = match blockheight {
            Some(height) => {
                if height == MEMPOOL_HEIGHT {
                    return Ok(None);
                }
                height
            }
            None => self
                .get_confirmed_height_for_tx(txid)
                .await
                .context("could not get height for txid")?,
        };

        let header = self
            .app
            .index()
            .chain()
            .get_block_header(height as usize)
            .await
            .context(format!("missing header at height {}", height))?;
        Ok(Some(header))
    }

    pub async fn best(&self) -> BlockHeader {
        self.app.index().chain().tip().await
    }

    pub async fn at_height(&self, height: usize) -> Option<BlockHeader> {
        self.app.index().chain().get_block_header(height).await
    }

    /// Get the height of block where a transaction was confirmed, or None if it's
    /// not confirmed.
    pub async fn get_confirmed_height_for_tx(&self, txid: &Txid) -> Option<u32> {
        height_by_txid(self.app.index().read_store(), txid).await
    }

    #[cfg(nexa)]
    pub async fn get_height(&self, header: &BlockHeader) -> Option<u64> {
        Some(header.height())
    }

    #[cfg(bch)]
    pub async fn get_height(&self, header: &BlockHeader) -> Option<u64> {
        self.app
            .index()
            .chain()
            .get_block_height(&header.block_hash())
            .await
    }
}
