#![allow(unused_imports, dead_code)]

pub mod common;

#[path = "partial_model_nested/local/mod.rs"]
mod local;

pub use common::{
    TestContext,
    bakery_chain::{create_tables, seed_data},
    bakery_dense::*,
    setup::*,
};
use sea_orm::{DbConn, DbErr, EntityLoaderTrait, RuntimeErr, Set, prelude::*, query::*};

#[sea_orm_macros::test]
fn cake_entity_loader() -> Result<(), DbErr> {
    use common::bakery_dense::prelude::*;
    use sea_orm::compound::EntityLoaderTrait;

    let ctx = TestContext::new("test_cake_entity_loader");
    let db = &ctx.db;
    create_tables(db)?;

    let bakery_1 = insert_bakery(db, "SeaSide Bakery")?;
    let bakery_2 = insert_bakery(db, "LakeSide Bakery")?;

    let baker_1 = insert_baker(db, "Jane", bakery_1.id)?;
    let baker_2 = insert_baker(db, "Peter", bakery_1.id)?;
    let baker_3 = insert_baker(db, "Fred", bakery_2.id)?; // does not make cake

    let cake_1 = insert_cake(db, "Cheesecake", Some(bakery_1.id))?;
    let cake_2 = insert_cake(db, "Coffee", Some(bakery_1.id))?;
    let cake_3 = insert_cake(db, "Chiffon", Some(bakery_2.id))?;
    let cake_4 = insert_cake(db, "Apple Pie", None)?; // no one makes apple pie

    insert_cake_baker(db, baker_1.id, cake_1.id)?;
    insert_cake_baker(db, baker_1.id, cake_2.id)?;
    insert_cake_baker(db, baker_2.id, cake_2.id)?;
    insert_cake_baker(db, baker_2.id, cake_3.id)?;

    let cakes = Cake::load().all(db)?;
    assert_eq!(
        cakes,
        [
            cake_1.clone(),
            cake_2.clone(),
            cake_3.clone(),
            cake_4.clone(),
        ]
    );

    let cakes = Cake::load().filter(Cake::COLUMN.name.like("Ch%")).all(db)?;
    assert_eq!(cakes, [cake_1.clone(), cake_3.clone()]);
    assert!(cakes[0].bakers.is_empty());
    assert!(cakes[0].bakery.is_unloaded());

    assert_eq!(
        Cake::load()
            .filter(Cake::COLUMN.name.like("Ch%"))
            .order_by_desc(Cake::COLUMN.name)
            .one(db)?
            .unwrap(),
        cake_3
    );

    let cakes = Cake::load().with(Bakery).all(db)?;
    assert_eq!(
        cakes,
        [
            cake_1.clone(),
            cake_2.clone(),
            cake_3.clone(),
            cake_4.clone(),
        ]
    );
    assert_eq!(cakes[0].bakery.as_ref().unwrap(), &bakery_1);
    assert_eq!(cakes[1].bakery.as_ref().unwrap(), &bakery_1);
    assert_eq!(cakes[2].bakery.as_ref().unwrap(), &bakery_2);
    assert!(cakes[3].bakery.is_not_found());

    // low-level API
    assert_eq!(
        cakes,
        cake::EntityLoader::load(
            Cake::load().all(db)?,
            &cake::EntityLoaderWith {
                bakery: true,
                lineitems: false,
                bakers: false,
            },
            &Default::default(),
            db
        )?
    );

    let cake_with_bakery = Cake::load()
        .filter(Cake::COLUMN.name.eq("Cheesecake"))
        .with(Bakery)
        .one(db)?
        .unwrap();
    assert_eq!(cake_with_bakery, cake_1);
    assert_eq!(cake_with_bakery.bakery.unwrap(), bakery_1);
    assert!(cake_with_bakery.bakers.is_empty());

    assert_eq!(
        Cake::load()
            .filter_by_id(cake_2.id)
            .with(Bakery)
            .one(db)?
            .unwrap(),
        {
            let mut cake_2 = cake_2.clone().into_ex();
            cake_2.bakery = BelongsTo::loaded(Some(bakery_1.clone()));
            cake_2
        }
    );

    let cakes = Cake::load().with(Bakery).with(Baker).all(db)?;
    assert_eq!(
        cakes,
        [
            cake_1.clone(),
            cake_2.clone(),
            cake_3.clone(),
            cake_4.clone()
        ]
    );
    assert_eq!(cakes[0].bakery.as_ref().unwrap(), &bakery_1);
    assert_eq!(cakes[1].bakery.as_ref().unwrap(), &bakery_1);
    assert_eq!(cakes[2].bakery.as_ref().unwrap(), &bakery_2);
    assert!(cakes[3].bakery.is_not_found());
    assert_eq!(cakes[0].bakers, [baker_1.clone()]);
    assert_eq!(cakes[1].bakers, [baker_1.clone(), baker_2.clone()]);
    assert_eq!(cakes[2].bakers, [baker_2.clone()]);
    assert!(cakes[3].bakers.is_empty());

    let cake_with_bakery_baker = Cake::load()
        .filter(Cake::COLUMN.name.eq("Chiffon"))
        .with(Bakery)
        .with(Baker)
        .one(db)?
        .unwrap();
    assert_eq!(cake_with_bakery_baker, cake_3);
    assert_eq!(cake_with_bakery_baker.bakery.unwrap(), bakery_2);
    assert_eq!(cake_with_bakery_baker.bakers, [baker_2.clone()]);

    // start again from baker

    let bakers = baker::Entity::find().all(db)?;
    assert_eq!(bakers, [baker_1.clone(), baker_2.clone(), baker_3.clone()]);

    let bakers = baker::Entity::load().with(cake::Entity).all(db)?;
    assert_eq!(bakers[0].id, baker_1.id);
    assert_eq!(bakers[1].id, baker_2.id);
    assert_eq!(bakers[2].id, baker_3.id);
    assert_eq!(bakers[0].cakes, [cake_1.clone(), cake_2.clone()]);
    assert_eq!(bakers[1].cakes, [cake_2.clone(), cake_3.clone()]);
    assert!(bakers[2].cakes.is_empty());

    // alternative API
    assert_eq!(
        bakers,
        baker::EntityLoader::load(
            baker::Entity::load().all(db)?,
            &baker::EntityLoaderWith {
                cakes: true,
                bakery: false,
            },
            &Default::default(),
            db
        )?
    );

    // 2 many
    // bakery -> baker
    //        -> cake

    let bakeries = bakery::Entity::load()
        .with(Baker)
        .with(cake::Entity)
        .order_by_asc(bakery::Column::Id)
        .all(db)?;

    assert_eq!(bakeries[0].bakers, [baker_1.clone(), baker_2.clone()]);
    assert_eq!(bakeries[1].bakers, [baker_3.clone()]);
    assert_eq!(bakeries[0].cakes, [cake_1.clone(), cake_2.clone()]);
    assert_eq!(bakeries[1].cakes, [cake_3.clone()]);

    // nested
    // cake -> bakery -> baker

    let cakes = Cake::load().with((Bakery, Baker)).all(db)?;
    assert_eq!(cakes[0].bakery.as_ref().unwrap().name, bakery_1.name);
    assert_eq!(cakes[1].bakery.as_ref().unwrap().name, bakery_1.name);
    assert_eq!(cakes[2].bakery.as_ref().unwrap().name, bakery_2.name);
    assert!(cakes[3].bakery.is_not_found());
    assert_eq!(
        cakes[0].bakery.as_ref().unwrap().bakers,
        [baker_1.clone(), baker_2.clone()]
    );
    assert_eq!(
        cakes[1].bakery.as_ref().unwrap().bakers,
        [baker_1.clone(), baker_2.clone()]
    );
    assert_eq!(cakes[2].bakery.as_ref().unwrap().bakers, [baker_3.clone()]);

    Ok(())
}

