Documentation
use std::{borrow::Cow, marker::PhantomData};

use crate::error::ContextWrapper;

use super::*;

pub trait Pebble {
    type Inner;
    fn get_bytes(v: &Self::Inner) -> Cow<[u8]>;
    fn from_bytes(v: Cow<[u8]>) -> anyhow::Result<Self::Inner>;
}

impl Pebble for () {
    type Inner = Self;
    fn get_bytes(_: &Self::Inner) -> Cow<[u8]> {
        Cow::Borrowed(&[])
    }

    fn from_bytes(_: Cow<[u8]>) -> anyhow::Result<Self::Inner> {
        Ok(())
    }
}

impl Pebble for Cow<'_, [u8]> {
    type Inner = Self;
    fn get_bytes(v: &Self::Inner) -> Cow<[u8]> {
        v.clone()
    }

    fn from_bytes(v: Cow<[u8]>) -> anyhow::Result<Self::Inner> {
        Ok(Cow::Owned(v.into_owned())) //todo: lifetime shit
    }
}

impl Pebble for Vec<u8> {
    type Inner = Self;
    fn get_bytes(v: &Self::Inner) -> Cow<[u8]> {
        Cow::Borrowed(v)
    }

    fn from_bytes(v: Cow<[u8]>) -> anyhow::Result<Self::Inner> {
        Ok(v.into_owned())
    }
}

impl Pebble for String {
    type Inner = Self;
    fn get_bytes(v: &Self::Inner) -> Cow<[u8]> {
        Cow::Borrowed(v.as_bytes())
    }

    fn from_bytes(v: Cow<[u8]>) -> anyhow::Result<Self::Inner> {
        String::from_utf8(v.into_owned()).anyhow()
    }
}

pub struct UsingSerde<T>(PhantomData<T>)
where
    T: serde::Serialize + for<'de> serde::Deserialize<'de>;

impl<T> Pebble for UsingSerde<T>
where
    T: serde::Serialize + for<'de> serde::Deserialize<'de>,
{
    type Inner = T;
    fn get_bytes(v: &T) -> Cow<[u8]> {
        Cow::Owned(postcard::to_allocvec(v).unwrap())
    }

    fn from_bytes(v: Cow<[u8]>) -> anyhow::Result<T> {
        postcard::from_bytes(&v).anyhow()
    }
}

impl<const N: usize> Pebble for [u8; N] {
    type Inner = Self;

    fn get_bytes(v: &Self::Inner) -> Cow<[u8]> {
        Cow::Borrowed(v)
    }

    fn from_bytes(v: Cow<[u8]>) -> anyhow::Result<Self::Inner> {
        Ok(v.into_owned()
            .try_into()
            .expect("Failed to deserlize slice"))
    }
}

#[cfg(feature = "bytemuck")]
pub struct UsingBytemuck<T>(PhantomData<T>)
where
    T: bytemuck::AnyBitPattern + bytemuck::NoUninit;

#[cfg(feature = "bytemuck")]
impl<T> Pebble for UsingBytemuck<T>
where
    T: bytemuck::AnyBitPattern + bytemuck::NoUninit,
{
    type Inner = T;
    fn get_bytes(v: &T) -> Cow<[u8]> {
        // It's always safe to take byte pointer to aligned struct because we *erasing* information
        Cow::Borrowed(bytemuck::bytes_of::<T>(v))
    }

    fn from_bytes(v: Cow<[u8]>) -> anyhow::Result<T> {
        // It's unsafe to transmute byte pointer to struct pointer because it can be misaligned.
        // We wrapping it into Packed<T> to tell compiler that alignment is always 1, and then copying it
        bytemuck::try_from_bytes::<Packed<T>>(&v).map(|x| x.0).anyhow()
    }
}

#[cfg(feature = "bellscoin")]
pub struct UsingConsensus<T>(PhantomData<T>)
where
    T: bellscoin::consensus::Decodable + bellscoin::consensus::Encodable;

#[cfg(feature = "bellscoin")]
impl<T> Pebble for UsingConsensus<T>
where
    T: bellscoin::consensus::Decodable + bellscoin::consensus::Encodable,
{
    type Inner = T;

    fn get_bytes(v: &Self::Inner) -> Cow<[u8]> {
        let mut result = Vec::new();
        v.consensus_encode(&mut result).unwrap();
        Cow::Owned(result)
    }

    fn from_bytes(v: Cow<[u8]>) -> anyhow::Result<Self::Inner> {
        bellscoin::consensus::Decodable::consensus_decode(&mut std::io::Cursor::new(&v)).anyhow()
    }
}

#[macro_export]
macro_rules! impl_pebble {
    (int $T:ty) => {
        impl Pebble for $T {
            type Inner = Self;

            fn get_bytes(v: &Self::Inner) -> std::borrow::Cow<[u8]> {
                Cow::Owned(v.to_be_bytes().to_vec())
            }

            fn from_bytes(v: Cow<[u8]>) -> anyhow::Result<Self::Inner> {
                Ok(Self::from_be_bytes((&*v).try_into().anyhow()?))
            }
        }
    };

    ($WRAPPER:ty = $INNER:ty) => {
        impl $crate::rocks_wrapper::Pebble for $WRAPPER {
            type Inner = Self;

            fn get_bytes(v: &Self::Inner) -> std::borrow::Cow<[u8]> {
                <$INNER>::get_bytes(&v.0)
            }

            fn from_bytes(v: std::borrow::Cow<[u8]>) -> anyhow::Result<Self::Inner> {
                <$INNER>::from_bytes(v).map(Self)
            }
        }
    };

    ($WRAPPER:ty as $INNER:ty) => {
        impl $crate::db::Pebble for $WRAPPER {
            type Inner = Self;

            fn get_bytes(v: &Self::Inner) -> std::borrow::Cow<[u8]> {
                let x = <$INNER>::from(v);
                let x = <$INNER>::get_bytes(&x);
                std::borrow::Cow::Owned(x.into_owned())
            }

            fn from_bytes(v: std::borrow::Cow<[u8]>) -> anyhow::Result<Self::Inner> {
                <$INNER>::from_bytes(v).map(Self::from)
            }
        }
    };
}

impl_pebble!(int i8);
impl_pebble!(int u8);
impl_pebble!(int i16);
impl_pebble!(int u16);
impl_pebble!(int i32);
impl_pebble!(int u32);
impl_pebble!(int i64);
impl_pebble!(int u64);
impl_pebble!(int i128);
impl_pebble!(int u128);