use crate::test_db::TestDb;
use agdb::AgdbSerialize;
use agdb::DbElement;
use agdb::DbError;
use agdb::DbId;
use agdb::DbMemory;
use agdb::DbSerialize;
use agdb::DbType;
use agdb::DbTypeMarker;
use agdb::DbValue;
use agdb::QueryBuilder;
use agdb::QueryId;
use agdb::QueryResult;

#[derive(Default, Debug, Clone, PartialEq, DbTypeMarker, DbValue, DbSerialize)]
enum Status {
    Active,
    #[default]
    Inactive,
}

#[derive(DbType, Debug)]
struct User {
    user_id: u64,
    password: String,
    status: Status,
}

#[derive(DbType, PartialEq, Debug)]
struct MyValue {
    db_id: Option<QueryId>,
    name: String,
    age: u64,
}

#[derive(DbType, PartialEq, Debug)]
struct MyValueWithBool {
    db_id: Option<QueryId>,
    name: String,
    is_active: bool,
    truths: Vec<bool>,
}

#[derive(Clone, PartialEq, Debug, DbValue, DbTypeMarker, DbSerialize)]
struct Attribute {
    name: String,
    value: String,
}

#[derive(DbType, Default, PartialEq, Debug)]
struct MyCustomVec {
    vec: Vec<Status>,
    attributes: Vec<Attribute>,
}

#[derive(DbType, PartialEq, Debug)]
struct WithOption {
    name: String,
    value: Option<u64>,
}

#[test]
fn user_db_types() {
    #[derive(Default, Debug, PartialEq, DbType)]
    struct MyData {
        bytes: Vec<u8>,
        u64: u64,
        u32: u32,
        i64: i64,
        i32: i32,
        f64: f64,
        f32: f32,
        string: String,
        vec_u64: Vec<u64>,
        vec_u32: Vec<u32>,
        vec_i64: Vec<i64>,
        vec_i32: Vec<i32>,
        vec_f64: Vec<f64>,
        vec_f32: Vec<f32>,
        vec_string: Vec<String>,
        custom_enum: Status,
    }

    let my_data = MyData {
        bytes: vec![1_u8],
        u64: 1_u64,
        u32: 2_u32,
        i64: -1_i64,
        i32: -2_i32,
        f64: 1.1_f64,
        f32: 2.2_f32,
        string: "hello".to_string(),
        vec_u64: vec![1_u64],
        vec_u32: vec![2_u32],
        vec_i64: vec![-1_i64],
        vec_i32: vec![-2_i32],
        vec_f64: vec![1.1_f64],
        vec_f32: vec![2.2_f32],
        vec_string: vec!["world".to_string()],
        custom_enum: Status::Active,
    };
    let keys: Vec<DbValue> = vec![
        "bytes".into(),
        "u64".into(),
        "u32".into(),
        "i64".into(),
        "i32".into(),
        "f64".into(),
        "f32".into(),
        "string".into(),
        "vec_u64".into(),
        "vec_u32".into(),
        "vec_i64".into(),
        "vec_i32".into(),
        "vec_f64".into(),
        "vec_f32".into(),
        "vec_string".into(),
        "custom_enum".into(),
    ];

    let element = DbElement {
        id: DbId(0),
        from: DbId::default(),
        to: DbId::default(),
        values: vec![
            ("bytes", vec![1_u8]).into(),
            ("u64", 1_u64).into(),
            ("u32", 2_u64).into(),
            ("i64", -1_i64).into(),
            ("i32", -2_i64).into(),
            ("f64", 1.1_f64).into(),
            ("f32", 2.2_f32).into(),
            ("string", "hello").into(),
            ("vec_u64", vec![1_u64]).into(),
            ("vec_u32", vec![2_u32]).into(),
            ("vec_i64", vec![-1_i64]).into(),
            ("vec_i32", vec![-2_i32]).into(),
            ("vec_f64", vec![1.1_f64]).into(),
            ("vec_f32", vec![2.2_f32]).into(),
            ("vec_string", vec!["world"]).into(),
            ("custom_enum", Status::Active).into(),
        ],
    };

    assert_eq!(MyData::db_keys(), keys);
    assert_eq!(&my_data.to_db_values(), &element.values);
    assert_eq!(MyData::from_db_element(&element).unwrap(), my_data);
}

