#pragma once

#include <cstddef>
#include <memory>
#include <utility>
#include <string>

namespace qkmeans {
namespace anns {

// Available ANNS backend types
enum class ANNSType {
    LSH,            // Custom LSH implementation (always available)
    FAISS,          // Facebook FAISS (optional)
    HNSW,           // hnswlib HNSW graphs (optional)
    BRUTE_FORCE     // Exact nearest neighbor (for debugging/small k)
};

// Convert ANNSType to string
inline const char* anns_type_name(ANNSType type) {
    switch (type) {
        case ANNSType::LSH: return "LSH";
        case ANNSType::FAISS: return "FAISS";
        case ANNSType::HNSW: return "HNSW";
        case ANNSType::BRUTE_FORCE: return "BRUTE_FORCE";
        default: return "UNKNOWN";
    }
}

// Configuration for ANNS backends
struct ANNSConfig {
    size_t dimension = 0;
    size_t expected_size = 1000;

    // LSH-specific
    size_t lsh_num_tables = 10;
    size_t lsh_num_hashes = 8;
    double lsh_rho = 0.5;

    // HNSW-specific
    size_t hnsw_M = 16;
    size_t hnsw_ef_construction = 200;
    size_t hnsw_ef_search = 50;

    // FAISS-specific
    int faiss_index_type = 0;           // 0=FLAT, 1=HNSW, 2=LSH, 3=IVF
    size_t faiss_hnsw_M = 32;           // HNSW: number of links per node
    size_t faiss_hnsw_efConstruction = 40;  // HNSW: construction-time search depth
    size_t faiss_hnsw_efSearch = 16;    // HNSW: search-time depth
    size_t faiss_lsh_nbits = 0;         // LSH: number of bits (0 = auto: 2*dim)
    size_t faiss_nlist = 100;           // IVF: number of inverted lists
    size_t faiss_nprobe = 10;           // IVF: number of lists to probe
};

// Result of a nearest neighbor query
struct QueryResult {
    size_t index;           // Index of the nearest point
    double distance_sq;     // Squared distance to the nearest point
};

// Abstract base class for ANNS backends
class ANNSBase {
public:
    virtual ~ANNSBase() = default;

    // Insert a point into the index
    // point: pointer to d-dimensional vector
    // idx: identifier for the point (usually its index in the original data)
    virtual void insert(const double* point, size_t idx) = 0;

    // Query the approximate nearest neighbor
    // point: pointer to d-dimensional query vector
    // Returns: (index, squared_distance) of the nearest point
    virtual QueryResult query(const double* point) const = 0;

    // Batch query for efficiency (optional override)
    // Default implementation calls query() in a loop
    virtual void batch_query(
        const double* points,
        size_t n,
        size_t* indices_out,
        double* distances_out
    ) const;

    // Get the number of points currently in the index
    virtual size_t size() const = 0;

    // Get the dimension of the points
    virtual size_t dimension() const = 0;

    // Clear all points from the index
    virtual void clear() = 0;

    // Reserve space for expected number of points
    virtual void reserve(size_t n) = 0;

    // Factory method to create ANNS backend
    static std::unique_ptr<ANNSBase> create(
        ANNSType type,
        const ANNSConfig& config
    );

    // Check if a backend type is available
    static bool is_available(ANNSType type);
};

} // namespace anns
} // namespace qkmeans
