//! How much of the window a client asked about the query log can still answer
//! for.
//!
//! Retention deletes old rows and advances a watermark in the same transaction
//! (`queries_repo.pruneOlderThan`), so the file knows the oldest instant it is
//! complete for. Without that fact on the wire a chart draws a pruned week as a
//! week of silence, which is the one reading that is certainly wrong.
//!
//! Two endpoints carry it — `/api/queries` and `/api/overview` — and they judge
//! it against their own effective lower bound: the client's `since` for the
//! query log, the period's aligned window start for the overview.

const std = @import("std");

const db = @import("../storage/db.zig");
const queries_repo = @import("../storage/repositories/queries_repo.zig");

pub const Coverage = struct {
    /// True only when the whole requested window is inside what the file still
    /// holds. A request with no lower bound at all asks about all of history,
    /// which no file that has ever pruned can promise.
    complete: bool,
    /// The oldest instant the file is complete for, unix seconds.
    available_since: i64,
};

pub fn of(available_since: i64, since: ?i64) Coverage {
    return .{
        .complete = if (since) |lower_bound| lower_bound >= available_since else false,
        .available_since = available_since,
    };
}

/// Reads the watermark for a request that is about to answer.
pub fn read(database: *db.Db, since: ?i64) db.Error!Coverage {
    return of(try queries_repo.availableSince(database), since);
}

// ---------------------------------------------------------------------------
// tests
// ---------------------------------------------------------------------------

const testing = std.testing;

test "a window that starts at or after the watermark is complete" {
    try testing.expect(of(1000, 1000).complete);
    try testing.expect(of(1000, 1001).complete);
    try testing.expect(!of(1000, 999).complete);
}

test "an unbounded window is never complete" {
    const unbounded = of(1000, null);
    try testing.expect(!unbounded.complete);
    try testing.expectEqual(@as(i64, 1000), unbounded.available_since);
}