#[test]
fn insert_node_values_custom() {
    #[derive(DbType)]
    struct MyValue {
        name: String,
        age: u64,
    }

    let mut db = TestDb::new();
    let my_value = MyValue {
        name: "my name".to_string(),
        age: 20,
    };
    db.exec_mut(QueryBuilder::insert().nodes().values(&my_value).query(), 1);
    db.exec_elements(
        QueryBuilder::select().ids(1).query(),
        &[DbElement {
            id: DbId(1),
            from: DbId::default(),
            to: DbId::default(),
            values: vec![("name", "my name").into(), ("age", 20_u64).into()],
        }],
    );
}

#[test]
fn insert_node_values_uniform_custom() {
    #[derive(DbType)]
    struct MyValue {
        name: String,
        age: u64,
    }

    let mut db = TestDb::new();
    let my_value = MyValue {
        name: "my name".to_string(),
        age: 20,
    };
    db.exec_mut(
        QueryBuilder::insert()
            .nodes()
            .count(2)
            .values_uniform(&my_value)
            .query(),
        2,
    );
    db.exec_elements(
        QueryBuilder::select().ids([1, 2]).query(),
        &[
            DbElement {
                id: DbId(1),
                from: DbId::default(),
                to: DbId::default(),
                values: vec![("name", "my name").into(), ("age", 20_u64).into()],
            },
            DbElement {
                id: DbId(2),
                from: DbId::default(),
                to: DbId::default(),
                values: vec![("name", "my name").into(), ("age", 20_u64).into()],
            },
        ],
    );
}

#[test]
fn select_custom_value_keys() {
    #[derive(Debug, Clone, PartialEq, DbType)]
    struct MyValue {
        name: String,
        age: u64,
    }

    let mut db = TestDb::new();
    let my_value = MyValue {
        name: "my name".to_string(),
        age: 20,
    };
    db.exec_mut(
        QueryBuilder::insert()
            .nodes()
            .count(2)
            .values_uniform(&my_value)
            .query(),
        2,
    );

    let db_value: MyValue = db
        .exec_result(
            QueryBuilder::select()
                .values(MyValue::db_keys())
                .ids(1)
                .query(),
        )
        .try_into()
        .unwrap();

    assert_eq!(&my_value, &db_value);

    let db_values: Vec<MyValue> = db
        .exec_result(
            QueryBuilder::select()
                .values(MyValue::db_keys())
                .ids([1, 2])
                .query(),
        )
        .try_into()
        .unwrap();

    assert_eq!(db_values, vec![my_value.clone(), my_value]);
}

#[test]
fn select_custom_value_with_id() {
    #[derive(Debug, Clone, PartialEq, DbType)]
    struct MyValue {
        db_id: Option<DbId>,
        name: String,
        age: u64,
    }

    let mut db = TestDb::new();
    let my_value = MyValue {
        db_id: None,
        name: "my name".to_string(),
        age: 20,
    };

    db.exec_mut(
        QueryBuilder::insert()
            .nodes()
            .count(2)
            .values_uniform(&my_value)
            .query(),
        2,
    );

    let db_values: Vec<MyValue> = db
        .exec_result(
            QueryBuilder::select()
                .values(MyValue::db_keys())
                .ids([1, 2])
                .query(),
        )
        .try_into()
        .unwrap();

    assert_eq!(
        db_values,
        vec![
            MyValue {
                db_id: Some(DbId(1)),
                name: "my name".to_string(),
                age: 20
            },
            MyValue {
                db_id: Some(DbId(2)),
                name: "my name".to_string(),
                age: 20
            }
        ]
    );
}

#[test]
fn insert_single_element() {
    #[derive(Debug, Clone, PartialEq, DbType)]
    struct MyValue {
        db_id: Option<DbId>,
        name: String,
        age: u64,
    }

    let mut db = TestDb::new();
    let my_value = MyValue {
        db_id: None,
        name: "my name".to_string(),
        age: 20,
    };

    db.exec_mut(
        QueryBuilder::insert()
            .nodes()
            .count(1)
            .values_uniform(&my_value)
            .query(),
        1,
    );

    let mut db_value: MyValue = db
        .exec_result(
            QueryBuilder::select()
                .values(MyValue::db_keys())
                .ids(1)
                .query(),
        )
        .try_into()
        .unwrap();

    db_value.age = 30;

    db.exec_mut(QueryBuilder::insert().element(&db_value).query(), 2);

    let other: MyValue = db
        .exec_result(
            QueryBuilder::select()
                .values(MyValue::db_keys())
                .ids(1)
                .query(),
        )
        .try_into()
        .unwrap();

    assert_eq!(other, db_value);
}

