// Apache-2.0 licensed. See ../LICENSE-APACHE.

//! # skade — fast Iceberg I/O + ergonomic SQL, embedded
//!
//! *Skaði, the winter queen: she keeps the icebergs in order.*
//!
//! `skade` turns one local directory into an Apache Iceberg warehouse — the
//! catalog is [`nornir-catalog`](https://crates.io/crates/nornir-catalog)
//! (pure-Rust, redb-backed, ACID, in-process), the data plane is
//! `iceberg-rust` 0.9 + Arrow 57, and the SQL surface is DataFusion. No REST
//! service, no JVM, no C deps.
//!
//! By default the data/metadata blobs live on the local filesystem. For a
//! shared object store (MinIO/S3) the [`object_store`] module provides a
//! pluggable [`ObjectStore`] trait with feature-gated backends (`rustfs`
//! embedded-local, `s3` rust-s3, `aws-s3` aws-sdk-s3) — wire one with
//! [`Warehouse::open_with_store`] / [`Warehouse::open_with_object_store_config`].
//!
//! ## Quickstart
//!
//! ```no_run
//! use std::sync::Arc;
//! use skade::arrow_array::{Int64Array, RecordBatch, StringArray};
//! use skade::arrow_schema::{DataType, Field, Schema};
//!
//! # async fn run() -> skade::Result<()> {
//! // One directory = catalog (catalog.redb) + warehouse (metadata + parquet).
//! let wh = skade::open("/var/lib/myapp/lake").await?;
//!
//! let schema = Schema::new(vec![
//!     Field::new("id", DataType::Int64, false),
//!     Field::new("name", DataType::Utf8, false),
//! ]);
//! let mut events = wh.table_or_create("events", &schema).await?;
//!
//! // Arrow RecordBatch in …
//! let batch = RecordBatch::try_new(Arc::new(schema), vec![
//!     Arc::new(Int64Array::from(vec![1, 2, 3])),
//!     Arc::new(StringArray::from(vec!["a", "b", "c"])),
//! ])?;
//! events.append(&[batch]).await?; // one fast_append snapshot
//!
//! // … Arrow RecordBatches out (full-snapshot scan).
//! let back = events.read().await?;
//! assert_eq!(back.iter().map(|b| b.num_rows()).sum::<usize>(), 3);
//! # let _ = back; Ok(()) }
//! ```
//!
//! With the **`sql`** feature (opt-in) you also get a DataFusion query surface —
//! `Table::sql` for one table and `Warehouse::sql` across the warehouse:
//! `events.sql("SELECT count(*) FROM events WHERE id > 1").await?`.
//!
//! ## What's in the box
//!
//! * [`open`] / [`Warehouse`] — catalog-from-path: `catalog.redb` + `file://`
//!   warehouse + storage factory in one builder call.
//! * [`Table`] — [`Table::append`] / [`Table::ingest`] (ParquetWriter →
//!   RollingFileWriter → DataFileWriter → `fast_append`, as one call),
//!   [`Table::read`] (`scan().select_all().to_arrow()` as one call),
//!   [`Table::sql`].
//! * [`WriteProps`] — the Parquet `WriterProperties` knobs that live in skade:
//!   `compression`, per-column **bloom filters** (`bloom_columns`), tuned
//!   **row-group size**, and **dictionary** control. Bloom filters add
//!   intra-file row-group skipping on point lookups (e.g. a `symbol`/`sha`
//!   probe), complementing the catalog's sort/`SortOrder` file-level skipping.
//!   Wire it per handle with [`Table::write_props`], or call the explicit
//!   [`append_props`] / [`ingest_parallel_props`] / [`ingest_props`] /
//!   [`ingest_pipelined_props`] free functions. The bare `append`/`ingest*` and
//!   the `*_with(Compression)` APIs still work (they delegate with defaults).
//! * Schema bridges — [`arrow_to_iceberg`], [`recast`] (`Utf8 → LargeUtf8` /
//!   `Binary → LargeBinary` widening), [`widen_for_iceberg`] and its inverse
//!   [`unwiden`] (`Int32 ↔ UInt32`, `Int64 ↔ UInt64` bit-reinterpret — Iceberg
//!   has no unsigned types).
//! * Windowed Parquet reads — [`parquet_layout`] + [`rowgroup_windows`] +
//!   [`read_row_groups`]: bounded-memory, thread-parallel bulk ingest.
//!
//! ## Version contract
//!
//! skade pins **iceberg 0.9.x / arrow-* 57.x / parquet 57.x / datafusion
//! 52.x** and re-exports them ([`iceberg`], [`arrow_array`], [`arrow_schema`],
//! [`arrow_cast`], [`parquet`], [`datafusion`], [`iceberg_datafusion`],
//! [`skade_katalog`]) so dependents can name these types without guessing the
//! matching majors. When any of those majors move, skade bumps its **minor**
//! version and says so in the changelog.
//!
//! ## Feature flags
//!
//! * `sql` *(opt-in)* — DataFusion + iceberg-datafusion: [`Warehouse::sql`],
//!   [`Warehouse::session`], [`Table::sql`]. Off by default so the write/read
//!   data plane stays lean (no datafusion, no `liblzma-sys`); enable it for SQL.
//! * `rustfs` *(opt-in)* — [`object_store::LocalFsStore`], an embedded,
//!   zero-external-dep local-filesystem [`ObjectStore`].
//! * `s3` *(opt-in)* — [`object_store::RustS3Store`], MinIO/S3 via `rust-s3`.
//!   The default S3 backend. (Won't compile in a workspace that links `gix`;
//!   use `aws-s3` there.)
//! * `aws-s3` *(opt-in)* — [`object_store::AwsS3Store`], MinIO/S3 via
//!   `aws-sdk-s3` — reuse the consumer's existing S3 SDK.
//!
//! See `.nornir/object-store.md` for the full design + MinIO wiring.

