use ecs::component::Component;
use ecs::query::term::Without;
use ecs::query::{
    TermWithFieldTuple as QueryTermWithFieldTuple,
    TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
};
use ecs::uid::Uid;
use ecs::{Component, Query, World};
use parking_lot::{Mutex, Once};

pub static SETUP: Once = Once::new();

pub static TEST_LOCK: Mutex<()> = Mutex::new(());

#[derive(Component)]
struct A;

#[derive(Component)]
struct B;

#[derive(Component)]
struct C;

#[derive(Component)]
struct D;

#[derive(Component)]
struct E;

#[derive(Component)]
struct F;

#[derive(Component)]
struct G;

fn setup()
{
    SETUP.call_once_force(|_| {
        assert_eq!(A::id().id(), 1);
        assert_eq!(B::id().id(), 2);
        assert_eq!(C::id().id(), 3);
        assert_eq!(D::id().id(), 4);
        assert_eq!(E::id().id(), 5);
        assert_eq!(F::id().id(), 6);
        assert_eq!(G::id().id(), 7);
    });
}

fn assert_query_finds_ents<QueryFieldTerms, QueryFieldlessTerms>(
    query: Query<'_, QueryFieldTerms, QueryFieldlessTerms>,
    mut expected_ent_ids: Vec<Uid>,
) where
    QueryFieldTerms: QueryTermWithFieldTuple,
    QueryFieldlessTerms: QueryTermWithoutFieldTuple,
{
    assert!(
        query.iter_with_euids().all(|(ent_id, _)| {
            let Some(index) = expected_ent_ids
                .iter()
                .position(|expected_id| *expected_id == ent_id)
            else {
                return false;
            };

            expected_ent_ids.remove(index);

            true
        }),
        "Unexpected entity was found. Expected entities left: {expected_ent_ids:?}"
    );

    assert_eq!(
        expected_ent_ids.len(),
        0,
        concat!(
            "Not all entities expected to be found was found. ",
            "Expected entities left: {:?}"
        ),
        expected_ent_ids
    );
}

#[test]
fn query_archetype_exists_with_edges_to_next_archetypes()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    let ent_1_id = world.create_entity((A, B, C));
    let ent_2_id = world.create_entity((A, B, C, D, E));
    let ent_3_id = world.create_entity((A, B, C, E));
    let ent_4_id = world.create_entity((A, B, C, G, F));

    assert_query_finds_ents(
        world.query::<(&A, &B, &C), ()>(),
        vec![ent_1_id, ent_2_id, ent_3_id, ent_4_id],
    );
}

#[test]
fn query_archetype_exists_with_2_comps_diff_to_next_archetype()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    let ent_1_id = world.create_entity((A, B, C, D, F));

    let ent_2_id = world.create_entity((A, B, F));

    assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
}

#[test]
fn query_archetype_exists_with_2_comps_diff_to_next_archetype_rev()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    let ent_1_id = world.create_entity((A, B, F));

    let ent_2_id = world.create_entity((A, B, C, D, F));

    assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
}

#[test]
fn query_archetype_exists_with_3_comps_diff_to_next_archetype()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    let ent_1_id = world.create_entity((A, B, C, D, E, F));

    let ent_2_id = world.create_entity((A, B, F));

    assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
}

#[test]
fn query_archetype_exists_with_3_comps_diff_to_next_archetype_rev()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    let ent_1_id = world.create_entity((A, B, F));

    let ent_2_id = world.create_entity((A, B, C, D, E, F));

    assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
}

#[test]
fn query_archetype_exists_with_4_comps_diff_to_next_archetype()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    let ent_1_id = world.create_entity((A, B, C, D, E, F, G));

    let ent_2_id = world.create_entity((A, B, G));

    assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]);
}

#[test]
fn query_archetype_exists_with_4_comps_diff_to_next_archetype_rev()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    let ent_1_id = world.create_entity((A, B, G));

    let ent_2_id = world.create_entity((A, B, C, D, E, F, G));

    assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]);
}

#[test]
fn query_archetype_exists_with_4_comps_diff_to_next_archetype_and_opt_comp()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    let ent_1_id = world.create_entity((A, B, C, D, E, F, G));

    let ent_2_id = world.create_entity((A, B, G));

    assert_query_finds_ents(
        world.query::<(&A, &Option<E>, &G), ()>(),
        vec![ent_1_id, ent_2_id],
    );
}

#[test]
fn query_archetype_nonexistant()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    world.create_entity((A, B, C));

    let ent_2_id = world.create_entity((A, B, C, D, E));
    let ent_3_id = world.create_entity((A, B, C, E));

    world.create_entity((A, B, C, G, F));

    assert_query_finds_ents(world.query::<(&A, &E), ()>(), vec![ent_2_id, ent_3_id]);
}

#[test]
fn query_archetype_nonexistant_and_opt_comp()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    world.create_entity((A, B, C));
    let ent_2_id = world.create_entity((A, B, C, D, E));
    let ent_3_id = world.create_entity((A, B, C, E));
    world.create_entity((A, B, C, G, F));

    assert_query_finds_ents(
        world.query::<(&A, &E, &Option<D>), ()>(),
        vec![ent_2_id, ent_3_id],
    );
}

#[test]
fn query_without_comp_and_archetype_exists()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    let ent_1_id = world.create_entity((A, B, C));

    world.create_entity((A, B, C, E));
    world.create_entity((A, B, C, F, E));

    let ent_2_id = world.create_entity((A, B, C, G));
    let ent_3_id = world.create_entity((A, B, C, G, F));

    assert_query_finds_ents(
        world.query::<(&A, &B, &C), (Without<E>,)>(),
        vec![ent_1_id, ent_2_id, ent_3_id],
    );
}

#[test]
fn query_without_required_comp_and_archetype_exists()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    world.create_entity((A, B, C));

    world.create_entity((A, B, C, E));
    world.create_entity((A, B, C, F, E));

    world.create_entity((A, B, C, G));
    world.create_entity((A, B, C, G, F));

    assert_query_finds_ents(world.query::<(&A, &B), (Without<B>,)>(), vec![]);
}

#[test]
fn query_without_comp_and_archetype_nonexistant()
{
    setup();

    let _test_lock = TEST_LOCK.lock();

    let mut world = World::new();

    world.create_entity((A, B, C));

    let ent_1_id = world.create_entity((A, B, C, E));

    world.create_entity((A, B, C, F, E));

    let ent_2_id = world.create_entity((A, B, C, G, E));
    world.create_entity((A, B, C, G, F, E));

    assert_query_finds_ents(
        world.query::<(&A, &E), (Without<F>,)>(),
        vec![ent_1_id, ent_2_id],
    );
}