#[test]
fn insert_multiple_elements() {
    #[derive(Debug, Clone, PartialEq, DbType)]
    struct MyValue {
        db_id: Option<DbId>,
        name: String,
        age: u64,
    }

    let mut db = TestDb::new();
    let my_value = MyValue {
        db_id: None,
        name: "my name".to_string(),
        age: 20,
    };

    db.exec_mut(
        QueryBuilder::insert()
            .nodes()
            .count(2)
            .values_uniform(&my_value)
            .query(),
        2,
    );

    let mut db_values: Vec<MyValue> = db
        .exec_result(
            QueryBuilder::select()
                .values(MyValue::db_keys())
                .ids([1, 2])
                .query(),
        )
        .try_into()
        .unwrap();

    db_values[0].age = 30;
    db_values[1].age = 40;

    db.exec_mut(QueryBuilder::insert().elements(&db_values).query(), 4);

    let other: Vec<MyValue> = db
        .exec_result(
            QueryBuilder::select()
                .values(MyValue::db_keys())
                .ids([1, 2])
                .query(),
        )
        .try_into()
        .unwrap();

    assert_eq!(other, db_values);
}

#[test]
fn derived_macro_should_not_panic() {
    let mut db = TestDb::new();

    #[derive(Debug, DbType)]
    struct User {
        value: u64,
    }

    db.exec_mut(
        QueryBuilder::insert()
            .nodes()
            .values([User { value: 0 }.to_db_values()])
            .query(),
        1,
    );

    let user: std::result::Result<User, DbError> = db
        .exec_result(QueryBuilder::search().from(1).query())
        .try_into();

    assert!(user.is_err());
    assert_eq!(user.unwrap_err().description, "Key 'value' not found");
}

#[test]
fn try_from_db_element() {
    let element = DbElement {
        id: DbId(1),
        from: DbId::default(),
        to: DbId::default(),
        values: vec![
            ("user_id", 100_u64).into(),
            ("password", "pswd").into(),
            ("status", Status::Active).into(),
        ],
    };

    let user: User = (&element).try_into().unwrap();

    assert_eq!(user.user_id, 100);
    assert_eq!(user.status, Status::Active);
    assert_eq!(user.password, "pswd");
}

#[test]
fn insert_element_alias_update_values() {
    let mut db = TestDb::new();
    let my_value = MyValue {
        db_id: Some("my_alias".into()),
        name: "my name".to_string(),
        age: 20,
    };
    db.exec_mut(
        QueryBuilder::insert().nodes().aliases("my_alias").query(),
        1,
    );
    db.exec_mut(QueryBuilder::insert().element(&my_value).query(), 2);
    db.exec_elements(
        QueryBuilder::select().ids(1).query(),
        &[DbElement {
            id: DbId(1),
            from: DbId::default(),
            to: DbId::default(),
            values: vec![("name", "my name").into(), ("age", 20_u64).into()],
        }],
    );
}

#[test]
fn insert_element_alias_new() {
    let mut db = TestDb::new();
    let my_value = MyValue {
        db_id: Some("my_alias".into()),
        name: "my name".to_string(),
        age: 20,
    };
    db.exec_mut(QueryBuilder::insert().element(&my_value).query(), 2);
    db.exec_elements(
        QueryBuilder::select().ids("my_alias").query(),
        &[DbElement {
            id: DbId(1),
            from: DbId::default(),
            to: DbId::default(),
            values: vec![("name", "my name").into(), ("age", 20_u64).into()],
        }],
    );
}

#[test]
fn insert_element_no_id_new_element() {
    let mut db = TestDb::new();
    let my_value = MyValue {
        db_id: None,
        name: "my name".to_string(),
        age: 20,
    };
    assert_eq!(
        db.exec_mut_result(QueryBuilder::insert().element(&my_value).query()),
        QueryResult {
            result: 2,
            elements: vec![DbElement {
                id: DbId(1),
                from: DbId::default(),
                to: DbId::default(),
                values: vec![],
            }]
        }
    );
    db.exec_elements(
        QueryBuilder::select().ids(1).query(),
        &[DbElement {
            id: DbId(1),
            from: DbId::default(),
            to: DbId::default(),
            values: vec![("name", "my name").into(), ("age", 20_u64).into()],
        }],
    );
}

