//! Cozo Datalog DDL. Run on first open; subsequent opens swallow `relation exists`.

/// Current per-repo schema version. Bump on any breaking schema change so
/// `Storage::open` can warn users to rebuild.
///
/// **v3** added the `Symbol:by_name` secondary index intended to
/// accelerate cross-file resolution (`resolve_cross_file_calls`).
/// **Empirical finding** (cozo-ce 0.7.13, verified by
/// `tests/perf_spike.rs::resolve_query_plan_uses_join_optimization`):
/// the planner does NOT use this index for the
/// `*Symbol{name, ...}, needle_set[name]` join shape — it picks
/// `mem_prefix_join` instead, joining the bound needle_set against
/// the full Symbol scan output. Wall-clock perf is still good
/// (<500ms for 100k symbols × 1k unresolved Calls in
/// `resolve_cross_file_calls_uses_name_index`) so the user-visible
/// behavior is fine, but the index is currently architectural
/// overhead. Kept in DDL for forward-compat: future Cozo versions
/// may pick it up. Removing would require another schema bump.
// CORE-C1: bumped from 3 → 4 for node_id backtick-escape migration.
// CKG-UI: bumped from 4 → 5 to add the `Symbol:by_qname` secondary
// index. `ckg ui`'s seed-lookup path was full-scanning `Symbol`
// on a `qname = $q` equality (~1.8s on raksul-directmail's 1M-row
// table). The new index brings it to sub-millisecond; existing
// databases need one re-index to migrate (handled automatically by
// `rebuild_at_path`).
pub const SCHEMA_VERSION: u32 = 6;

/// String form of `SCHEMA_VERSION` (L7). Pre-computed to avoid repeated
/// `.to_string()` calls in `stamp_schema_version` on every `Storage::open`.
pub const SCHEMA_VERSION_STR: &str = "6";

/// Per-repo relations + indexes. `::index create` is system-rule syntax,
/// idempotent because `run_idempotent` swallows `relation already
/// exists`-style errors when re-running on an existing DB. Order
/// matters: indexes must come AFTER the relation they reference.
pub const PER_REPO_DDL: &[&str] = &[
    ":create Symbol {id: String => qname: String, name: String, kind: String, \
     file: String, line: Int, col: Int, is_public: Bool, doc: String, hash: String}",
    ":create File {path: String => language: String, hash: String}",
    ":create Doc {id: String => body: String}",
    ":create Route {id: String => method: String, path: String, handler: String}",
    ":create Calls {src: String, dst: String => confidence: Float}",
    ":create Imports {src: String, dst: String => confidence: Float}",
    ":create Extends {src: String, dst: String => confidence: Float}",
    ":create Implements {src: String, dst: String => confidence: Float}",
    ":create Defines {src: String, dst: String}",
    ":create Documents {src: String, dst: String}",
    ":create Tests {src: String, dst: String}",
    // `await` call sites — confidence mirrors `Calls`.
    ":create Awaits {src: String, dst: String => confidence: Float}",
    // Free-form key/value for schema version, last-resolver run, etc.
    ":create Meta {key: String => value: String}",
    // Secondary index on Symbol.name. Backs the I8 `needle_set[name]`
    // join in `resolve_cross_file_calls` — without this index, that
    // query falls back to a full Symbol scan filtered by membership,
    // which scales with total symbols not needle count.
    "::index create Symbol:by_name {name}",
    // Secondary index on Symbol.qname. Backs `ckg ui`'s seed-lookup
    // fast path: `*Symbol:by_qname{qname: $q, id}` is a sub-ms point
    // query on a 1M-row table; the pre-index `*Symbol{qname: $q, ...}`
    // shape was a ~1.8s full scan. Cozo's planner picks this up
    // automatically when the bound predicate is exact-match on the
    // indexed column (verified against cozo-ce 0.7.13 on
    // raksul-directmail). UI's `lookup_seed` explicitly queries
    // `*Symbol:by_qname` first to guarantee index use regardless of
    // future planner drift.
    "::index create Symbol:by_qname {qname}",
    // Phase 02 (index speed): per-file mtime cache. Allows the indexer
    // to skip blake3 hashing on files whose mtime hasn't moved since
    // the last index — warm re-index of an unchanged repo goes from
    // O(total_bytes) (hash every file) to O(file_count) (stat every
    // file). Old DBs without this relation get it added via the
    // idempotent DDL on their next open. Cleanup of stale rows when
    // files are removed is best-effort: a deleted file leaves a
    // dangling FileMtime row, but it costs nothing functionally —
    // future hash checks against a missing path simply return None.
    ":create FileMtime {path: String => mtime: Int}",
];

/// Registry DB relations (cross-repo).
pub const REGISTRY_DDL: &[&str] = &[
    ":create Repo {repo_id: String => db_path: String, root_path: String, \
     head_sha: String, last_scanned: String, languages: [String]}",
    ":create GlobalSymbol {gid: String => qname: String, repo_id: String, \
     local_node_id: String, kind: String}",
    ":create CROSS_CALLS {src: String, dst: String => confidence: Float}",
    ":create CROSS_IMPORTS {src: String, dst: String => confidence: Float}",
    ":create CROSS_EXTENDS {src: String, dst: String => confidence: Float}",
    ":create CROSS_IMPLEMENTS {src: String, dst: String => confidence: Float}",
    ":create CROSS_AWAITS {src: String, dst: String => confidence: Float}",
];
