use alloc::string::ToString;
use core::{any::Any, mem::transmute};

use crate::*;

fn demote_static<'db>(ty: StaticType) -> DynamicType<'db> {
    // SAFETY: this has GOT to be safe, right? i can't figure out how to
    // make ReflectedType covariant, which would let rustc just
    // do it automatically.
    unsafe { transmute::<DynamicType<'static>, DynamicType<'db>>(ty) }
}

/// Home of `serialize` and `deserialize`.
///
/// Known as the reflection database, this tracks the reflected types of all known
/// static Rust types, as well as how to serialize/deserialize the ones which
/// require serde integration.
///
/// # Lifetimes, the reflection database, and you
///
/// `refloctopus` is designed so you can use ONLY compile-time generated
/// reflected types if you want, but it also allows runtime generated
/// reflected types. The only place these lifetimes appear in the public API is
/// `register_type`, but they leak into the definition of the type.
///
/// The `'db` lifetime represents (better yet, it is) how long the particular reflected type
/// references we are tracking are valid. Why not always own the reflected types? Because then we
/// need to copy out the top-level metadata that could otherwise remain read-only
/// and shared! This way, the db can be initialized with only `Cow::Borrowed` of statics
/// (either derived, const fn'd, or truly runtime borrowed).
#[derive(Clone, Default)]
pub struct Db<'db> {
    pub(crate) known_types: BTreeMap<TypeId, (DynamicType<'db>, FieldsStorage<'db>)>,
    #[cfg(feature = "use_serde")]
    pub(crate) deserialize_trampolines: BTreeMap<TypeId, DeserializeTrampoline>,
    #[cfg(feature = "use_serde")]
    pub(crate) serialize_vtables: BTreeMap<TypeId, *mut ()>,
}

#[derive(Clone)]
/// An area of memory, owned or borrowed, storing
/// the field data for a particular `ReflectedType`.
pub struct FieldsStorage<'db> {
    pub(crate) area: Cow<'db, [u8]>,
}
impl<'db> FieldsStorage<'db> {
    pub fn borrow(&'db self) -> Self {
        Self::from_inner(Cow::Borrowed(&self.area))
    }

    pub fn from_inner(area: Cow<'db, [u8]>) -> FieldsStorage {
        FieldsStorage { area }
    }

    pub fn into_inner(self) -> Cow<'db, [u8]> {
        self.area
    }

    pub fn cursor(&'db self) -> FieldsCursor<'db> {
        FieldsCursor {
            cursor: Cell::new(self.area.as_ptr() as *mut u8),
            _marker: Default::default(),
        }
    }
}

impl<'db> Db<'db> {
    /// Make a new, empty reflection database.
    pub fn new() -> Self {
        Default::default()
    }