#[test]
fn insert_element_missing_id() {
    let mut db = TestDb::new();
    let my_value = MyValue {
        db_id: Some(1.into()),
        name: "my name".to_string(),
        age: 20,
    };
    db.exec_mut_error(
        QueryBuilder::insert().element(&my_value).query(),
        "Id '1' not found",
    );
}

#[test]
fn insert_element_bool() {
    let mut db = TestDb::new();
    let mut my_value = MyValueWithBool {
        db_id: None,
        name: "my name".to_string(),
        is_active: true,
        truths: vec![true, false],
    };
    db.exec_mut(QueryBuilder::insert().element(&my_value).query(), 3);
    let my_value_from_db: MyValueWithBool = db
        .exec_result(QueryBuilder::select().ids(1).query())
        .try_into()
        .unwrap();
    my_value.db_id = Some(QueryId::Id(DbId(1)));
    assert_eq!(my_value, my_value_from_db);
}

#[test]
fn insert_element_to_bool_conversion() {
    let mut db = TestDb::new();
    db.exec_mut(
        QueryBuilder::insert()
            .nodes()
            .values([[
                ("name", "my name").into(),
                ("is_active", 50).into(),
                ("truths", vec![1, 0]).into(),
            ]])
            .query(),
        1,
    );
    let my_value_from_db: MyValueWithBool = db
        .exec_result(QueryBuilder::select().ids(1).query())
        .try_into()
        .unwrap();
    let expected = MyValueWithBool {
        db_id: Some(QueryId::Id(DbId(1))),
        name: "my name".to_string(),
        is_active: true,
        truths: vec![true, false],
    };
    assert_eq!(expected, my_value_from_db);
}

#[test]
fn insert_vectorized_custom_types() {
    let mut db = TestDb::new();
    let my_type = MyCustomVec {
        vec: vec![Status::Active, Status::Inactive],
        attributes: vec![
            Attribute {
                name: "name".to_string(),
                value: "value".to_string(),
            },
            Attribute {
                name: "name2".to_string(),
                value: "value2".to_string(),
            },
        ],
    };
    db.exec_mut(QueryBuilder::insert().element(&my_type).query(), 2);
    let my_type_from_db: MyCustomVec = db
        .exec_result(QueryBuilder::select().ids(1).query())
        .try_into()
        .unwrap();
    assert_eq!(my_type, my_type_from_db);
}

#[test]
fn select_user_type() {
    let mut db = TestDb::new();
    let mut my_value = MyValue {
        db_id: None,
        name: "my name".to_string(),
        age: 20,
    };
    let result = db.exec_mut_result(QueryBuilder::insert().element(&my_value).query());
    let my_value_from_db: MyValue = db
        .exec_result(QueryBuilder::select().elements::<MyValue>().ids(1).query())
        .try_into()
        .unwrap();
    my_value.db_id = Some(result.elements[0].id.into());
    assert_eq!(my_value, my_value_from_db);
}

#[test]
fn with_option_some() {
    let mut db = TestDb::new();
    let my_value = WithOption {
        name: "my name".to_string(),
        value: Some(20),
    };
    db.exec_mut(QueryBuilder::insert().element(&my_value).query(), 2);
    let my_value_from_db: WithOption = db
        .exec_result(QueryBuilder::select().ids(1).query())
        .try_into()
        .unwrap();
    assert_eq!(my_value, my_value_from_db);
}

#[test]
fn with_option_some_wrong_type() {
    let mut db = TestDb::new();
    let my_value = WithOption {
        name: "my name".to_string(),
        value: Some(20),
    };
    db.exec_mut(QueryBuilder::insert().element(&my_value).query(), 2);
    db.exec_mut(
        QueryBuilder::insert()
            .values([[("value", "string").into()]])
            .ids(1)
            .query(),
        1,
    );
    let err: Result<WithOption, DbError> = db
        .exec_result(QueryBuilder::select().ids(1).query())
        .try_into();
    let err_text = err
        .unwrap_err()
        .description
        .split(". (")
        .next()
        .unwrap()
        .to_owned();

    assert_eq!(
        err_text,
        "Failed to convert value of 'value': [Db:TypeError] Cannot convert 'string' to 'u64'"
    );
}

