use db::{Db, catalog, fields, vocab};
use domain::{
    AuditActor, FieldType, LocalizedLabel, NewFieldDefinition, NewTerm, ObjectInput, Visibility,
};
use search::SearchClient;
use sqlx::PgPool;

fn meili() -> (String, String) {
    (
        std::env::var("MEILI_URL").expect("MEILI_URL must be set"),
        std::env::var("MEILI_MASTER_KEY").expect("MEILI_MASTER_KEY must be set"),
    )
}

fn unique_index() -> String {
    format!("reindex_test_{}", uuid::Uuid::new_v4().simple())
}

// Path is relative to this crate's root; the schema lives in the `db` crate.
// If the workspace layout changes, update this path.
#[sqlx::test(migrations = "../db/migrations")]
async fn reindex_resolves_term_labels_and_finds_by_label(pool: PgPool) {
    let db = Db::from_pool(pool);

    // a material vocabulary with a "wood" term
    let mut tx = db.pool().begin().await.unwrap();

    let material = vocab::create_vocabulary(&mut tx, AuditActor::System, "material")
        .await
        .unwrap();

    let wood = vocab::add_term(
        &mut tx,
        AuditActor::System,
        &NewTerm {
            vocabulary_id: material.id,
            external_uri: None,
            labels: vec![LocalizedLabel {
                lang: "en".into(),
                label: "wood".into(),
            }],
        },
    )
    .await
    .unwrap();

    fields::create_field_definition(
        &mut tx,
        &NewFieldDefinition {
            key: "material".into(),
            field_type: FieldType::Term {
                vocabulary_id: material.id,
            },
            required: false,
            group_key: None,
            labels: vec![LocalizedLabel {
                lang: "en".into(),
                label: "material".into(),
            }],
        },
    )
    .await
    .unwrap();

    let object_id = catalog::create_object(
        &mut tx,
        AuditActor::System,
        &ObjectInput {
            object_number: "LM-1".into(),
            object_name: "vase".into(),
            number_of_objects: 1,
            brief_description: None,
            current_location: None,
            current_owner: None,
            recorder: None,
            recording_date: None,
            visibility: Visibility::Public,
        },
    )
    .await
    .unwrap();

    tx.commit().await.unwrap();

    // set the material field to the wood term
    let mut tx = db.pool().begin().await.unwrap();

    catalog::set_object_fields(
        &mut tx,
        AuditActor::System,
        object_id,
        serde_json::json!({ "material": wood.to_string() })
            .as_object()
            .unwrap(),
    )
    .await
    .unwrap();

    tx.commit().await.unwrap();

    let (url, key) = meili();
    let client = SearchClient::connect(&url, &key, &unique_index()).unwrap();

    client.ensure_index().await.unwrap();
    client.reindex_all(&db).await.unwrap();

    // found by the object name
    assert_eq!(client.search("vase").await.unwrap(), vec![object_id]);
    // found by the resolved TERM LABEL (not the uuid)
    assert_eq!(client.search("wood").await.unwrap(), vec![object_id]);
}
