/// Table schema definitions. Single source of truth for all table structures.
/// Arrow schemas are derived from these definitions.
///
/// ALL string columns are nullable. XML data is inherently sparse --
/// any element or attribute can be absent. Parquet handles nulls efficiently
/// via dictionary encoding and run-length encoded validity bitmaps.
use std::sync::Arc;
use arrow::datatypes::{DataType, Field, Schema, SchemaRef};

#[derive(Clone, Copy, PartialEq)]
pub enum DT { I32, Str }

pub struct ColDef {
    pub name: &'static str,
    pub dtype: DT,
    pub nullable: bool,
}

pub struct TableDef {
    pub name: &'static str,
    pub columns: &'static [ColDef],
}

const fn col(name: &'static str, dtype: DT, nullable: bool) -> ColDef {
    ColDef { name, dtype, nullable }
}
const fn int(name: &'static str) -> ColDef { col(name, DT::I32, false) }
const fn int_n(name: &'static str) -> ColDef { col(name, DT::I32, true) }
const fn str_(name: &'static str) -> ColDef { col(name, DT::Str, true) }

/// Define tables and auto-generate ALL_TABLES.
macro_rules! tables {
    ( $( $ID:ident $name:literal : [ $($col:expr),+ $(,)? ] );+ $(;)? ) => {
        $( pub static $ID: TableDef = TableDef { name: $name, columns: &[ $($col),+ ] }; )+
        pub static ALL_TABLES: &[&TableDef] = &[ $( &$ID ),+ ];
    };
}

tables! {
    CITATION "citation": [
        int("pmid"), int("version"), str_("status"), str_("owner"),
        str_("indexing_method"), str_("journal_nlm_id"), str_("journal_medline_ta"),
        str_("journal_title"), str_("journal_iso_abbr"),
        str_("issn_print"), str_("issn_electronic"), str_("issn_linking"),
        str_("journal_country"),
        str_("pub_model"), str_("volume"), str_("issue"), str_("cited_medium"),
        str_("pub_year"), str_("pub_month"), str_("pub_day"), str_("medline_date"),
        str_("title"), str_("vernacular_title"),
        str_("pagination"), str_("start_page"), str_("end_page"),
        str_("copyright"), str_("coi_statement"), int_n("n_references"),
        str_("date_completed"), str_("date_revised"),
        str_("pub_status"), str_("author_list_complete"),
    ];
    ABSTRACT_TEXT "abstract_text": [
        int("pmid"), str_("abstract_type"), str_("language"), int("seq"),
        str_("label"), str_("nlm_category"), str_("text"),
    ];
    PERSON "person": [
        int("pmid"), str_("role"), int("seq"), str_("valid"), str_("equal_contrib"),
        str_("last_name"), str_("fore_name"), str_("initials"),
        str_("suffix"), str_("collective_name"),
    ];
    PERSON_AFFILIATION "person_affiliation": [
        int("pmid"), str_("role"), int("person_seq"), int("seq"), str_("affiliation"),
    ];
    PERSON_IDENTIFIER "person_identifier": [
        int("pmid"), str_("role"), int("person_seq"), str_("source"), str_("identifier"),
    ];
    MESH_HEADING "mesh_heading": [
        int("pmid"), int("seq"), str_("descriptor_ui"), str_("descriptor_name"),
        str_("major"), str_("type_"), str_("auto_hm"),
    ];
    MESH_QUALIFIER "mesh_qualifier": [
        int("pmid"), int("heading_seq"), int("seq"),
        str_("qualifier_ui"), str_("qualifier_name"), str_("major"), str_("auto_hm"),
    ];
    KEYWORD "keyword": [
        int("pmid"), str_("list_owner"), int("seq"), str_("value"), str_("major"),
    ];
    CHEMICAL "chemical": [
        int("pmid"), int("seq"), str_("registry_number"),
        str_("substance_ui"), str_("substance_name"),
    ];
    SUPPL_MESH "suppl_mesh": [
        int("pmid"), int("seq"), str_("type_"), str_("ui"), str_("name"),
    ];
    GRANT_INFO "grant_info": [
        int("pmid"), int("seq"), str_("grant_id"), str_("acronym"),
        str_("agency"), str_("country"),
    ];
    PUBLICATION_TYPE "publication_type": [
        int("pmid"), int("seq"), str_("ui"), str_("value"),
    ];
    ARTICLE_ID "article_id": [
        int("pmid"), str_("id_type"), str_("value"),
    ];
    ELOCATION_ID "elocation_id": [
        int("pmid"), int("seq"), str_("eid_type"), str_("valid"), str_("value"),
    ];
    PUB_DATE_HISTORY "pub_date_history": [
        int("pmid"), str_("pub_status"),
        str_("year"), str_("month"), str_("day"), str_("hour"), str_("minute"),
    ];
    ARTICLE_DATE "article_date": [
        int("pmid"), int("seq"), str_("date_type"), str_("year"), str_("month"), str_("day"),
    ];
    COMMENT_CORRECTION "comment_correction": [
        int("pmid"), int("seq"), str_("ref_type"), str_("ref_source"),
        int_n("ref_pmid"), str_("note"),
    ];
    DATA_BANK "data_bank": [
        int("pmid"), int("seq"), str_("name"), str_("complete"),
    ];
    ACCESSION_NUMBER "accession_number": [
        int("pmid"), int("bank_seq"), int("seq"), str_("value"),
    ];
    REFERENCE "reference": [
        int("pmid"), int("list_seq"), int("ref_seq"), str_("citation_text"), str_("list_title"),
    ];
    REFERENCE_ID "reference_id": [
        int("pmid"), int("list_seq"), int("ref_seq"), str_("id_type"), str_("value"),
    ];
    OTHER_ID "other_id": [
        int("pmid"), int("seq"), str_("source"), str_("value"),
    ];
    GENERAL_NOTE "general_note": [
        int("pmid"), int("seq"), str_("owner"), str_("text"),
    ];
    OBJECT_INFO "object_info": [
        int("pmid"), int("seq"), str_("type_"),
    ];
    OBJECT_PARAM "object_param": [
        int("pmid"), int("obj_seq"), int("seq"), str_("name"), str_("value"),
    ];
    CITATION_TAG "citation_tag": [
        int("pmid"), str_("tag_type"), int("seq"), str_("value"),
    ];
    DELETE_CITATION "delete_citation": [
        int("pmid"),
    ];
}

pub fn arrow_schema(td: &TableDef) -> SchemaRef {
    let fields: Vec<Field> = td.columns.iter().map(|c| {
        let dt = match c.dtype {
            DT::I32 => DataType::Int32,
            DT::Str => DataType::Utf8,
        };
        Field::new(c.name, dt, c.nullable)
    }).collect();
    Arc::new(Schema::new(fields))
}
