//! The `querylog.db` schema version chain: comptime metadata and nothing else.
//!
//! Separate from `querylog_migrations.zig` so `tools/cut.zig` can import it
//! without linking SQLite. Nothing in this file may reach for `db.zig`, for a
//! C symbol, or for an allocator — the release gate reads these constants at
//! build time, and a dependency here would drag the whole storage layer into
//! the cut tool.
//!
//! **There are no migration hooks.** A step is a SQL file, period. Every
//! shipped step is therefore byte-comparable against the previous tag, which is
//! what lets the cut gate prove a released migration was never edited. A future
//! change that genuinely cannot be expressed in SQL must amend this design in
//! its own spec rather than adding a code path here.

const std = @import("std");

/// The version a file created by this build carries in `PRAGMA user_version`.
pub const current_version: i32 = 1;

/// The oldest stamped version this build can reach `current_version` from.
/// A file stamped below this refuses to open.
///
/// An EXPLICIT BREAK in a future release is expressed here and only here: bump
/// `current_version`, set `minimum_supported_version = current_version`, and
/// ship no step. The chain then cannot reach the new version from below the
/// minimum, so `open` refuses the old file by the ordinary rules. A break is
/// always versioned, always refused at runtime, and never silent.
pub const minimum_supported_version: i32 = 1;

/// The literal `user_version` the 0.0.12 and 0.0.13 binaries stamped: the CRC32
/// of their DDL text, under the pre-migration policy where a stamp mismatch
/// meant "replace the file".
///
/// FROZEN. It is derived from nothing at build time on purpose — recomputing it
/// from today's DDL would silently stop recognising the files it exists to
/// recognise the moment the schema moves. Editing it strands every 0.0.12 and
/// 0.0.13 file that has not yet been opened by a migration-aware build, which
/// is why the cut gate fails on any change to this line.
pub const legacy_fingerprint: i32 = 1975011655;

/// Logical versions live far below any plausible CRC32 stamp. A value above
/// this is not a version this project ever wrote, so it classifies as
/// unsupported rather than as a from-the-future schema.
pub const version_floor_guard: i32 = 1_000_000;

/// One entry per shipped step: `step_sql[i]` migrates version
/// `minimum_supported_version + i` to `minimum_supported_version + i + 1`.
/// Each entry is `@embedFile("migrations/v<from>.sql")`, and each such file is
/// immutable once released.
///
/// **Step-authoring rules** (the runner enforces the first, the equivalence
/// oracle catches violations of the rest):
///
/// - A step contains no transaction statement. No `BEGIN`, no `COMMIT`, no
///   `ROLLBACK`, no `SAVEPOINT`: the runner wraps the whole chain in one
///   transaction and installs an authorizer that denies them outright.
/// - A step that changes a table's shape must REBUILD it, so that the CREATE
///   text SQLite stores ends up byte-identical to the fresh DDL's:
///   `DROP` every view over `<t>` first; `ALTER TABLE <t> RENAME TO <t>_old`;
///   `CREATE TABLE <t> ...` pasted verbatim from `querylog_schema.ddl`;
///   `INSERT INTO <t> SELECT ... FROM <t>_old`; `DROP TABLE <t>_old`; recreate
///   every index and trigger of `<t>` verbatim; recreate the dropped views
///   verbatim last.
/// - `ALTER TABLE ... ADD COLUMN` and `ALTER TABLE ... RENAME COLUMN` on a kept
///   table are forbidden. SQLite rewrites the stored CREATE text under them,
///   and the oracle's exact-text layer would rightly call the result unequal.
pub const step_sql: []const [:0]const u8 = &.{};

comptime {
    std.debug.assert(minimum_supported_version >= 1);
    std.debug.assert(minimum_supported_version <= current_version);
    std.debug.assert(current_version <= version_floor_guard);
    std.debug.assert(legacy_fingerprint < 0 or legacy_fingerprint > version_floor_guard);
    std.debug.assert(step_sql.len == @as(usize, @intCast(current_version - minimum_supported_version)));
}

test "the chain covers exactly the supported range" {
    try std.testing.expectEqual(
        @as(usize, @intCast(current_version - minimum_supported_version)),
        step_sql.len,
    );
}

test "the legacy anchor is the literal 0.0.12 stamp" {
    // Not `fingerprintOf(ddl)`. The number is a historical fact about released
    // binaries, so a test that recomputed it would move with the schema and
    // prove nothing.
    try std.testing.expectEqual(@as(i32, 1975011655), legacy_fingerprint);
}
