#!/usr/bin/env bash
# Generate or check the public API snapshot.
#
# The baseline is a curated allowlist of Oxiland's intentional public surface.
# `check` diffs the embedded list against api/oxiland-public-api.txt, then runs
# scripts/check-public-api-owned.py so owned src/ items cannot drift silently.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BASELINE="$ROOT/api/oxiland-public-api.txt"
MODE="${1:-check}"

generate() {
  python3 - <<'PY'
items = [
    "oxiland",
    "oxiland::Error",
    "oxiland::Error::InvalidRdf",
    "oxiland::Error::Io",
    "oxiland::Error::OpenStore",
    "oxiland::Error::Parse",
    "oxiland::Error::Serialize",
    "oxiland::Error::SparqlEvaluation",
    "oxiland::Error::SparqlParse",
    "oxiland::Error::Storage",
    "oxiland::Error::Unsupported",
    "oxiland::FeatureValue",
    "oxiland::FeatureValue::Boolean",
    "oxiland::FeatureValue::Integer",
    "oxiland::FeatureValue::String",
    "oxiland::BridgeToken",
    "oxiland::FeatureMap",
    "oxiland::FeatureMap::get",
    "oxiland::FeatureMap::new",
    "oxiland::FeatureMap::set",
    "oxiland::StorageFeatures",
    "oxiland::Model",
    "oxiland::Model::add",
    "oxiland::Model::add_to_graph",
    "oxiland::Model::as_storage",
    "oxiland::Model::backend",
    "oxiland::Model::bulk_insert_quads",
    "oxiland::Model::capabilities",
    "oxiland::Model::clear",
    "oxiland::Model::clear_graph",
    "oxiland::Model::contains",
    "oxiland::Model::copy_to",
    "oxiland::Model::contains_in_graph",
    "oxiland::Model::export_nquads_to_path",
    "oxiland::Model::feature",
    "oxiland::Model::find",
    "oxiland::Model::import_nquads_from_path",
    "oxiland::Model::insert_quad",
    "oxiland::Model::insert_quad_unchecked",
    "oxiland::Model::is_empty",
    "oxiland::Model::len",
    "oxiland::Model::migrate_legacy_store",
    "oxiland::Model::new",
    "oxiland::Model::open",
    "oxiland::Model::open_with",
    "oxiland::Model::remove",
    "oxiland::Model::remove_from_graph",
    "oxiland::Model::remove_quad",
    "oxiland::Model::set_feature",
    "oxiland::Model::set_storage_feature",
    "oxiland::Model::set_storage_instance",
    "oxiland::Model::storage_backend_available",
    "oxiland::Model::storage_feature",
    "oxiland::Model::storage_instance",
    "oxiland::Model::store",
    "oxiland::Model::sync",
    "oxiland::Model::transaction",
    "oxiland::Model::with_world",
    "oxiland::Model::world",
    "oxiland::ModelTransaction",
    "oxiland::ModelTransaction::add",
    "oxiland::ModelTransaction::add_to_graph",
    "oxiland::ModelTransaction::clear",
    "oxiland::ModelTransaction::clear_graph",
    "oxiland::ModelTransaction::insert_quad",
    "oxiland::ModelTransaction::remove",
    "oxiland::ModelTransaction::remove_from_graph",
    "oxiland::ModelTransaction::remove_quad",
    "oxiland::OpenOptions",
    "oxiland::OpenOptions::backend",
    "oxiland::OpenOptions::can_create",
    "oxiland::OpenOptions::create",
    "oxiland::OpenOptions::fjall",
    "oxiland::OpenOptions::sqlite",
    "oxiland::OpenOptions::rocksdb",
    "oxiland::OpenOptions::redb",
    "oxiland::OpenOptions::lmdb",
    "oxiland::OpenOptions::is_read_only",
    "oxiland::OpenOptions::new",
    "oxiland::OpenOptions::path",
    "oxiland::OpenOptions::read_only",
    "oxiland::ParseError",
    "oxiland::ParseError::location",
    "oxiland::ParseError::message",
    "oxiland::Query",
    "oxiland::Query::as_str",
    "oxiland::Query::available_named_graphs",
    "oxiland::Query::base_iri",
    "oxiland::Query::cancellation_token",
    "oxiland::Query::default_graph",
    "oxiland::Query::default_graph_as_union",
    "oxiland::Query::execute",
    "oxiland::Query::limit",
    "oxiland::Query::new",
    "oxiland::Query::offset",
    "oxiland::Query::prefix",
    "oxiland::QueryResults",
    "oxiland::QueryResults::Boolean",
    "oxiland::QueryResults::Graph",
    "oxiland::QueryResults::Solutions",
    "oxiland::ResultsFormat",
    "oxiland::ResultsFormat::Csv",
    "oxiland::ResultsFormat::Json",
    "oxiland::ResultsFormat::Tsv",
    "oxiland::ResultsFormat::Xml",
    "oxiland::ResultsFormat::from_media_type",
    "oxiland::ResultsFormat::from_name",
    "oxiland::ResultsFormat::media_type",
    "oxiland::ResultsFormat::name",
    "oxiland::StorageBackend",
    "oxiland::StorageBackend::Fjall",
    "oxiland::StorageBackend::descriptor",
    "oxiland::StorageBackend::is_compiled",
    "oxiland::StorageBackend::Sqlite",
    "oxiland::StorageBackend::RocksDb",
    "oxiland::StorageBackend::Redb",
    "oxiland::StorageBackend::Lmdb",
    "oxiland::StorageBackend::Memory",
    "oxiland::StorageBackend::from_name",
    "oxiland::StorageBackend::name",
    "oxiland::StorageBackendDescriptor",
    "oxiland::StorageBackendDescriptor::backend",
    "oxiland::StorageBackendDescriptor::cargo_feature",
    "oxiland::StorageBackendDescriptor::compiled",
    "oxiland::StorageBackendDescriptor::durable",
    "oxiland::StorageBackendDescriptor::layout_reader",
    "oxiland::StorageBackendDescriptor::name",
    "oxiland::StorageCapabilities",
    "oxiland::StorageCapabilities::backend",
    "oxiland::StorageCapabilities::bulk_load",
    "oxiland::StorageCapabilities::clear",
    "oxiland::StorageCapabilities::durable",
    "oxiland::StorageCapabilities::fjall",
    "oxiland::StorageCapabilities::for_backend",
    "oxiland::StorageCapabilities::sqlite",
    "oxiland::StorageCapabilities::rocksdb",
    "oxiland::StorageCapabilities::redb",
    "oxiland::StorageCapabilities::lmdb",
    "oxiland::StorageCapabilities::memory",
    "oxiland::StorageCapabilities::read_only",
    "oxiland::StorageCapabilities::sync",
    "oxiland::StorageCapabilities::transactions",
    "oxiland::Update",
    "oxiland::Update::as_str",
    "oxiland::Update::base_iri",
    "oxiland::Update::cancellation_token",
    "oxiland::Update::default_graph",
    "oxiland::Update::default_graph_as_union",
    "oxiland::Update::execute",
    "oxiland::Update::new",
    "oxiland::Update::prefix",
    "oxiland::compiled_backends",
    "oxiland::is_known_backend_name",
    "oxiland::supported_backends",
    "oxiland::serialize_graph_results_to_writer",
    "oxiland::serialize_query_results_to_string",
    "oxiland::serialize_query_results_to_writer",
    "oxiland::Result",
    "oxiland::StatementMatches",
    "oxiland::StatementPattern",
    "oxiland::StatementPattern::graph_name",
    "oxiland::StatementPattern::object",
    "oxiland::StatementPattern::predicate",
    "oxiland::StatementPattern::subject",
    "oxiland::World",
    "oxiland::World::clear_log_handler",
    "oxiland::World::feature",
    "oxiland::World::log",
    "oxiland::World::log_level",
    "oxiland::World::new",
    "oxiland::World::raptor",
    "oxiland::World::raptor_bridge",
    "oxiland::World::raptor_init_handler",
    "oxiland::World::rasqal",
    "oxiland::World::rasqal_init_handler",
    "oxiland::World::register_parser_factory",
    "oxiland::World::register_query_factory",
    "oxiland::World::register_serializer_factory",
    "oxiland::World::register_storage_factory",
    "oxiland::World::set_feature",
    "oxiland::World::set_log_handler",
    "oxiland::World::set_log_level",
    "oxiland::World::set_raptor",
    "oxiland::World::set_raptor_bridge",
    "oxiland::World::set_raptor_init_handler",
    "oxiland::World::set_rasqal",
    "oxiland::World::set_rasqal_init_handler",
    "oxiland::LogFacility",
    "oxiland::LogFacility::General",
    "oxiland::LogFacility::Io",
    "oxiland::LogFacility::Model",
    "oxiland::LogFacility::Query",
    "oxiland::LogFacility::Utility",
    "oxiland::LogFacility::name",
    "oxiland::LogLevel",
    "oxiland::LogLevel::Debug",
    "oxiland::LogLevel::Error",
    "oxiland::LogLevel::Info",
    "oxiland::LogLevel::Warn",
    "oxiland::LogLevel::name",
    "oxiland::LogRecord",
    "oxiland::LogRecord::facility",
    "oxiland::LogRecord::level",
    "oxiland::LogRecord::message",
    "oxiland::LayoutReaderPolicy",
    "oxiland::LayoutReaderPolicy::FormatV1",
    "oxiland::LayoutReaderPolicy::None",
    "oxiland::utility",
    "oxiland::utility::DigestAlgorithm",
    "oxiland::utility::DigestAlgorithm::Md5",
    "oxiland::utility::DigestAlgorithm::Sha1",
    "oxiland::utility::DigestAlgorithm::Sha256",
    "oxiland::utility::DigestAlgorithm::from_name",
    "oxiland::utility::DigestAlgorithm::name",
    "oxiland::utility::Namespace",
    "oxiland::utility::Namespace::base",
    "oxiland::utility::Namespace::expand",
    "oxiland::utility::Namespace::new",
    "oxiland::utility::Namespace::prefix",
    "oxiland::utility::digest_bytes",
    "oxiland::utility::digest_hex",
    "oxiland::utility::digest_path",
    "oxiland::utility::file_uri_to_path",
    "oxiland::utility::join_iri",
    "oxiland::utility::normalize_nfc",
    "oxiland::utility::normalize_nfkc",
    "oxiland::utility::path_to_file_uri",
    "oxiland::utility::relativize_iri",
    "oxiland::utility::resolve_iri",
    "oxiland::utility::vocab",
    "oxiland::utility::vocab::dc",
    "oxiland::utility::vocab::dc::NS",
    "oxiland::utility::vocab::dc::creator",
    "oxiland::utility::vocab::dc::description",
    "oxiland::utility::vocab::dc::title",
    "oxiland::utility::vocab::owl",
    "oxiland::utility::vocab::owl::NS",
    "oxiland::utility::vocab::owl::class",
    "oxiland::utility::vocab::owl::ontology",
    "oxiland::utility::vocab::owl::same_as",
    "oxiland::utility::vocab::rdf",
    "oxiland::utility::vocab::rdf::NS",
    "oxiland::utility::vocab::rdf::object",
    "oxiland::utility::vocab::rdf::predicate",
    "oxiland::utility::vocab::rdf::property",
    "oxiland::utility::vocab::rdf::statement",
    "oxiland::utility::vocab::rdf::subject",
    "oxiland::utility::vocab::rdf::type_",
    "oxiland::utility::vocab::rdfs",
    "oxiland::utility::vocab::rdfs::NS",
    "oxiland::utility::vocab::rdfs::class",
    "oxiland::utility::vocab::rdfs::comment",
    "oxiland::utility::vocab::rdfs::label",
    "oxiland::utility::vocab::rdfs::sub_class_of",
    "oxiland::utility::vocab::rdfs::sub_property_of",
    "oxiland::utility::vocab::xsd",
    "oxiland::utility::vocab::xsd::NS",
    "oxiland::utility::vocab::xsd::boolean",
    "oxiland::utility::vocab::xsd::date_time",
    "oxiland::utility::vocab::xsd::decimal",
    "oxiland::utility::vocab::xsd::integer",
    "oxiland::utility::vocab::xsd::string",
    "oxiland::io",
    "oxiland::io::BomStrippingReader",
    "oxiland::io::BomStrippingReader::new",
    "oxiland::io::GraphTarget",
    "oxiland::io::GraphTarget::Dataset",
    "oxiland::io::GraphTarget::DefaultGraph",
    "oxiland::io::GraphTarget::Named",
    "oxiland::io::Parser",
    "oxiland::io::Parser::base_iri",
    "oxiland::io::Parser::feature",
    "oxiland::io::Parser::for_syntax",
    "oxiland::io::Parser::graph_target",
    "oxiland::io::Parser::load_collecting",
    "oxiland::io::Parser::load_into",
    "oxiland::io::Parser::load_path_collecting",
    "oxiland::io::Parser::load_path_into",
    "oxiland::io::Parser::load_path_transactional",
    "oxiland::io::Parser::load_transactional",
    "oxiland::io::Parser::parse_path",
    "oxiland::io::Parser::parse_path_with_extension",
    "oxiland::io::Parser::parse_reader",
    "oxiland::io::Parser::parse_slice",
    "oxiland::io::Parser::parse_str",
    "oxiland::io::Parser::set_feature",
    "oxiland::io::Parser::syntax",
    "oxiland::io::QuadStream",
    "oxiland::io::Serializer",
    "oxiland::io::Serializer::base_iri",
    "oxiland::io::Serializer::feature",
    "oxiland::io::Serializer::for_syntax",
    "oxiland::io::Serializer::serialize_model_to_path",
    "oxiland::io::Serializer::serialize_model_to_string",
    "oxiland::io::Serializer::serialize_model_to_writer",
    "oxiland::io::Serializer::serialize_quads_to_string",
    "oxiland::io::Serializer::serialize_quads_to_writer",
    "oxiland::io::Serializer::serialize_triples_to_writer",
    "oxiland::io::Serializer::set_feature",
    "oxiland::io::Serializer::syntax",
    "oxiland::io::Serializer::with_prefix",
    "oxiland::io::SliceStream",
    "oxiland::io::SourceLocation",
    "oxiland::io::SourceLocation::column",
    "oxiland::io::SourceLocation::line",
    "oxiland::io::SourceLocation::offset",
    "oxiland::io::Syntax",
    "oxiland::io::Syntax::NQuads",
    "oxiland::io::Syntax::NTriples",
    "oxiland::io::Syntax::RdfXml",
    "oxiland::io::Syntax::TriG",
    "oxiland::io::Syntax::Turtle",
    "oxiland::io::Syntax::all",
    "oxiland::io::Syntax::can_parse",
    "oxiland::io::Syntax::can_serialize",
    "oxiland::io::Syntax::extension",
    "oxiland::io::Syntax::from_extension",
    "oxiland::io::Syntax::from_media_type",
    "oxiland::io::Syntax::from_name",
    "oxiland::io::Syntax::media_type",
    "oxiland::io::Syntax::name",
    "oxiland::io::Syntax::supports_datasets",
    "oxiland::io::primitives",
    "oxiland::sparql",
    "oxiland::storage",
    "oxiland::terms",
    "oxiland::terms::blank_node",
    "oxiland::terms::named_node",
]
print("\n".join(items))
PY
}

case "$MODE" in
  generate)
    generate >"$BASELINE"
    echo "wrote $BASELINE"
    ;;
  check)
    tmp="$(mktemp)"
    generate >"$tmp"
    if ! diff -u "$BASELINE" "$tmp"; then
      echo "public API snapshot drift; run: scripts/generate-public-api.sh generate" >&2
      rm -f "$tmp"
      exit 1
    fi
    rm -f "$tmp"
    echo "public API snapshot ok"
    python3 "$ROOT/scripts/check-public-api-owned.py"
    ;;
  *)
    echo "usage: $0 [check|generate]" >&2
    exit 2
    ;;
esac
