use serde::Serialize;
use specta::{DataType, DefOpts, Type};
use typescript_type_def::{
    type_expr::{DefinedTypeInfo, Ident, TypeDefinition, TypeExpr, TypeInfo, TypeIntersection},
    TypeDef,
};

use crate::Record;

#[derive(Clone, Debug, Serialize)]
pub struct Entry<Val>
where
    Val: Record + Serialize,
    Val::SelfId: Serialize,
{
    #[serde(flatten)]
    pub key: Val::SelfId,
    #[serde(flatten)]
    pub val: Val,
}

impl<Val> Entry<Val>
where
    Val: Record + Serialize,
    Val::SelfId: Serialize,
{
    pub fn new(key: Val::SelfId, val: Val) -> Self {
        Entry { key, val }
    }

    pub fn get_id(self) -> Val::SelfId {
        self.key
    }
    pub fn get_val(&self) -> &Val {
        &self.val
    }
    pub fn split(self) -> (Val::SelfId, Val) {
        let Entry { key, val } = self;
        (key, val)
    }
    pub fn as_tuple(&self) -> (Val::SelfId, &Val) {
        let Entry { key, val } = self;
        (*key, val)
    }
}

impl<Val> From<(Val::SelfId, Val)> for Entry<Val>
where
    Val: Record + Serialize,
    Val::SelfId: Serialize,
{
    fn from(tuple: (Val::SelfId, Val)) -> Self {
        Entry::new(tuple.0, tuple.1)
    }
}

impl<Val> Type for Entry<Val>
where
    Val: Record + Serialize,
    Val::SelfId: Serialize,
{
    const NAME: &'static str = "Entry";

    fn inline(opts: DefOpts, generics: &[DataType]) -> DataType {
        todo!()
    }
}

impl<Val> TypeDef for Entry<Val>
where
    Self: 'static,
    Val: Record + Serialize + TypeDef,
    Val::SelfId: Serialize + TypeDef,
{
    const INFO: TypeInfo = TypeInfo::Defined(DefinedTypeInfo {
        def: TypeDefinition {
            docs: None,
            path: &[],
            name: Ident("Entry"),
            generic_vars: &[Ident("T"), Ident("Id")],
            def: TypeExpr::Intersection(TypeIntersection {
                docs: None,
                members: &[TypeExpr::Ref(&Val::SelfId::INFO), TypeExpr::Ref(&Val::INFO)],
            }),
        },
        generic_args: &[],
    });
}
