/* Synthetic schedule over the unchanged K3 cache and real temporary shard I/O.
 * The second argument must be a disposable COPY of the fixture: the last case
 * truncates that shard after indexing to produce an actual short-read failure.
 * No model forward pass or physical-device benchmark is performed. */
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "k3_cache.h"

static void init(K3Cache *c, K3St *st, const char *dir) {
    assert(k3_st_open(st, dir) == 0);
    K3Cfg cfg; memset(&cfg, 0, sizeof cfg);
    cfg.n_layers = 1; cfg.n_experts = 24; cfg.topk = 4;
    /* Fixed budget, eight 12,288-byte slots for this fixture geometry. */
    assert(k3_cache_init(c, st, &cfg, 8 * 12288) == 0);
    assert(c->nslot == 8 && c->slot_bytes == 12288 && c->src.getmany);
}
static void consume(K3Cache *c, int e) {
    K3ExpertQ q;
    assert(c->src.get(&c->src, 0, e, &q) == 0);
    K3ExpertRef r; assert(k3_expert_ref(c->st, 0, e, &r) == 0);
    unsigned char *raw = malloc((size_t)r.nbytes); assert(raw);
    assert(k3_expert_load(c->st, &r, raw) == r.nbytes);
    const unsigned char *got[] = {q.p1, q.s1, q.p2, q.s2, q.p3, q.s3};
    for (int i = 0; i < 3; i++) {
        assert(memcmp(got[2*i], raw + r.m[i].p_off, (size_t)r.m[i].p_bytes) == 0);
        assert(memcmp(got[2*i+1], raw + r.m[i].s_off, (size_t)r.m[i].s_bytes) == 0);
    }
    free(raw);
}
int main(int argc, char **argv) {
    if (argc != 3) { fprintf(stderr, "usage: cache-probe fixture disposable-fault-copy\n"); return 2; }
    K3Cache c; K3St st; init(&c, &st, argv[1]);
    int ids[] = {0, 1, 2, 3};
    assert(c.src.getmany(&c.src, 0, ids, 4) == 4);
    assert(c.hits == 0 && c.misses == 0 && c.prefetch_reads == 4 && c.bytes_read == 6528);
    for (int i = 0; i < 4; i++) {
        assert(c.src.resident(&c.src, 0, i, NULL));
        for (int j = 0; j < i; j++) assert(c.slot_of[i] != c.slot_of[j]);
    }
    printf("batch_before_get: requests=0; prefetch_reads=4; payload_bytes=6528; distinct resident slots=4\n");
    for (int i = 0; i < 4; i++) consume(&c, i);
    assert(c.hits == 4 && c.misses == 0 && c.prefetch_reads == 4 && c.bytes_read == 6528);
    printf("first_consumption: hits=4/4; prefetch_reads=4; adjusted_hits=0; payload_bytes=6528\n");
    for (int i = 0; i < 4; i++) consume(&c, i);
    assert(c.hits == 8 && c.misses == 0 && c.prefetch_reads == 4 && c.bytes_read == 6528);
    printf("repeat_consumption: hits=8/8; prefetch_reads=4; adjusted_hits=4; payload_bytes=6528\n");
    k3_cache_reset_stats(&c);
    assert(c.prefetch_reads == 0 && c.hits == 0 && c.misses == 0 && c.bytes_read == 0);
    for (int i = 0; i < 4; i++) consume(&c, i);
    assert(c.hits == 4 && c.misses == 0 && c.prefetch_reads == 0 && c.bytes_read == 0);
    printf("reset_then_warm: hits=4/4; prefetch_reads=0; payload_bytes=0; residency preserved\n");
    k3_cache_free(&c); k3_st_close(&st);

    init(&c, &st, argv[1]);
    assert(c.src.getmany(&c.src, 0, ids, 4) == 4);
    for (int i = 0; i < 4; i++) consume(&c, 0);
    assert(c.hits == 4 && c.prefetch_reads == 4 && c.bytes_read == 6528);
    assert(c.hits - c.prefetch_reads == 0);
    printf("unused_prefetch_schedule: get(0) four times; hits=4; prefetch_reads=4; adjusted_hits=0 despite three repeated gets\n");
    k3_cache_free(&c); k3_st_close(&st);

    init(&c, &st, argv[2]);
    consume(&c, 0); k3_cache_reset_stats(&c);
    K3ExpertRef r; assert(k3_expert_ref(&st, 0, 20, &r) == 0);
    assert(truncate(st.path[r.shard], r.off) == 0);
    int missing[] = {20, 21, 22, 23};
    assert(c.src.getmany(&c.src, 0, missing, 4) == 0);
    for (int i = 0; i < 4; i++) assert(!c.src.resident(&c.src, 0, missing[i], NULL));
    for (int i = 0; i < c.nslot; i++) assert(c.key_of[i] != K3_SLOT_INFLIGHT);
    assert(c.prefetch_reads == 0 && c.bytes_read == 0);
    K3ExpertQ q; assert(c.src.get(&c.src, 0, 23, &q) != 0);
    assert(c.misses == 1 && c.hits == 0 && c.bytes_read == 0);
    assert(c.src.resident(&c.src, 0, 0, NULL));
    printf("truncated_shard: batch publishes 0/4; reservations released; later get fails; earlier resident expert survives\n");
    k3_cache_free(&c); k3_st_close(&st);
    printf("PASS: 6 cache scenarios; real synthetic-shard I/O; no model inference or device-throughput measurement\n");
    return 0;
}