mod bridge;
mod delete;
mod error;
pub mod object_store;
mod parquet_io;
mod read;
mod table;
mod warehouse;
mod write;

pub use bridge::{arrow_to_iceberg, recast, unwiden, widen_for_iceberg};
pub use error::{Result, SkadeError};
pub use object_store::{MemoryStore, ObjectStore, ObjectStoreConfig, ObjectStoreFactory, S3Config};
/// The Parquet compression codec for writes (re-exported from `parquet`).
pub use parquet::basic::Compression;
pub use parquet_io::{parquet_layout, read_row_groups, rowgroup_windows};
pub use read::{
    DeltaPlan, EqualityDeleteFile, Scalar, ScanFilter, ScanPlanStats, arrow_schema_of, plan_stats,
    read_all, read_columns, read_delta, read_equality_deletes, read_filtered, read_limited,
    scan_count,
};
pub use table::Table;
pub use warehouse::{DEFAULT_NAMESPACE, Warehouse};
pub use write::{
    IngestStats, WriteProps, append, append_props, append_with, ingest, ingest_parallel,
    ingest_parallel_props, ingest_parallel_with, ingest_pipelined, ingest_pipelined_props,
    ingest_props, ingest_with,
};

// Version-contract re-exports: the exact majors skade is built against.
pub use arrow_array;
pub use arrow_cast;
pub use arrow_schema;
pub use iceberg;
pub use parquet;
pub use skade_katalog;

#[cfg(feature = "sql")]
pub use datafusion;
#[cfg(feature = "sql")]
pub use iceberg_datafusion;

use std::path::Path;

/// Open (creating if absent) the Iceberg warehouse at `dir`.
/// Shorthand for [`Warehouse::open`].
pub async fn open(dir: impl AsRef<Path>) -> Result<Warehouse> {
    Warehouse::open(dir).await
}