#[sea_orm_macros::test]
fn entity_loader_join_three() {
    let ctx = TestContext::new("entity_loader_join_three");
    create_tables(&ctx.db).unwrap();

    seed_data::init_1(&ctx, true);

    let db = &ctx.db;

    // verify basics
    let cake_13 = cake::Entity::find_by_id(13).one(db).unwrap().unwrap();
    let cake_15 = cake::Entity::find_by_id(15).one(db).unwrap().unwrap();

    // new find by key feature
    #[rustfmt::skip]
    assert_eq!(cake::Entity::find_by_name("Cheesecake").one(db).unwrap().unwrap().id, 13);
    #[rustfmt::skip]
    assert_eq!(cake::Entity::find_by_name("Chocolate").one(db).unwrap().unwrap().id, 15);

    // new load by key feature
    #[rustfmt::skip]
    assert_eq!(cake::Entity::load().filter_by_name("Cheesecake").one(db).unwrap().unwrap().id, 13);
    #[rustfmt::skip]
    assert_eq!(cake::Entity::load().filter_by_name("Chocolate").one(db).unwrap().unwrap().id, 15);

    let lineitems = lineitem::Entity::load().all(db).unwrap();
    assert_eq!(lineitems[0].cake_id, 13);
    assert_eq!(lineitems[1].cake_id, 15);
    assert_eq!(lineitems[0].order_id, 101);
    assert_eq!(lineitems[1].order_id, 101);

    // lineitem join order
    let lineitems = lineitem::Entity::load()
        .with(order::Entity)
        .all(db)
        .unwrap();
    assert_eq!(lineitems[0].order.as_ref().unwrap().id, 101);
    assert_eq!(lineitems[0].order.as_ref().unwrap().total, 10.into());
    assert_eq!(lineitems[1].order.as_ref().unwrap().id, 101);
    assert_eq!(lineitems[1].order.as_ref().unwrap().total, 10.into());

    // lineitem join cake
    let lineitems = lineitem::Entity::load().with(cake::Entity).all(db).unwrap();
    assert_eq!(lineitems[0].cake.as_ref().unwrap().id, 13);
    assert_eq!(lineitems[0].cake.as_ref().unwrap().name, "Cheesecake");
    assert_eq!(lineitems[1].cake.as_ref().unwrap().id, 15);
    assert_eq!(lineitems[1].cake.as_ref().unwrap().name, "Chocolate");

    // lineitem join order + cake
    let lineitems = lineitem::Entity::load()
        .with(order::Entity)
        .with(cake::Entity)
        .all(db)
        .unwrap();
    assert_eq!(lineitems[0].order.as_ref().unwrap().id, 101);
    assert_eq!(lineitems[0].order.as_ref().unwrap().total, 10.into());
    assert_eq!(lineitems[1].order.as_ref().unwrap().id, 101);
    assert_eq!(lineitems[1].order.as_ref().unwrap().total, 10.into());
    assert_eq!(lineitems[0].cake.as_ref().unwrap().id, 13);
    assert_eq!(lineitems[0].cake.as_ref().unwrap().name, "Cheesecake");
    assert_eq!(lineitems[1].cake.as_ref().unwrap().id, 15);
    assert_eq!(lineitems[1].cake.as_ref().unwrap().name, "Chocolate");

    // 1 layer select
    let order = order::Entity::load()
        .with(customer::Entity)
        .order_by_asc(order::Column::Id)
        .one(db)
        .unwrap()
        .unwrap();

    assert_eq!(
        order,
        order::ModelEx {
            id: 101,
            total: 10.into(),
            bakery_id: 42,
            customer_id: 11,
            placed_at: "2020-01-01 00:00:00Z".parse().unwrap(),
            bakery: BelongsTo::Unloaded,
            customer: BelongsTo::loaded(customer::Model {
                id: 11,
                name: "Bob".to_owned(),
                notes: Some("Sweet tooth".into()),
            }),
            lineitems: HasMany::Unloaded,
        }
    );

    // 1 layer select
    let order = order::Entity::load()
        .with(bakery::Entity)
        .with(customer::Entity)
        .order_by_asc(order::Column::Id)
        .one(db)
        .unwrap()
        .unwrap();

    assert_eq!(
        order,
        order::ModelEx {
            id: 101,
            total: 10.into(),
            bakery_id: 42,
            customer_id: 11,
            placed_at: "2020-01-01 00:00:00Z".parse().unwrap(),
            bakery: BelongsTo::loaded(bakery::Model {
                id: 42,
                name: "cool little bakery".into(),
                profit_margin: 4.1,
            }),
            customer: BelongsTo::loaded(customer::ModelEx {
                id: 11,
                name: "Bob".to_owned(),
                notes: Some("Sweet tooth".into()),
                orders: HasMany::Unloaded,
            }),
            lineitems: HasMany::Unloaded,
        }
    );

    // 1 layer select
    let order = order::Entity::load()
        .with(customer::Entity)
        .with(lineitem::Entity)
        .order_by_asc(order::Column::Id)
        .one(db)
        .unwrap()
        .unwrap();

    assert_eq!(
        order,
        order::ModelEx {
            id: 101,
            total: 10.into(),
            bakery_id: 42,
            customer_id: 11,
            placed_at: "2020-01-01 00:00:00Z".parse().unwrap(),
            bakery: BelongsTo::Unloaded,
            customer: BelongsTo::loaded(customer::ModelEx {
                id: 11,
                name: "Bob".to_owned(),
                notes: Some("Sweet tooth".into()),
                orders: HasMany::Unloaded,
            }),
            lineitems: HasMany::Loaded(vec![
                lineitem::ModelEx {
                    id: 1,
                    price: 2.into(),
                    quantity: 2,
                    order_id: 101,
                    cake_id: 13,
                    order: BelongsTo::Unloaded,
                    cake: BelongsTo::Unloaded,
                },
                lineitem::ModelEx {
                    id: 2,
                    price: 3.into(),
                    quantity: 2,
                    order_id: 101,
                    cake_id: 15,
                    order: BelongsTo::Unloaded,
                    cake: BelongsTo::Unloaded,
                }
            ]),
        }
    );

    // 2 layers
    let order = order::Entity::load()
        .with(customer::Entity)
        .with((lineitem::Entity, cake::Entity))
        .order_by_asc(order::Column::Id)
        .one(db)
        .unwrap()
        .unwrap();

    assert_eq!(
        order,
        order::ModelEx {
            id: 101,
            total: 10.into(),
            bakery_id: 42,
            customer_id: 11,
            placed_at: "2020-01-01 00:00:00Z".parse().unwrap(),
            bakery: BelongsTo::Unloaded,
            customer: BelongsTo::loaded(customer::Model {
                id: 11,
                name: "Bob".to_owned(),
                notes: Some("Sweet tooth".into()),
            }),
            lineitems: HasMany::Loaded(vec![
                lineitem::ModelEx {
                    id: 1,
                    price: 2.into(),
                    quantity: 2,
                    order_id: 101,
                    cake_id: 13,
                    order: BelongsTo::Unloaded,
                    cake: BelongsTo::loaded(cake_13),
                },
                lineitem::ModelEx {
                    id: 2,
                    price: 3.into(),
                    quantity: 2,
                    order_id: 101,
                    cake_id: 15,
                    order: BelongsTo::Unloaded,
                    cake: BelongsTo::loaded(cake_15),
                }
            ]),
        }
    );
}

