use std::{borrow::Borrow, fmt::Debug, ops::RangeBounds};

use armour_derive::armour_metrics;
use fjall::Slice;

use super::{ItemWithRaw, RawTree, helpers::*};
use crate::{Cid, DbError, DbResult, Entry, Record};

#[derive(derive_more::Debug)]
pub struct ReadTree<Item: Record> {
    pub(crate) raw: RawTree,
    pub(crate) phantom: std::marker::PhantomData<Item>,
}

impl<Item: Record> Clone for ReadTree<Item> {
    fn clone(&self) -> Self {
        Self {
            raw: self.raw.clone(),
            phantom: std::marker::PhantomData,
        }
    }
}

impl<Item> ReadTree<Item>
where
    Item: Record<Value = Slice> + Debug,
{
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    #[armour_metrics(prefix = "armour_logdb_tree", name = self.raw.static_name())]
    fn get_by_slice(&self, key: &[u8]) -> DbResult<Option<Item>> {
        let val = self.raw.tree.get(key)?;
        match val {
            Some(val) => Ok(Some(Item::deser_owned(val))),
            None => Ok(None),
        }
    }

    /// get value by id
    pub fn get(&self, id: impl Borrow<Item::SelfId> + Debug) -> DbResult<Option<Item>> {
        let key = Cid::encode(id.borrow());
        self.get_by_slice(key.as_ref())
    }

    /// get value by id or return not found
    pub fn get_or_err(&self, id: impl Borrow<Item::SelfId> + Debug) -> DbResult<Item> {
        let key = Cid::encode(id.borrow());
        self.get_by_slice(key.as_ref())
            .and_then(|item| item.ok_or(DbError::NotFound))
    }

    /// check exist by id
    #[doc(alias = "exists")]
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    #[armour_metrics(prefix = "armour_logdb_tree", name = self.raw.static_name())]
    pub fn contains(&self, id: impl Borrow<Item::SelfId> + Debug) -> DbResult<bool> {
        let key = Cid::encode(id.borrow());
        self.raw
            .tree
            .contains_key(key.as_ref())
            .map_err(DbError::from)
    }

    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    #[armour_metrics(prefix = "armour_logdb_tree", name = self.raw.static_name())]
    pub fn size_of(&self, id: impl Borrow<Item::SelfId> + Debug) -> DbResult<Option<u32>> {
        let key = Cid::encode(id.borrow());
        self.raw.tree.size_of(key.as_ref()).map_err(DbError::from)
    }

    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    #[armour_metrics(prefix = "armour_logdb_tree", name = self.raw.static_name())]
    pub fn first(&self) -> DbResult<Option<Entry<Item>>> {
        let val = self.raw.tree.first_key_value();
        match val {
            Some(val) => {
                let (key, val) = val.into_inner()?;
                let id = Item::deser_key(&key);
                let value = Item::deser_owned(val);
                Ok(Some(Entry::new(id, value)))
            }
            None => Ok(None),
        }
    }

    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    #[armour_metrics(prefix = "armour_logdb_tree", name = self.raw.static_name())]
    pub fn last(&self) -> DbResult<Option<Entry<Item>>> {
        let val = self.raw.tree.last_key_value();
        match val {
            Some(val) => {
                let (key, val) = val.into_inner()?;
                let id = Item::deser_key(&key);
                let value = Item::deser_owned(val);
                Ok(Some(Entry::new(id, value)))
            }
            None => Ok(None),
        }
    }

    /// get iterator for collection
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    pub fn iter(&self) -> super::IterTree<Entry<Item>> {
        counter!("armour_logdb_tree_iter_total", "name" => self.raw.name.clone()).increment(1);
        self.raw.tree.iter().filter_map(entry_filter_map::<Item>)
    }

    /// get iterator by prefix for collection
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    pub fn scan_prefix<P>(&self, prefix: P) -> super::IterTree<Entry<Item>>
    where
        P: AsRef<[u8]> + Debug,
    {
        counter!("armour_logdb_tree_scan_prefix_total", "name" => self.raw.name.clone())
            .increment(1);
        self.raw
            .tree
            .prefix(prefix)
            .filter_map(entry_filter_map::<Item>)
    }

    /// get iterator by prefix for collection
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    pub fn scan_prefix_key<P>(&self, prefix: P) -> super::IterTree<Item::SelfId>
    where
        P: AsRef<[u8]> + Debug,
    {
        counter!("armour_logdb_tree_scan_prefix_key_total", "name" => self.raw.name.clone())
            .increment(1);
        self.raw
            .tree
            .prefix(prefix)
            .filter_map(entry_key_filter_map::<Item>)
    }

    /// get iterator by range for collection
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    pub fn range_iter<K, R>(&self, range: R) -> super::IterTree<Entry<Item>>
    where
        K: AsRef<[u8]>,
        R: RangeBounds<K> + Debug,
    {
        counter!("armour_logdb_tree_range_iter_total", "name" => self.raw.name.clone())
            .increment(1);
        self.raw
            .tree
            .range(range)
            .filter_map(entry_filter_map::<Item>)
    }

    /// get iterator by prefix for collection
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    pub fn try_scan_prefix<P>(&self, prefix: P) -> super::TryIterTree<Entry<Item>>
    where
        P: AsRef<[u8]> + Debug,
    {
        counter!("armour_logdb_tree_try_scan_prefix_total", "name" => self.raw.name.clone())
            .increment(1);
        self.raw.tree.prefix(prefix).map(entry_map::<Item>)
    }

    /// get iterator by prefix for collection
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    pub fn try_scan_prefix_with_raw<P>(&self, prefix: P) -> super::TryIterTree<ItemWithRaw<Item>>
    where
        P: AsRef<[u8]> + Debug,
    {
        counter!("armour_logdb_tree_try_scan_prefix_with_raw_total", "name" => self.raw.name.clone()).increment(1);
        self.raw.tree.prefix(prefix).map(entry_map_with_raw::<Item>)
    }

    /// get iterator by range for collection
    /// if item error, return Error
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    pub fn try_range_iter<K, R>(self, range: R) -> super::TryIterTree<ItemWithRaw<Item>>
    where
        K: AsRef<[u8]>,
        R: RangeBounds<K> + Debug,
    {
        counter!("armour_logdb_tree_try_range_iter_total", "name" => self.raw.name.clone())
            .increment(1);
        self.raw.tree.range(range).map(entry_map_with_raw::<Item>)
    }

    /// iter without deserialize value
    #[instrument(level = "debug", skip_all, fields(name = Item::NAME))]
    pub fn iter_keys(&self) -> impl DoubleEndedIterator<Item = Item::SelfId> {
        counter!("armour_logdb_tree_iter_keys_total", "name" => self.raw.name.clone()).increment(1);
        self.raw
            .tree
            .iter()
            .filter_map(entry_key_filter_map::<Item>)
    }
}