#[test]
fn with_option_none() {
    let mut db = TestDb::new();
    let my_value = WithOption {
        name: "my name".to_string(),
        value: None,
    };
    db.exec_mut(QueryBuilder::insert().element(&my_value).query(), 1);
    let my_value_from_db: WithOption = db
        .exec_result(QueryBuilder::select().ids(1).query())
        .try_into()
        .unwrap();
    assert_eq!(my_value, my_value_from_db);
}

#[test]
fn with_option_bad_value() {
    let mut db = TestDb::new();
    let my_value = WithOption {
        name: "my name".to_string(),
        value: Some(20),
    };
    db.exec_mut(QueryBuilder::insert().element(&my_value).query(), 2);
    db.exec_mut(
        QueryBuilder::insert()
            .values([[("name", 100).into()]])
            .ids(1)
            .query(),
        1,
    );
    let err: Result<WithOption, DbError> = db
        .exec_result(QueryBuilder::select().ids(1).query())
        .try_into();
    let err_text = err
        .unwrap_err()
        .description
        .split(". (")
        .next()
        .unwrap()
        .to_owned();

    assert_eq!(
        err_text,
        "Failed to convert value of 'name': [Db:TypeError] Cannot convert 'i64' to 'string'"
    );
}

#[test]
fn with_option_missing_value() {
    let mut db = TestDb::new();
    let my_value = WithOption {
        name: "my name".to_string(),
        value: Some(20),
    };
    db.exec_mut(QueryBuilder::insert().element(&my_value).query(), 2);
    db.exec_mut(QueryBuilder::remove().values("name").ids(1).query(), 1);
    let err: Result<WithOption, DbError> = db
        .exec_result(QueryBuilder::select().ids(1).query())
        .try_into();

    assert_eq!(err.unwrap_err().description, "Key 'name' not found");
}

#[test]
fn try_from_db_element_bad_conversion() {
    let element = DbElement {
        id: DbId(1),
        from: DbId::default(),
        to: DbId::default(),
        values: vec![
            ("user_id", 100_u64).into(),
            ("password", 1_i64).into(),
            ("status", Status::Active).into(),
        ],
    };

    let err: Result<User, DbError> = (&element).try_into();
    let err_text = err
        .unwrap_err()
        .description
        .split(". (")
        .next()
        .unwrap()
        .to_owned();

    assert_eq!(
        err_text,
        "Failed to convert value of 'password': [Db:TypeError] Cannot convert 'i64' to 'string'"
    );
}

#[test]
fn derived_serialization_struct() {
    #[derive(DbSerialize, Debug, PartialEq)]
    struct S {
        f1: u64,
        f2: u64,
    }

    let s = S { f1: 1, f2: 2 };
    let serialized = s.serialize();
    let deserialized = S::deserialize(&serialized).unwrap();

    assert_eq!(s, deserialized);
}

#[test]
fn derived_serialization_tuple() {
    #[derive(DbSerialize, Debug, PartialEq)]
    struct S(u64, u64);

    let s = S(1, 2);
    let serialized = s.serialize();
    let deserialized = S::deserialize(&serialized).unwrap();

    assert_eq!(s, deserialized);
}

#[derive(DbSerialize, Debug, PartialEq)]
struct S1 {
    f1: u64,
}

#[derive(DbSerialize, Debug, PartialEq)]
struct S2(S1);

#[derive(DbSerialize, Debug, PartialEq)]
struct S3(S2, S2);

#[derive(DbSerialize, Debug, PartialEq)]
enum MyOtherEnum {
    A,
    B,
}

#[derive(DbSerialize, Debug, PartialEq)]
enum MyE {
    A,
    B(u64),
    C(u64, u64),
    D(S3),
    E(MyOtherEnum),
    F { f1: u64, f2: u64 },
}

#[test]
fn derived_serialization_enum_unit() {
    let s = MyE::A;
    let serialized = s.serialize();
    let deserialized = MyE::deserialize(&serialized).unwrap();

    assert_eq!(s, deserialized);
}

#[test]
fn derived_serialization_enum_tuple() {
    let s = MyE::B(1);
    let serialized = s.serialize();
    let deserialized = MyE::deserialize(&serialized).unwrap();

    assert_eq!(s, deserialized);
}

