use crate::{
    error::{GenericError, PERes, PIRes, ReadError},
    id::{IndexId, SegmentId},
    index::{
        config::{IndexTypeInternal, ValueMode},
        serialization::{deserialize, reuse_deserialize},
        tree::nodes::{Node, TreeNode, TreeNodeRef},
    },
    persy::PersyImpl,
    snapshots::SnapshotRef,
};

pub(crate) struct IndexLimits {
    bottom: usize,
    top: usize,
}
impl IndexLimits {
    pub(crate) fn new(bottom: usize, top: usize) -> Self {
        Self { bottom, top }
    }
    pub(crate) fn bottom(&self) -> usize {
        self.bottom
    }
    pub(crate) fn top(&self) -> usize {
        self.top
    }
}

pub trait IndexKeeper<K, V> {
    fn load(&self, node: &TreeNodeRef) -> PERes<TreeNode<K, V>> {
        Ok(self.failable_load(node)?.unwrap())
    }
    fn failable_load(&self, node: &TreeNodeRef) -> PERes<Option<TreeNode<K, V>>>;
    fn load_with(&self, node: &TreeNodeRef, reuse: Option<Node<K>>) -> PERes<TreeNode<K, V>>;
    fn get_root(&self) -> PERes<Option<TreeNodeRef>>;
    fn value_mode(&self) -> ValueMode;
    fn index_name(&self) -> &String;
}

pub(crate) trait IndexModify<K, V>: IndexKeeper<K, V> {
    fn load_from_node<R, C>(&self, node_ref: &TreeNodeRef, loader: C) -> PIRes<Option<R>>
    where
        C: FnOnce(&TreeNode<K, V>, u16) -> R;
    fn lock(&mut self, node: &TreeNodeRef, version: u16) -> PIRes<bool>;
    fn is_locked(&mut self, node: &TreeNodeRef) -> bool;
    fn unlock(&mut self, node: &TreeNodeRef) -> bool;
    fn get_root_refresh(&mut self) -> PIRes<Option<TreeNodeRef>>;
    fn lock_config(&mut self) -> PIRes<bool>;
    fn insert(&mut self, node: TreeNode<K, V>) -> PIRes<TreeNodeRef>;
    fn update_with<R, C>(&mut self, node_ref: &TreeNodeRef, updater: C) -> PIRes<R>
    where
        C: FnOnce(&mut TreeNode<K, V>) -> (bool, R);
    fn delete(&mut self, node: &TreeNodeRef, version: u16) -> PIRes<()>;
    fn delete_to_own(&mut self, node: &TreeNodeRef) -> PIRes<TreeNode<K, V>>;
    fn set_root(&mut self, root: Option<TreeNodeRef>) -> PIRes<()>;
    fn limits(&self) -> &IndexLimits;
}

pub struct IndexSegmentKeeper<'a> {
    name: String,
    segment: SegmentId,
    root: Option<TreeNodeRef>,
    store: &'a PersyImpl,
    snapshot: SnapshotRef,
    value_mode: ValueMode,
}

impl<'a> IndexSegmentKeeper<'a> {
    pub fn new(
        name: &str,
        index_id: &IndexId,
        root: Option<TreeNodeRef>,
        store: &'a PersyImpl,
        snapshot: &SnapshotRef,
        value_mode: ValueMode,
    ) -> IndexSegmentKeeper<'a> {
        IndexSegmentKeeper {
            name: name.to_string(),
            segment: index_id.get_data_id(),
            root,
            store,
            snapshot: snapshot.clone(),
            value_mode,
        }
    }
}

impl<K: IndexTypeInternal, V: IndexTypeInternal> IndexKeeper<K, V> for IndexSegmentKeeper<'_> {
    fn failable_load(&self, node: &TreeNodeRef) -> PERes<Option<TreeNode<K, V>>> {
        self.store
            .read_snap_fn(self.segment, node, &self.snapshot, deserialize)
            .map_err(map_read_err)
    }
    fn load_with(&self, node: &TreeNodeRef, reuse: Option<Node<K>>) -> PERes<TreeNode<K, V>> {
        let rec = self
            .store
            .read_snap_fn(self.segment, node, &self.snapshot, |e| reuse_deserialize(e, reuse))
            .map_err(map_read_err)?
            .unwrap();
        Ok(rec)
    }
    fn get_root(&self) -> PERes<Option<TreeNodeRef>> {
        Ok(self.root)
    }
    fn value_mode(&self) -> ValueMode {
        self.value_mode.clone()
    }

    fn index_name(&self) -> &String {
        &self.name
    }
}

pub(crate) fn map_read_err(r: ReadError) -> GenericError {
    match r {
        ReadError::SegmentNotFound => panic!("The segment should be already checked"),
        ReadError::InvalidPersyId(_) => panic!("The Internal id should be everytime valid"),
        ReadError::Generic(g) => g,
    }
}
