#pragma once
// mediadb C++ client — API bindings for the mediadb REST server
// Requires: libhttppp, cJSON

#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>

namespace mediadb {

// ── error ──

class ClientError : public std::runtime_error {
  public:
    explicit ClientError(const std::string& msg) : std::runtime_error(msg) {}
};

// ── data types ──

struct StoreInfo {
    std::string id;
    std::string name;
    std::string created_at;
    int album_count = 0;
};

struct AlbumInfo {
    std::string id;
    std::string name;
    std::string store_id;
    std::string created_at;
    bool is_public = false;
    int media_count = 0;
};

struct MediaInfo {
    std::string id;
    std::string filename;
    std::string kind;           // "image", "video", "audio", "unknown"
    std::string content_type;
    std::string album_id;
    std::string store_id;
    std::string created_at;
    std::uint64_t size_bytes = 0;
};

  struct MediaWithPreview {
    MediaInfo info;
    std::vector<std::uint8_t> preview;
    std::string preview_content_type;
  };

// ── streaming callback ──

/// Called with each chunk of body data.  Return false to abort the stream.
using StreamCallback = std::function<bool(const char* data, size_t len)>;

// ── preview options ──

struct PreviewOptions {
    int width = 0;              // 0 = original
    int height = 0;             // 0 = original
    std::string fmt = "webp";   // webp|png|jpeg|avif|mp4|av1|h265
    int t_seconds = 0;          // seek offset for video
    int duration = 0;           // clip length for video formats (mp4/av1/h265)
};

// ── client ──

class Client {
  public:
    /// @param base_url  e.g. "https://localhost:8443"
    /// @param verify_tls  set false to skip TLS cert verification (self-signed)
    /// @param http3  set true to use HTTP/3 (QUIC) instead of TCP
    explicit Client(const std::string& base_url, bool verify_tls = true, bool http3 = false);
    ~Client();

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

    // ── authentication ──

    /// Login with username/password.  Stores the bearer token internally.
    void login(const std::string& username, const std::string& password);

    /// Set an existing bearer token directly.
    void set_token(const std::string& token);

    /// Get the current bearer token (empty if not logged in).
    const std::string& token() const;

    /// Set max retry attempts for HTTP requests (0 = unlimited).
    void setMaxTries(size_t maxTries);

    // ── stores ──

    StoreInfo              create_store(const std::string& name);
    std::vector<StoreInfo> list_stores();
    StoreInfo              get_store(const std::string& id);
    StoreInfo              rename_store(const std::string& id, const std::string& new_name);
    void                   delete_store(const std::string& id);

    // ── albums ──

    AlbumInfo              create_album(const std::string& store_id, const std::string& name);
    std::vector<AlbumInfo> list_albums(const std::string& store_id);
    AlbumInfo              get_album(const std::string& id);
    AlbumInfo              set_album_public(const std::string& id, bool is_public);
    void                   delete_album(const std::string& id);

    // ── media ──

    /// Import a file already accessible on the server filesystem (POST JSON).
    MediaInfo              add_media(const std::string& album_id, const std::string& server_path);
    std::vector<MediaInfo> list_media(const std::string& album_id);
    MediaInfo              get_media(const std::string& id);
    std::vector<std::uint8_t> get_media_data(const std::string& id);

    /// Stream raw media data in chunks via callback (avoids loading the entire
    /// file into memory).  Returns total bytes streamed.
    std::uint64_t get_media_data_stream(const std::string& id,
                                        const StreamCallback& cb);

    /// Fetch a byte range of raw media data from the server.
    /// Uses the /media/<id>/raw endpoint with HTTP Range header.
    std::vector<std::uint8_t> get_media_data_range(const std::string& id,
                                                    std::uint64_t offset,
                                                    std::uint64_t length);

    /// Get the total size of a media file (via metadata, no data transfer).
    std::uint64_t get_media_size(const std::string& id);

    MediaWithPreview        get_media_with_preview(const std::string& id,
                             const PreviewOptions& opts = {});
    void                   delete_media(const std::string& id);

    // ── upload (multipart/form-data) ──

    /// Upload raw bytes.
    MediaInfo upload(const std::string& album_id,
                     const std::string& filename,
                     const std::vector<std::uint8_t>& data);

    /// Upload from a local file path.
    MediaInfo upload_file(const std::string& album_id,
                          const std::string& local_path);

    // ── preview / transcode ──

    /// Download a preview (image or video clip) as raw bytes.
    /// Returns the bytes and sets out_content_type to the MIME type.
    /// Results are cached client-side (LRU, 128 MB).
    std::vector<std::uint8_t> get_preview(const std::string& media_id,
                                          const PreviewOptions& opts = {},
                                          std::string* out_content_type = nullptr);

    /// Return a byte range from a cached preview.
    /// Fetches and caches the full preview on first call, then serves ranges from cache.
    std::vector<std::uint8_t> get_preview_range(const std::string& media_id,
                                                const PreviewOptions& opts,
                                                std::uint64_t offset,
                                                std::uint64_t length,
                                                std::string* out_content_type = nullptr);

    // ── export / import ──

    /// Export the entire database as a single binary archive.
    /// Returns the raw bytes of the MDE1 archive.
    std::vector<std::uint8_t> export_db();

    /// Stream the export archive in chunks via callback.
    std::uint64_t export_db_stream(const StreamCallback& cb);

    /// Export and write directly to a local file (streamed, low memory).
    void export_db_to_file(const std::string& local_path);

    /// Import a binary archive (MDE1 format) into the database.
    /// Existing entries with the same ID are skipped.
    void import_db(const std::vector<std::uint8_t>& data);

    /// Import from a local file.
    void import_db_from_file(const std::string& local_path);

  private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
};

} // namespace mediadb