#[sea_orm_macros::test]
fn entity_loader_self_join() -> Result<(), DbErr> {
    use common::film_store::{staff, staff_compact, staff_mono};

    let ctx = TestContext::new("entity_loader_self_join");
    let db = &ctx.db;

    db.get_schema_builder().register(staff::Entity).apply(db)?;

    let alan = staff::ActiveModel {
        name: Set("Alan".into()),
        reports_to_id: Set(None),
        ..Default::default()
    }
    .insert(db)?;

    staff::ActiveModel {
        name: Set("Ben".into()),
        reports_to_id: Set(Some(alan.id)),
        ..Default::default()
    }
    .insert(db)?;

    staff::ActiveModel {
        name: Set("Alice".into()),
        reports_to_id: Set(Some(alan.id)),
        ..Default::default()
    }
    .insert(db)?;

    staff::ActiveModel {
        name: Set("Elle".into()),
        reports_to_id: Set(None),
        ..Default::default()
    }
    .insert(db)?;

    // load belongs_to

    let staff = staff::Entity::load()
        .with(staff::Relation::ReportsTo)
        .all(db)?;

    assert_eq!(staff[0].name, "Alan");
    assert!(staff[0].reports_to.is_unloaded_or_not_found());

    assert_eq!(staff[1].name, "Ben");
    assert_eq!(staff[1].reports_to.as_ref().unwrap().name, "Alan");

    assert_eq!(staff[2].name, "Alice");
    assert_eq!(staff[2].reports_to.as_ref().unwrap().name, "Alan");

    assert_eq!(staff[3].name, "Elle");
    assert!(staff[3].reports_to.is_unloaded_or_not_found());

    // load belongs_to reverse

    let staff = staff::Entity::load()
        .with(staff::Relation::Manages)
        .all(db)?;

    assert_eq!(staff[0].name, "Alan");
    assert_eq!(staff[0].manages[0].name, "Ben");
    assert_eq!(staff[0].manages[1].name, "Alice");

    assert_eq!(staff[1].name, "Ben");
    assert!(staff[1].manages.is_empty());

    assert_eq!(staff[2].name, "Alice");
    assert!(staff[2].manages.is_empty());

    assert_eq!(staff[3].name, "Elle");
    assert!(staff[3].manages.is_empty());

    // load both sides

    let staff = staff::Entity::load()
        .with(staff::Relation::ReportsTo)
        .with(staff::Relation::Manages)
        .all(db)?;

    assert_eq!(staff[0].name, "Alan");
    assert!(staff[0].reports_to.is_unloaded_or_not_found());
    assert_eq!(staff[0].manages[0].name, "Ben");
    assert_eq!(staff[0].manages[1].name, "Alice");

    assert_eq!(staff[1].name, "Ben");
    assert_eq!(staff[1].reports_to.as_ref().unwrap().name, "Alan");
    assert!(staff[1].manages.is_empty());

    assert_eq!(staff[2].name, "Alice");
    assert_eq!(staff[2].reports_to.as_ref().unwrap().name, "Alan");
    assert!(staff[2].manages.is_empty());

    assert_eq!(staff[3].name, "Elle");
    assert!(staff[3].reports_to.is_unloaded_or_not_found());
    assert!(staff[3].manages.is_empty());

    // test self_ref on model without reverse relation (not dual)

    let staff = staff_mono::Entity::load()
        .filter_by_id(2)
        .with(staff_mono::Relation::ReportsTo)
        .one(db)?
        .unwrap();

    assert_eq!(staff.name, "Ben");
    assert_eq!(
        staff.reports_to.unwrap(),
        staff_mono::Entity::find_by_id(alan.id).one(db)?.unwrap()
    );

    // test self_ref on compact_model

    let staff = staff_compact::Entity::load()
        .with(staff_compact::Relation::ReportsTo)
        .with(staff_compact::Relation::Manages)
        .all(db)?;

    assert_eq!(staff[0].name, "Alan");
    assert!(staff[0].reports_to.is_unloaded_or_not_found());
    assert_eq!(staff[0].manages[0].name, "Ben");
    assert_eq!(staff[0].manages[1].name, "Alice");

    assert_eq!(staff[1].name, "Ben");
    assert_eq!(staff[1].reports_to.as_ref().unwrap().name, "Alan");
    assert!(staff[1].manages.is_empty());

    assert_eq!(staff[2].name, "Alice");
    assert_eq!(staff[2].reports_to.as_ref().unwrap().name, "Alan");
    assert!(staff[2].manages.is_empty());

    assert_eq!(staff[3].name, "Elle");
    assert!(staff[3].reports_to.is_unloaded_or_not_found());
    assert!(staff[3].manages.is_empty());

    // test pagination on loader

    let mut pager = staff::Entity::load()
        .with(staff::Relation::ReportsTo)
        .order_by_asc(staff::COLUMN.id)
        .paginate(db, 2);

    let staff = pager.fetch_and_next()?.unwrap();

    assert_eq!(staff[0].name, "Alan");
    assert!(staff[0].reports_to.is_unloaded_or_not_found());

    assert_eq!(staff[1].name, "Ben");
    assert_eq!(staff[1].reports_to.as_ref().unwrap().name, "Alan");

    let staff = pager.fetch_and_next()?.unwrap();

    assert_eq!(staff[0].name, "Alice");
    assert_eq!(staff[0].reports_to.as_ref().unwrap().name, "Alan");

    assert_eq!(staff[1].name, "Elle");
    assert!(staff[1].reports_to.is_unloaded_or_not_found());

    assert!(pager.fetch_and_next()?.is_none());

    Ok(())
}