#[test]
fn derived_serialization_enum_tuple_multiple() {
    let s = MyE::C(1, 2);
    let serialized = s.serialize();
    let deserialized = MyE::deserialize(&serialized).unwrap();

    assert_eq!(s, deserialized);
}

#[test]
fn derived_serialization_enum_nested_struct() {
    let s = MyE::D(S3(S2(S1 { f1: 1 }), S2(S1 { f1: 2 })));
    let serialized = s.serialize();
    let deserialized = MyE::deserialize(&serialized).unwrap();

    assert_eq!(s, deserialized);
}

#[test]
fn derived_serialization_enum_nested_enum() {
    let s = MyE::E(MyOtherEnum::A);
    let serialized = s.serialize();
    let deserialized = MyE::deserialize(&serialized).unwrap();

    assert_eq!(s, deserialized);
}

#[test]
fn derived_serialization_enum_struct() {
    let s = MyE::F { f1: 1, f2: 2 };
    let serialized = s.serialize();
    let deserialized = MyE::deserialize(&serialized).unwrap();

    assert_eq!(s, deserialized);
}

#[test]
fn derive_serialization_empty_struct() {
    #[derive(DbSerialize, PartialEq, Debug)]

    struct S {}

    let s = S {};
    let serialized = s.serialize();
    let deserialized = S::deserialize(&serialized).unwrap();
    assert_eq!(s, deserialized);
}

#[test]
fn derive_db_type_flatten_nested_struct() {
    #[derive(DbType, PartialEq, Debug)]
    struct Flattened {
        db_id: Option<DbId>,
        category: String,
        #[agdb(flatten)]
        custom: MyCustomVec,
    }

    let mut flattened = Flattened {
        db_id: None,
        category: "test".into(),
        custom: MyCustomVec {
            vec: vec![],
            attributes: vec![],
        },
    };

    let mut db = TestDb::new();
    db.exec_mut(QueryBuilder::insert().element(&flattened).query(), 3);

    let keys = db
        .exec_result(QueryBuilder::select().keys().ids(1).query())
        .elements[0]
        .values
        .iter()
        .map(|kv| kv.key.to_string())
        .collect::<Vec<String>>();

    assert_eq!(
        keys,
        vec![
            "category".to_string(),
            "vec".to_string(),
            "attributes".to_string()
        ]
    );

    let retrieved: Flattened = db
        .exec_result(
            QueryBuilder::select()
                .elements::<Flattened>()
                .ids(1)
                .query(),
        )
        .try_into()
        .unwrap();

    flattened.db_id = Some(DbId(1));
    assert_eq!(flattened, retrieved);
}

#[test]
fn derive_db_type_skip_field() {
    #[derive(DbType, PartialEq, Debug)]
    struct Skipped {
        db_id: Option<DbId>,
        category: String,
        #[agdb(skip)]
        custom: MyCustomVec,
    }

    let mut db = TestDb::new();
    let mut skipped = Skipped {
        db_id: None,
        category: "category".to_string(),
        custom: MyCustomVec::default(),
    };
    db.exec_mut(QueryBuilder::insert().element(&skipped).query(), 1);
    let keys = db
        .exec_result(QueryBuilder::select().keys().ids(1).query())
        .elements[0]
        .values
        .iter()
        .map(|kv| kv.key.to_string())
        .collect::<Vec<String>>();

    assert_eq!(keys, vec!["category".to_string()]);

    let retrieved: Skipped = db
        .exec_result(QueryBuilder::select().elements::<Skipped>().ids(1).query())
        .try_into()
        .unwrap();

    skipped.db_id = Some(DbId(1));
    assert_eq!(skipped, retrieved);
}

#[test]
fn derive_db_type_rename_field() {
    #[derive(DbType, PartialEq, Debug)]
    struct Renamed {
        db_id: Option<DbId>,
        #[agdb(rename = "category_name")]
        category: String,
    }

    let mut db = TestDb::new();
    let mut renamed = Renamed {
        db_id: None,
        category: "category".to_string(),
    };
    db.exec_mut(QueryBuilder::insert().element(&renamed).query(), 1);
    let keys = db
        .exec_result(QueryBuilder::select().keys().ids(1).query())
        .elements[0]
        .values
        .iter()
        .map(|kv| kv.key.to_string())
        .collect::<Vec<String>>();

    assert_eq!(keys, vec!["category_name".to_string()]);

    let retrieved: Renamed = db
        .exec_result(QueryBuilder::select().elements::<Renamed>().ids(1).query())
        .try_into()
        .unwrap();

    renamed.db_id = Some(DbId(1));
    assert_eq!(renamed, retrieved);
}

