#pragma once
#include <memory>
#include <vector>

// ── ISimilarityEngine ─────────────────────────────────────────────────────────
// Backend-agnostic gallery similarity engine for the identity matcher.
//
// The full reference gallery (n_gallery × 512 L2-normalised embeddings) is
// uploaded to the GPU once at construction and stays resident. Per frame, the
// small query matrix (n_faces × 512) is uploaded and a single SGEMM produces the
// similarity matrix S (n_gallery × n_faces, column-major) — i.e. S[g + f*n_gal]
// is cosine_similarity(gallery[g], query[f]).
//
// The GPU math backend (cuBLAS/CUDA or rocBLAS/HIP) is selected at compile time
// by CMake (SAE_GEMM_BACKEND); backends/gemm_backend.cpp provides
// make_similarity_engine(). The core matcher node sees only this interface and
// holds no CUDA/HIP/BLAS headers.
//
// TRACES: AR-026 | SR-001
// The resident matrix GROWS. Per-film expansion (AR-018/AR-019) promotes new
// reference views mid-film, and those have to be scored by the same multiply as
// the baked references rather than by a side loop — "this set is small" is not
// an exception, because the annex grows with cast size and film length. Rows are
// therefore appended to the resident matrix and the next compute() covers baked
// and promoted references alike, in one GEMM. The deferred pass (AR-020) then
// inherits a single contiguous operand to score the TBI queue against.

struct ISimilarityEngine {
    virtual ~ISimilarityEngine() = default;

    // Largest n_faces accepted by compute() per call (bounds GPU buffer sizes).
    virtual int max_faces() const = 0;

    // Rows currently resident: the baked gallery plus every appended promotion.
    // This is compute()'s column stride, and it changes as rows are appended —
    // read it per call rather than caching it across frames.
    virtual int n_gallery() const = 0;

    /// TRACES: AR-026 | SR-001
    /// Append n_rows unit-norm embeddings (row-major, 512 floats each) to the
    /// resident matrix. Amortised O(1) per row: capacity grows geometrically, so
    /// a promotion does not re-upload the gallery. Invalidates any pointer
    /// previously returned by compute().
    virtual void append_rows(const float* rows_row_major, int n_rows) = 0;

    // Compute similarities for n_faces query embeddings.
    // query_row_major: n_faces × 512, row fi at query + fi*512.
    // Returns a pointer to host memory holding S column-major: the gallery
    // similarities for face fi start at result + fi*n_gallery(). The pointer is
    // owned by the engine and valid until the next compute() or append_rows().
    virtual const float* compute(const float* query_row_major, int n_faces) = 0;
};

// gallery_row_major: n_gallery × 512, embedding i at gallery + i*512.
std::unique_ptr<ISimilarityEngine> make_similarity_engine(
    const float* gallery_row_major, int n_gallery, int max_faces);
