use brk_error::Result;
use brk_types::{Dollars, ExchangeRates, HistoricalPrice, HistoricalPriceEntry, Hour4, Timestamp};
use vecdb::ReadableVec;

use crate::Query;

impl Query {
    pub fn live_price(&self) -> Result<Dollars> {
        let mut oracle = self.computer().prices.live_oracle(self.indexer())?;

        if let Some(mempool) = self.mempool() {
            let txs = mempool.get_txs();
            oracle.process_outputs(
                txs.values()
                    .flat_map(|tx| &tx.tx().output)
                    .map(|txout| (txout.value, txout.type_())),
            );
        }

        Ok(oracle.price_dollars())
    }

    pub fn historical_price(&self, timestamp: Option<Timestamp>) -> Result<HistoricalPrice> {
        let prices = match timestamp {
            Some(ts) => self.price_at(ts)?,
            None => self.all_prices()?,
        };
        Ok(HistoricalPrice {
            prices,
            exchange_rates: ExchangeRates {},
        })
    }

    fn price_at(&self, target: Timestamp) -> Result<Vec<HistoricalPriceEntry>> {
        let h4 = Hour4::from_timestamp(target);
        let cents = self.computer().prices.spot.cents.hour4.collect_one(h4);
        Ok(vec![HistoricalPriceEntry {
            time: usize::from(h4.to_timestamp()) as u64,
            usd: Dollars::from(cents.flatten().unwrap_or_default()),
        }])
    }

    fn all_prices(&self) -> Result<Vec<HistoricalPriceEntry>> {
        let computer = self.computer();
        Ok(computer
            .prices
            .spot
            .cents
            .hour4
            .collect()
            .into_iter()
            .enumerate()
            .filter_map(|(i, cents)| {
                Some(HistoricalPriceEntry {
                    time: usize::from(Hour4::from(i).to_timestamp()) as u64,
                    usd: Dollars::from(cents?),
                })
            })
            .collect())
    }
}