#[sea_orm_macros::test]
fn entity_loader_self_join_via() -> Result<(), DbErr> {
    use common::blogger::{profile, user, user_follower, user_mono};

    let ctx = TestContext::new("test_entity_loader_self_join_via");
    let db = &ctx.db;

    db.get_schema_builder()
        .register(profile::Entity)
        .register(user::Entity)
        .register(user_follower::Entity)
        .apply(db)?;

    let alice = user::ActiveModel::builder()
        .set_name("Alice")
        .set_email("@1")
        .set_profile(profile::ActiveModel::builder().set_picture("Alice.jpg"))
        .insert(db)?;

    let bob = user::ActiveModel::builder()
        .set_name("Bob")
        .set_email("@2")
        .set_profile(profile::ActiveModel::builder().set_picture("Bob.jpg"))
        .insert(db)?;

    let sam = user::ActiveModel::builder()
        .set_name("Sam")
        .set_email("@3")
        .set_profile(profile::ActiveModel::builder().set_picture("Sam.jpg"))
        .insert(db)?;

    user_follower::ActiveModel {
        user_id: Set(alice.id),
        follower_id: Set(bob.id),
    }
    .insert(db)?;

    user_follower::ActiveModel {
        user_id: Set(alice.id),
        follower_id: Set(sam.id),
    }
    .insert(db)?;

    user_follower::ActiveModel {
        user_id: Set(bob.id),
        follower_id: Set(sam.id),
    }
    .insert(db)?;

    // test user + follower

    let users = user::Entity::load().with(user_follower::Entity).all(db)?;

    assert_eq!(users[0].name, alice.name);
    assert_eq!(users[0].followers.len(), 2);
    assert_eq!(users[0].followers[0].name, bob.name);
    assert_eq!(users[0].followers[1].name, sam.name);

    assert_eq!(users[1].name, bob.name);
    assert_eq!(users[1].followers.len(), 1);
    assert_eq!(users[1].followers[0].name, sam.name);

    assert_eq!(users[2].name, sam.name);
    assert!(users[2].followers.is_empty());

    // test user + follower, but on an Entity without reverse relation (following)

    let users = user_mono::Entity::load()
        .with(user_follower::Entity)
        .all(db)?;

    assert_eq!(users[0].name, alice.name);
    assert_eq!(users[0].followers.len(), 2);
    assert_eq!(users[0].followers[0].name, bob.name);
    assert_eq!(users[0].followers[1].name, sam.name);

    assert_eq!(users[1].name, bob.name);
    assert_eq!(users[1].followers.len(), 1);
    assert_eq!(users[1].followers[0].name, sam.name);

    assert_eq!(users[2].name, sam.name);
    assert!(users[2].followers.is_empty());

    // test user + follower + following (both sides)

    let users = user::Entity::load()
        .with(user::Follower)
        .with(user::Following)
        .all(db)?;

    assert_eq!(users[0].name, alice.name);
    assert_eq!(users[0].followers.len(), 2);
    assert_eq!(users[0].followers[0].name, bob.name);
    assert_eq!(users[0].followers[1].name, sam.name);
    assert!(users[0].following.is_empty());

    assert_eq!(users[1].name, bob.name);
    assert_eq!(users[1].followers.len(), 1);
    assert_eq!(users[1].followers[0].name, sam.name);
    assert_eq!(users[1].following.len(), 1);
    assert_eq!(users[1].following[0].name, alice.name);

    assert_eq!(users[2].name, sam.name);
    assert!(users[2].followers.is_empty());
    assert_eq!(users[2].following.len(), 2);
    assert_eq!(users[2].following[0].name, alice.name);
    assert_eq!(users[2].following[1].name, bob.name);

    // test user + profile

    let users = user::Entity::load()
        .with(profile::Entity)
        .with(user_follower::Entity)
        .all(db)?;

    assert_eq!(users[0].profile, alice.profile);
    assert_eq!(users[0].followers.len(), 2);
    assert_eq!(users[0].followers[0].name, bob.name);
    assert_eq!(users[0].followers[1].name, sam.name);

    assert_eq!(users[1].profile, bob.profile);
    assert_eq!(users[1].followers.len(), 1);
    assert_eq!(users[1].followers[0].name, sam.name);

    assert_eq!(users[2].profile, sam.profile);
    assert!(users[2].followers.is_empty());

    // test user + profile with nested user + profile

    let users = user::Entity::load()
        .with(profile::Entity)
        .with((user::Follower, profile::Entity))
        .all(db)?;

    assert_eq!(users[0].profile, alice.profile);
    assert_eq!(users[0].followers.len(), 2);
    assert_eq!(users[0].followers[0], bob);
    assert_eq!(users[0].followers[1], sam);

    assert_eq!(users[1].profile, bob.profile);
    assert_eq!(users[1].followers.len(), 1);
    assert_eq!(users[1].followers[0], sam);

    assert_eq!(users[2].profile, sam.profile);
    assert!(users[2].followers.is_empty());

    // test all: user profile + follower profile + following profile

    let users = user::Entity::load()
        .with(profile::Entity)
        .with((user::Follower, profile::Entity))
        .with((user::Following, profile::Entity))
        .all(db)?;

    assert_eq!(users[0].profile, alice.profile);
    assert_eq!(users[0].followers.len(), 2);
    assert_eq!(users[0].followers[0], bob);
    assert_eq!(users[0].followers[1], sam);
    assert!(users[0].following.is_empty());

    assert_eq!(users[1].profile, bob.profile);
    assert_eq!(users[1].followers.len(), 1);
    assert_eq!(users[1].followers[0], sam);
    assert_eq!(users[1].following.len(), 1);
    assert_eq!(users[1].following[0], alice);

    assert_eq!(users[2].profile, sam.profile);
    assert!(users[2].followers.is_empty());
    assert_eq!(users[2].following.len(), 2);
    assert_eq!(users[2].following[0], alice);
    assert_eq!(users[2].following[1], bob);

    // test nested loading, but left right swapped

    let alice_profile = profile::Entity::load()
        .filter_by_id(alice.profile.as_ref().unwrap().id)
        .with(sea_orm::compound::EntityLoaderWithSelf(
            user::Entity,
            user::Follower,
        ))
        .one(db)?
        .unwrap();

    assert_eq!(
        alice_profile.picture,
        alice.profile.as_ref().unwrap().picture
    );
    assert_eq!(alice_profile.user.as_ref().unwrap().followers.len(), 2);
    assert_eq!(
        alice_profile.user.as_ref().unwrap().followers[0].name,
        bob.name
    );
    assert_eq!(
        alice_profile.user.as_ref().unwrap().followers[1].name,
        sam.name
    );

    let sam_profile = profile::Entity::load()
        .filter_by_id(sam.profile.as_ref().unwrap().id)
        .with(sea_orm::compound::EntityLoaderWithSelfRev(
            user::Entity,
            user::Following,
        ))
        .one(db)?
        .unwrap();
    assert_eq!(sam_profile.picture, sam.profile.as_ref().unwrap().picture);
    assert_eq!(sam_profile.user.as_ref().unwrap().following.len(), 2);
    assert_eq!(
        sam_profile.user.as_ref().unwrap().following[0].name,
        alice.name
    );
    assert_eq!(
        sam_profile.user.as_ref().unwrap().following[1].name,
        bob.name
    );

    Ok(())
}