    /// Deserialize a value out of some data.
    ///
    /// The reflection database is consulted for the reflection type.
    #[cfg(feature = "use_serde")]
    pub fn deserialize<'de, T: Any, D>(
        &'db self,
        src: D,
    ) -> Result<T, <D as serde::Deserializer<'de>>::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use serde::de::DeserializeSeed;

        let (rust_type, fields) = self
            .known_types
            .get(&TypeId::of::<T>())
            .ok_or_else(|| serde::de::Error::custom("type missing from db, cannot deserialize"))?;
        let mut uninit = core::mem::MaybeUninit::uninit();
        crate::Deserialize {
            db: self,
            name: rust_type.name,
            dst: ShapedOutputLocation {
                ptr: uninit.as_mut_ptr() as *mut u8,
                shape: &rust_type.shape,
                fields: &fields.cursor(),
                _data: Default::default(),
            },
        }
        .deserialize(src)?;
        Ok(unsafe { uninit.assume_init() })
    }

    /// Serialize some value.
    ///
    /// The reflection database is consulted for the reflection type.
    pub fn serialize<S: serde::Serializer, T: HasKey>(
        &'db self,
        s: S,
        val: &T,
    ) -> Result<S::Ok, S::Error> {
        let id = TypeId::of::<T::Key>();
        let (rust_type, fields) = self.known_types.get(&id).ok_or_else(|| {
            <S::Error as serde::ser::Error>::custom("type missing from db, cannot serialize")
        })?;
        <Serialize as serde::Serialize>::serialize(
            &Serialize::new(
                self,
                rust_type.name,
                &fields.cursor(),
                &ShapedLocation {
                    shape: &rust_type.shape,
                    ptr: val as *const _ as *const _,
                    fields: FieldsStorage::from_inner(Cow::Borrowed(&fields.area)),
                    _data: Default::default(),
                },
            ),
            s,
        )
    }

    /// Associate a `ReflectedType` with some Rust type.
    ///
    /// This should generally only be called from the implementation of `Reflect::register`,
    /// because there is no other convenient place to have a [FieldsStorage].
    pub fn register_type<T: Reflect>(&mut self, fields: FieldsStorage<'db>) -> &mut Db<'db> {
        self.insert(
            TypeId::of::<T::Key>(),
            demote_static(T::rust_type()),
            fields,
        );
        self
    }

    /// Associate a `#[derive(Reflect)]` type.
    ///
    /// This _can_ be called anywhere, but `Reflect::register` implementations also call it.
    pub fn register_const<T: StaticReflect>(&mut self) -> &mut Db<'db> {
        self.insert(
            TypeId::of::<T::Key>(),
            demote_static(Cow::Borrowed(&T::RUST_TYPE)),
            T::FIELDS.clone(),
        );

        self
    }

    /// Old fashioned 100% runtime type registration. For when you're
    /// doing something that the derive can't help you with.
    pub fn insert(&mut self, id: TypeId, val: DynamicType<'db>, fields: FieldsStorage<'db>) {
        self.known_types.insert(id, (val, fields));
    }

    /// When `T` is encountered during serialization, it will not be reflected into
    /// (even if it has a known type!) and instead the `Serialize`/`Deserialize`
    /// implementations will be used.
    ///
    /// `Default::default` is required to steal a trait object pointer without doing untoward
    /// undefined behavior. When `&raw` is stable, this bound can be removed.
    #[cfg(feature = "use_serde")]
    pub fn register_serde_leaf<
        T: HasKey + Default + serde::Serialize + for<'de> serde::Deserialize<'de>,
    >(
        &mut self,
    ) -> &mut Db<'db> {
        fn de<T: for<'b> serde::Deserialize<'b>>(
            d: &mut dyn erased_serde::Deserializer,
            dst: &ShapedOutputLocation,
        ) -> Result<(), erased_serde::Error> {
            let x: T = T::deserialize(d)?;
            // SAFETY: if everything has gone right, this is the crucial line that the correctness
            // of the reflection system is to ensure.
            unsafe { dst.ptr.cast::<T>().write(x) };
            Ok(())
        }
        let typeid = TypeId::of::<T::Key>();
        // SAFETY: this isn't completely kosher, we're peeking into the DST
        // representation of trait objects. i feel pretty good that this won't break.
        // the tests should catch it if it does i hope?
        let vtable = unsafe {
            core::mem::transmute::<&dyn erased_serde::Serialize, TraitObject>(
                &T::default() as &dyn erased_serde::Serialize
            )
            .vtable
        };
        self.serialize_vtables.insert(typeid, vtable);
        self.deserialize_trampolines.insert(typeid, de::<T>);
        self
    }

    #[cfg(feature = "use_serde")]
    /// Deserialize a leaf serde value,
    pub(crate) fn deserialize_leaf<'de, 'data, 'visitor, 'fields, D>(
        &self,
        d: D,
        leaf: TypeId,
        dst: &ShapedOutputLocation<'data, 'visitor, 'fields>,
    ) -> Result<(), erased_serde::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use serde::de::Error as deError;
        let trampoline = self.deserialize_trampolines.get(&leaf).ok_or_else(|| {
            erased_serde::Error::custom(alloc::format!(
                "leaf type {:?} missing from reflection db",
                leaf
            ))
        })?;
        trampoline(&mut erased_serde::Deserializer::erase(d), dst)
            .map_err(|e| erased_serde::Error::custom(e.to_string()))
    }

    /// Find a type in the db.
    pub(crate) fn lookup_id(
        &'db self,
        id: TypeId,
    ) -> Result<&'db (DynamicType<'db>, FieldsStorage), alloc::string::String> {
        self.known_types
            .get(&id)
            .ok_or_else(|| alloc::format!("reflection db missing type info for typeid {:?}", id))
    }
}
#[doc(hidden)]
/// Used internally by the db to avoid borrowed metadata
pub trait HasKey {
    type Key: 'static;
}
macro_rules! haskey {
    ($($name:ident),+) => {
        $(
            impl<'a> HasKey for $name<'a> {
                type Key = $name<'static>;
            }
        )+
    }
}

haskey!(EnumArm, DataShape, Field);
