use core::ops::Deref;
use std::sync::{Arc, atomic::Ordering};

use arrayvec::ArrayVec;
use derive_more::Debug;
use sled::InlineArray;

use super::{builder::CollectionBuilder, db::Database, raw_tree::RawTree};
use crate::{
    Cid, Record,
    indexes::IndexUpdateCollection,
    replication::ChangeEvent,
    utils::{Change, EventHandlers},
};

pub(crate) const MIN_PAR_SIZE: usize = 2usize.pow(16);

pub type IndexesMap<Item> = ArrayVec<Box<dyn IndexUpdateCollection<Item>>, 8>;

#[derive(Debug)]
pub struct CollectionTree<Item: Record> {
    pub(crate) raw: RawTree,
    #[debug("{}", self.index_maps.len())]
    pub(crate) index_maps: Arc<IndexesMap<Item>>,
    pub(crate) handlers: Arc<EventHandlers<Item>>,
}

impl<Item: Record> Clone for CollectionTree<Item> {
    fn clone(&self) -> Self {
        Self {
            raw: self.raw.clone(),
            index_maps: self.index_maps.clone(),
            handlers: self.handlers.clone(),
        }
    }
}

impl<Item> CollectionTree<Item>
where
    Item: Record<Value = InlineArray> + std::fmt::Debug,
{
    #[doc(alias = "new")]
    #[doc(alias = "create")]
    pub fn open(db: &Database) -> Self {
        CollectionBuilder::new(db).build()
    }

    pub fn with_name(db: &Database, name: &str) -> Self {
        CollectionBuilder::with_name(db, name).build()
    }

    pub fn builder(db: &Database) -> CollectionBuilder<'_, Item> {
        CollectionBuilder::new(db)
    }

    #[doc(alias = "inner")]
    pub fn raw(&self) -> &RawTree {
        &self.raw
    }

    #[inline]
    pub(super) fn invalidate_hash(&self, id: &Item::SelfId) {
        let group = id.group_id();
        self.raw.inner.invalidate_hash(group);
    }

    /// update indexes when item body is changed
    #[inline]
    pub(super) fn update_indexes(&self, id: &Item::SelfId, old: Option<&Item>, new: Option<&Item>) {
        for map in self.index_maps.iter() {
            map.update(id, old, new);
        }
    }

    /// update fn when update item body or create item
    #[inline]
    pub(super) fn upsert_event(&self, id: &Item::SelfId, old: Option<&Item>, new: &Item) {
        if old.is_none() {
            self.raw.inner.seq.fetch_add(1, Ordering::Relaxed);
        }

        if let Some(f) = self.handlers.on_upsert.as_ref() {
            f(id, Change { old, new });
        }
    }

    /// call when item removed
    #[inline]
    pub(super) fn remove_event(&self, id: &Item::SelfId, item: &Item) {
        self.raw.inner.seq.fetch_sub(1, Ordering::AcqRel);

        if let Some(f) = self.handlers.on_remove.as_ref() {
            f(id, item);
        }
    }

    /// item, old_id, new_id
    #[inline]
    pub(super) fn id_change_event(
        &self,
        item: &Item,
        old_id: &Item::SelfId,
        new_id: &Item::SelfId,
    ) {
        if let Some(f) = self.handlers.on_id_change.as_ref() {
            f(item, old_id, new_id);
        }
    }

    #[instrument(level = "trace", skip(self, event_fn), fields(name = self.name))]
    pub(crate) fn send_event(
        &self,
        event_fn: impl FnOnce() -> ChangeEvent<super::ByteValue, super::ByteValue>,
    ) {
        if let Some((sender, _)) = self.subscribers.get() {
            match sender.try_broadcast(event_fn()) {
                Ok(None) => {}
                Ok(Some(item)) => {
                    error!(?item, "broadcast is full, removed");
                }
                Err(err) => {
                    error!(%err);
                }
            }
        }
    }
}

impl<Item: Record> Deref for CollectionTree<Item> {
    type Target = RawTree;

    fn deref(&self) -> &Self::Target {
        &self.raw
    }
}

impl<Item: Record> From<CollectionTree<Item>> for RawTree {
    fn from(value: CollectionTree<Item>) -> Self {
        value.raw
    }
}