#[sea_orm_macros::test]
fn entity_loader_diamond_relations() -> Result<(), DbErr> {
    use local::{bakery as diamond_bakery, create_tables, worker};

    let ctx = TestContext::new("entity_loader_diamond_relations");
    create_tables(&ctx.db)?;

    let db = &ctx.db;

    let bob = worker::ActiveModel {
        name: Set("Bob".to_owned()),
        ..Default::default()
    }
    .insert(db)?;

    let alice = worker::ActiveModel {
        name: Set("Alice".to_owned()),
        ..Default::default()
    }
    .insert(db)?;

    let yum = diamond_bakery::ActiveModel {
        name: Set("YumBakery".to_owned()),
        profit_margin: Set(4.1),
        manager_id: Set(bob.id),
        cashier_id: Set(alice.id),
        ..Default::default()
    }
    .insert(db)?;

    let yum = diamond_bakery::Entity::load()
        .filter_by_id(yum.id)
        .with(diamond_bakery::Relation::Manager)
        .with(diamond_bakery::Relation::Cashier)
        .one(db)?
        .unwrap();

    assert_eq!(yum.name, "YumBakery");
    assert_eq!(yum.manager.as_ref().unwrap().name, "Bob");
    assert_eq!(yum.cashier.as_ref().unwrap().name, "Alice");

    let workers = worker::Entity::load()
        .order_by_asc(worker::Column::Id)
        .with(worker::Relation::BakeryManager)
        .with(worker::Relation::BakeryCashier)
        .all(db)?;

    assert_eq!(workers[0].name, "Bob");
    assert_eq!(workers[0].manager_of.len(), 1);
    assert_eq!(workers[0].manager_of[0].name, "YumBakery");
    assert!(workers[0].cashier_of.is_empty());

    assert_eq!(workers[1].name, "Alice");
    assert!(workers[1].manager_of.is_empty());
    assert_eq!(workers[1].cashier_of.len(), 1);
    assert_eq!(workers[1].cashier_of[0].name, "YumBakery");

    let yum = diamond_bakery::Entity::load()
        .filter_by_id(yum.id)
        .with((
            diamond_bakery::Relation::Manager,
            worker::Relation::BakeryManager,
        ))
        .with((
            diamond_bakery::Relation::Cashier,
            worker::Relation::BakeryCashier,
        ))
        .one(db)?
        .unwrap();

    let manager = yum.manager.as_ref().unwrap();
    assert_eq!(manager.name, "Bob");
    assert_eq!(manager.manager_of.len(), 1);
    assert_eq!(manager.manager_of[0].name, "YumBakery");
    assert!(manager.cashier_of.is_empty());

    let cashier = yum.cashier.as_ref().unwrap();
    assert_eq!(cashier.name, "Alice");
    assert!(cashier.manager_of.is_empty());
    assert_eq!(cashier.cashier_of.len(), 1);
    assert_eq!(cashier.cashier_of[0].name, "YumBakery");

    Ok(())
}