#[test]
fn derive_db_type_skip_generic() {
    #[derive(DbType)]
    struct S {
        db_id: Option<DbId>,
        name: String,
        #[agdb(skip)]
        _generic: std::sync::Arc<u64>,
    }

    let _ = S {
        db_id: None,
        name: "test".to_string(),
        _generic: std::sync::Arc::new(42),
    };
}

#[test]
fn derive_serialize_vec_t() {
    #[derive(DbSerialize)]
    struct MyVec<T: AgdbSerialize> {
        values: Vec<T>,
    }
    let _ = MyVec {
        values: vec![1_u64, 2_u64],
    };
}

#[test]
fn derive_db_value_vec_t() {
    #[derive(DbValue, DbSerialize)]
    struct MyVec<T: AgdbSerialize> {
        values: Vec<T>,
    }
}

#[test]
fn derive_db_type_db_id_no_option() {
    #[derive(DbType, PartialEq)]
    struct S {
        db_id: DbId,
        name: String,
    }

    let mut db = TestDb::new();
    db.exec_mut(
        QueryBuilder::insert()
            .element(&S {
                db_id: DbId::default(),
                name: "name".to_string(),
            })
            .query(),
        1,
    );
    let s: S = db
        .exec_result(QueryBuilder::select().elements::<S>().ids(1).query())
        .try_into()
        .unwrap();
    assert_eq!(s.db_id, DbId(1));
    assert_eq!(s.name, "name");
}

#[test]
fn derive_db_element() {
    #[derive(DbElement)]
    struct Type1 {
        db_id: DbId,
        name: String,
    }

    #[derive(DbElement)]
    struct Type2 {
        db_id: DbId,
        name: String,
    }

    let mut db = TestDb::new();
    let root_id = db
        .exec_mut_result(QueryBuilder::insert().nodes().aliases("root").query())
        .elements[0]
        .id;
    let ty1 = db
        .exec_mut_result(
            QueryBuilder::insert()
                .element(&Type1 {
                    db_id: DbId::default(),
                    name: "type1".to_string(),
                })
                .query(),
        )
        .elements[0]
        .id;
    let ty2 = db
        .exec_mut_result(
            QueryBuilder::insert()
                .element(&Type2 {
                    db_id: DbId::default(),
                    name: "type2".to_string(),
                })
                .query(),
        )
        .elements[0]
        .id;
    db.exec_mut(
        QueryBuilder::insert()
            .edges()
            .from(root_id)
            .to([ty1, ty2])
            .query(),
        2,
    );

    let ty1_result: Vec<Type1> = db
        .exec_result(
            QueryBuilder::select()
                .elements::<Type1>()
                .search()
                .from("root")
                .query(),
        )
        .try_into()
        .unwrap();

    assert_eq!(ty1_result.len(), 1);
    assert_eq!(ty1_result[0].name, "type1");
}

#[test]
fn insert_element_by_value() {
    #[derive(DbElement)]
    struct Type1 {
        name: String,
    }

    let mut db = TestDb::new();

    db.exec_mut(
        QueryBuilder::insert()
            .element(Type1 {
                name: "test".to_string(),
            })
            .query(),
        2,
    );
}

#[test]
fn implicit_and_explicit_condition_with_db_element_derive() {
    #[derive(Debug, DbElement)]
    struct S {
        title: String,
    }

    let mut db = DbMemory::new("").unwrap();
    db.exec_mut(QueryBuilder::insert().nodes().aliases("root").query())
        .unwrap();
    db.exec_mut(
        QueryBuilder::insert()
            .element(S {
                title: "test".to_string(),
            })
            .query(),
    )
    .unwrap();
    db.exec_mut(QueryBuilder::insert().edges().from("root").to(2).query())
        .unwrap();

    let _result: S = db
        .exec(
            QueryBuilder::select()
                .elements::<S>()
                .search()
                .from("root")
                .where_()
                .node()
                .or()
                .edge()
                .query(),
        )
        .unwrap()
        .try_into()
        .unwrap();
}
