//! Seed data: a representative subset of the Spectrum Cataloguing field set.
//!
//! Idempotent — each vocabulary and field definition is created only if a row with
//! that key does not already exist. Vocabularies are seeded empty; their terms are
//! populated by the organization or a later import. The inventory-minimum fields
//! (object number, name, location, …) live in the typed object core, not here.

use domain::{
    AuditActor, AuthorityKind, FieldType, LocalizedLabel, NewFieldDefinition, VocabularyId,
};

use crate::{fields, vocab};

/// Seed the Spectrum cataloguing vocabularies and field definitions on `conn`.
/// Pass a transaction connection (`&mut *tx`) so the whole seed is atomic.
pub async fn seed_spectrum_cataloguing(conn: &mut sqlx::PgConnection) -> Result<(), sqlx::Error> {
    let material = ensure_vocabulary(conn, "material").await?;
    let object_name = ensure_vocabulary(conn, "object_name").await?;
    let technique = ensure_vocabulary(conn, "technique").await?;

    let definitions = [
        def(
            "object_type",
            FieldType::Term {
                vocabulary_id: object_name,
            },
            "identification",
            &[("sv", "Sakord"), ("en", "Object type")],
        ),
        def(
            "title",
            FieldType::LocalizedText,
            "identification",
            &[("sv", "Titel"), ("en", "Title")],
        ),
        def(
            "comments",
            FieldType::Text,
            "identification",
            &[("sv", "Kommentarer"), ("en", "Comments")],
        ),
        def(
            "material",
            FieldType::Term {
                vocabulary_id: material,
            },
            "description",
            &[("sv", "Material"), ("en", "Material")],
        ),
        def(
            "technique",
            FieldType::Term {
                vocabulary_id: technique,
            },
            "description",
            &[("sv", "Teknik"), ("en", "Technique")],
        ),
        def(
            "physical_description",
            FieldType::Text,
            "description",
            &[("sv", "Fysisk beskrivning"), ("en", "Physical description")],
        ),
        def(
            "dimensions",
            FieldType::Text,
            "description",
            &[("sv", "Mått"), ("en", "Dimensions")],
        ),
        def(
            "inscription",
            FieldType::Text,
            "description",
            &[("sv", "Inskription"), ("en", "Inscription")],
        ),
        def(
            "content_description",
            FieldType::Text,
            "content",
            &[
                ("sv", "Innehållsbeskrivning"),
                ("en", "Content description"),
            ],
        ),
        def(
            "production_date",
            FieldType::Date,
            "production",
            &[("sv", "Tillverkningsdatum"), ("en", "Production date")],
        ),
        def(
            "production_place",
            FieldType::Authority {
                kind: Some(AuthorityKind::Place),
            },
            "production",
            &[("sv", "Tillverkningsplats"), ("en", "Production place")],
        ),
        def(
            "production_person",
            FieldType::Authority {
                kind: Some(AuthorityKind::Person),
            },
            "production",
            &[("sv", "Tillverkare"), ("en", "Maker")],
        ),
    ];

    for definition in &definitions {
        ensure_field_definition(conn, definition).await?;
    }

    Ok(())
}

/// Get-or-create a vocabulary by key, returning its id.
async fn ensure_vocabulary(
    conn: &mut sqlx::PgConnection,
    key: &str,
) -> Result<VocabularyId, sqlx::Error> {
    if let Some(existing) = vocab::vocabulary_by_key(&mut *conn, key).await? {
        Ok(existing.id)
    } else {
        Ok(
            vocab::create_vocabulary(&mut *conn, AuditActor::System, key)
                .await?
                .id,
        )
    }
}

/// Create a field definition only if its key is not already present.
async fn ensure_field_definition(
    conn: &mut sqlx::PgConnection,
    definition: &NewFieldDefinition,
) -> Result<(), sqlx::Error> {
    if fields::field_definition_by_key(&mut *conn, &definition.key)
        .await?
        .is_none()
    {
        fields::create_field_definition(&mut *conn, definition).await?;
    }

    Ok(())
}

fn def(
    key: &str,
    field_type: FieldType,
    group: &str,
    label_pairs: &[(&str, &str)],
) -> NewFieldDefinition {
    NewFieldDefinition {
        key: key.to_owned(),
        field_type,
        required: false,
        group_key: Some(group.to_owned()),
        labels: label_pairs
            .iter()
            .map(|(lang, label)| LocalizedLabel {
                lang: (*lang).to_owned(),
                label: (*label).to_owned(),
            })
            .collect(),
    }
}