pub fn insert_bakery(db: &DbConn, name: &str) -> Result<bakery::Model, DbErr> {
    bakery::ActiveModel {
        name: Set(name.to_owned()),
        profit_margin: Set(1.0),
        ..Default::default()
    }
    .insert(db)
}

pub fn insert_baker(db: &DbConn, name: &str, bakery_id: i32) -> Result<baker::Model, DbErr> {
    baker::ActiveModel {
        name: Set(name.to_owned()),
        contact_details: Set(serde_json::json!({})),
        bakery_id: Set(Some(bakery_id)),
        ..Default::default()
    }
    .insert(db)
}

pub fn insert_cake(db: &DbConn, name: &str, bakery_id: Option<i32>) -> Result<cake::Model, DbErr> {
    cake::ActiveModel {
        name: Set(name.to_owned()),
        price: Set(rust_decimal::Decimal::ONE),
        gluten_free: Set(false),
        bakery_id: Set(bakery_id),
        ..Default::default()
    }
    .insert(db)
}

pub fn insert_cake_baker(
    db: &DbConn,
    baker_id: i32,
    cake_id: i32,
) -> Result<cakes_bakers::Model, DbErr> {
    cakes_bakers::ActiveModel {
        cake_id: Set(cake_id),
        baker_id: Set(baker_id),
    }
    .insert(db)
}
