#![warn(clippy::unwrap_used)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg_attr(not(feature = "std"), macro_use)]
#[cfg(not(feature = "std"))]
extern crate alloc;

#[macro_use]
extern crate tracing;
#[macro_use]
extern crate metrics;

pub use armour_derive::*;
pub use async_broadcast::RecvError;
pub use cid::{Cid, KeyScheme, KeyType, key_size};
#[cfg(feature = "sled")]
pub use database::{db::Database, raw_tree::RawTree, tree::CollectionTree};
pub use dyn_types::{Typ, get_type::GetType, value::Value};
pub use error::{DbError, DbResult};
#[cfg(feature = "fjall")]
pub use fjall::{self, Slice};
pub use indexes::index::{CompositionIndex, HashIndexExtractor};
pub use rapira;
pub use record::{Migration, Record};
#[cfg(feature = "sled")]
pub use sled::{self, InlineArray};
#[cfg(feature = "fjall")]
pub use transactional::{db::TxDb, raw::TxRawTree, tree::TxTree};
pub use types::{entry::Entry, fuid::Fuid, id64::Id64, record_status::RecordStatus};
#[cfg(all(feature = "sled", feature = "id32"))]
pub use types::{ident::ID, low_id::LowId, uid::Uid};

#[cfg(feature = "bytemuck")]
pub use crate::database::byte_as_slice::BytesAsValSlice;

pub mod cid;
#[cfg(feature = "sled")]
pub mod database;
pub mod dyn_types;
pub mod error;
pub mod indexes;
#[cfg(feature = "fjall")]
pub mod logdb;
pub mod record;
pub mod replication;
#[cfg(feature = "fjall")]
pub mod transactional;
pub mod types;
pub mod utils;

pub mod migrations {
    pub use crate::record::{MigrationRes, MigrationType};
}

pub type KV<K, V> = (K, V);

/// key should be Base64Url decoded 56 bytes long (~ 75 chars)
///
/// `const_hasher!(module_name, "ENV_KEY_NAME");`
///
/// env key concatenated with "APP_ID_KEY_" will be used to get the key
#[macro_export]
macro_rules! const_hasher {
    ($m_name: ident, $key_name: literal) => {
        pub mod $m_name {
            use $crate::types::enc::{Cipher, IdHasher};

            #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
            pub struct Hasher;

            impl IdHasher for Hasher {
                const HASHER: Cipher = Cipher::new(env!(concat!("APP_ID_KEY_", $key_name)));
            }
        }
    };
}

/// key should be Base64Url decoded 56 bytes long (~ 75 chars)
///
/// `hasher!(module_name, "hash_key");`
#[macro_export]
macro_rules! hasher {
    ($m_name: ident, $key: literal) => {
        pub mod $m_name {
            use $crate::types::enc::{Cipher, IdHasher};

            #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
            pub struct Hasher;

            impl IdHasher for Hasher {
                const HASHER: Cipher = Cipher::new($key);
            }
        }
    };
}

/// rapira_record!(User, UserID, sled::InlineArray, "users")
#[macro_export]
macro_rules! rapira_record {
    ($ty: ty, $id_ty: ty, $value_ty: ty, $name: literal) => {
        impl $crate::Record for $ty {
            type SelfId = $id_ty;
            type Value = $value_ty;
            const NAME: &'static str = $name;
            fn deser(bytes: &Self::Value) -> Self {
                rapira::deserialize(bytes.as_ref()).unwrap()
            }
            fn ser(&self) -> $value_ty {
                rapira::serialize(self).into()
            }
        }
    };
}

/// zerocopy_record!(User, UserID, sled::InlineArray, "users")
#[macro_export]
macro_rules! zerocopy_record {
    ($ty: ty, $id_ty: ty, $value_ty: ty, $name: literal) => {
        const SIZE: usize = std::mem::size_of::<$ty>();
        impl $crate::Record for $ty {
            type SelfId = $id_ty;
            type Value = $value_ty;
            const NAME: &'static str = $name;
            fn deser(bytes: &Self::Value) -> Self {
                let bytes: &[u8] = bytes.as_ref();
                let bytes: [u8; SIZE] = bytes.try_into().unwrap();
                zerocopy::transmute!(bytes)
            }
            fn ser(&self) -> $value_ty {
                let bytes: &[u8; SIZE] = zerocopy::transmute_ref!(self);
                bytes.into()
            }
        }
    };
}

/// bytemuck_record!(User, UserID, sled::InlineArray, "users")
#[macro_export]
macro_rules! bytemuck_slice_record {
    ($ty: ty, $id_ty: ty, $value_ty: ty, $name: literal) => {
        impl $crate::Record for $ty {
            type SelfId = $id_ty;
            type Value = $value_ty;
            const NAME: &'static str = $name;
            fn deser(bytes: &Self::Value) -> Self {
                if bytes.is_empty() {
                    return Default::default();
                }
                let vec = bytes.as_ref().to_vec();
                let vec: Vec<ProfileId> = bytemuck::cast_vec(vec);
                Self(vec)
            }
            fn ser(&self) -> $value_ty {
                let bytes: &[u8] = bytemuck::cast_slice(self);
                bytes.to_vec().into()
            }
        }
    };
}

/// `zerocopy_cid!(Key, [KeyType::U64...], Type::GROUP_BITS, field_name)`
#[macro_export]
macro_rules! zerocopy_cid {
    ($ty: ty, [$($key_type:expr),*], $group_bits:expr, $field:ident) => {
        impl $crate::Cid for $ty {
            type B = [u8; size_of::<Self>()];
            const TY: $crate::KeyScheme = $crate::KeyScheme::Typed(&[$($key_type),*]);
            const GROUP_BITS: u32 = $group_bits;
            fn encode(&self) -> Self::B {
                zerocopy::transmute!(*self)
            }
            fn decode(bytes: &Self::B) -> armour::types::Result<Self> {
                Ok(zerocopy::transmute!(*bytes))
            }
            fn group_id(&self) -> u32 {
                self.$field.group_id()
            }
        }
    };
}

#[cfg(test)]
mod tests {
    use rapira::Rapira;

    use super::*;
    use crate::types::id64::Id64;

    #[derive(Clone, Rapira)]
    struct User {
        name: String,
    }

    impl GetType for User {
        const TYPE: Typ = Typ::Custom("unimplemented", &[]);
    }

    const_hasher!(test_key, "TEST");
    type UserID = Id64<test_key::Hasher>;
    rapira_record!(User, UserID, Vec<u8>, "users");

    #[test]
    fn test_rapira_record() {
        let _user = User {
            name: "John Doe".to_string(),
        };
    }
}
