/**
 * @file database.hpp
 * @brief Synor Database SDK for C++
 *
 * Modern C++17 SDK for multi-model database with Key-Value, Document, Vector, and Time Series stores.
 */

#pragma once

#include <string>
#include <vector>
#include <optional>
#include <future>
#include <memory>
#include <map>
#include <cstdint>
#include "wallet.hpp"  // For SynorException

namespace synor {
namespace database {

/// Database configuration
struct Config {
    std::string api_key;
    std::string endpoint = "https://db.synor.io/v1";
    uint32_t timeout_ms = 60000;
    uint32_t retries = 3;
    bool debug = false;
};

/// A key-value entry
struct KeyValue {
    std::string key;
    std::string value;  // JSON string
    std::optional<int32_t> ttl;
    std::optional<int64_t> created_at;
    std::optional<int64_t> updated_at;
};

/// A document
struct Document {
    std::string id;
    std::string collection;
    std::map<std::string, std::string> data;  // JSON data
    int64_t created_at;
    int64_t updated_at;
};

/// Query for documents
struct Query {
    std::optional<std::string> filter;  // JSON
    std::optional<std::string> sort;    // JSON
    std::optional<int32_t> limit;
    std::optional<int32_t> offset;
    std::optional<std::vector<std::string>> projection;
};

/// A vector entry
struct VectorEntry {
    std::string id;
    std::vector<double> vector;
    std::optional<std::map<std::string, std::string>> metadata;
};

/// A search result
struct SearchResult {
    std::string id;
    double score;
    std::optional<std::vector<double>> vector;
    std::optional<std::map<std::string, std::string>> metadata;
};

/// A time series data point
struct DataPoint {
    int64_t timestamp;
    double value;
    std::optional<std::map<std::string, std::string>> tags;
};

/// Time range for queries
struct TimeRange {
    int64_t start;
    int64_t end;
};

/// Aggregation settings
struct Aggregation {
    std::string function;  // avg, sum, min, max, count
    std::string interval;  // 1m, 5m, 1h, 1d
};

// Forward declaration
class SynorDatabaseImpl;

/// Key-Value store interface
class KeyValueStore {
public:
    explicit KeyValueStore(std::shared_ptr<SynorDatabaseImpl> impl);

    std::future<std::optional<std::string>> get(const std::string& key);
    std::future<void> set(const std::string& key, const std::string& value,
                         std::optional<int32_t> ttl = std::nullopt);
    std::future<void> remove(const std::string& key);
    std::future<std::vector<KeyValue>> list(const std::string& prefix);

private:
    std::shared_ptr<SynorDatabaseImpl> impl_;
};

/// Document store interface
class DocumentStore {
public:
    explicit DocumentStore(std::shared_ptr<SynorDatabaseImpl> impl);

    std::future<std::string> create(const std::string& collection, const std::string& document);
    std::future<Document> get(const std::string& collection, const std::string& id);
    std::future<void> update(const std::string& collection, const std::string& id,
                            const std::string& update);
    std::future<void> remove(const std::string& collection, const std::string& id);
    std::future<std::vector<Document>> query(const std::string& collection, const Query& query);

private:
    std::shared_ptr<SynorDatabaseImpl> impl_;
};

/// Vector store interface
class VectorStore {
public:
    explicit VectorStore(std::shared_ptr<SynorDatabaseImpl> impl);

    std::future<void> upsert(const std::string& collection, const std::vector<VectorEntry>& vectors);
    std::future<std::vector<SearchResult>> search(const std::string& collection,
                                                   const std::vector<double>& vector, int32_t k);
    std::future<void> remove(const std::string& collection, const std::vector<std::string>& ids);

private:
    std::shared_ptr<SynorDatabaseImpl> impl_;
};

/// Time series store interface
class TimeSeriesStore {
public:
    explicit TimeSeriesStore(std::shared_ptr<SynorDatabaseImpl> impl);

    std::future<void> write(const std::string& series, const std::vector<DataPoint>& points);
    std::future<std::vector<DataPoint>> query(const std::string& series, const TimeRange& range,
                                              std::optional<Aggregation> aggregation = std::nullopt);

private:
    std::shared_ptr<SynorDatabaseImpl> impl_;
};

/// Synor Database client
class SynorDatabase {
public:
    explicit SynorDatabase(const Config& config);
    ~SynorDatabase();

    SynorDatabase(const SynorDatabase&) = delete;
    SynorDatabase& operator=(const SynorDatabase&) = delete;
    SynorDatabase(SynorDatabase&&) noexcept;
    SynorDatabase& operator=(SynorDatabase&&) noexcept;

    /// Key-Value store operations
    KeyValueStore& kv();

    /// Document store operations
    DocumentStore& documents();

    /// Vector store operations
    VectorStore& vectors();

    /// Time series store operations
    TimeSeriesStore& timeseries();

    /// Close the client
    void close();

    /// Check if the client is closed
    bool is_closed() const;

    /// Perform a health check
    std::future<bool> health_check();

private:
    std::shared_ptr<SynorDatabaseImpl> impl_;
    std::unique_ptr<KeyValueStore> kv_;
    std::unique_ptr<DocumentStore> documents_;
    std::unique_ptr<VectorStore> vectors_;
    std::unique_ptr<TimeSeriesStore> timeseries_;
};

}  // namespace database
}  // namespace synor
