/**
 * @file storage.hpp
 * @brief Synor Storage SDK for C++
 *
 * Modern C++17 SDK for IPFS-compatible decentralized storage.
 */

#pragma once

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

namespace synor {
namespace storage {

/// Pin status
enum class PinStatus {
    Queued,
    Pinning,
    Pinned,
    Failed,
    Unpinned
};

/// Hash algorithm
enum class HashAlgorithm {
    Sha2_256,
    Blake3
};

/// Entry type
enum class EntryType {
    File,
    Directory
};

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

/// Upload options
struct UploadOptions {
    std::optional<std::string> name;
    bool wrap_with_directory = false;
    HashAlgorithm hash_algorithm = HashAlgorithm::Sha2_256;
    std::optional<int32_t> chunk_size;
    std::optional<int32_t> pin_duration_days;
};

/// Upload response
struct UploadResponse {
    std::string cid;
    int64_t size;
    std::optional<std::string> name;
    std::string created_at;
};

/// Pin information
struct Pin {
    std::string cid;
    std::optional<std::string> name;
    PinStatus status;
    int64_t size;
    std::string created_at;
    std::optional<std::string> expires_at;
    std::vector<std::string> delegates;
};

/// Pin request
struct PinRequest {
    std::string cid;
    std::optional<std::string> name;
    std::optional<int32_t> duration_days;
    std::vector<std::string> origins;
    std::map<std::string, std::string> meta;
};

/// CAR file information
struct CarFile {
    std::string cid;
    int64_t size;
    std::vector<std::string> roots;
    std::string created_at;
};

/// File entry for upload
struct FileEntry {
    std::string path;
    std::vector<uint8_t> content;
};

/// Directory entry
struct DirectoryEntry {
    std::string name;
    std::string cid;
    int64_t size;
    EntryType type;
};

/// Storage statistics
struct StorageStats {
    int64_t total_size;
    int32_t pin_count;
    int64_t bandwidth_used;
    double quota_used;
};

/// Storage client
class Client {
public:
    explicit Client(const Config& config);
    ~Client();

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

    // Upload operations
    UploadResponse upload(std::span<const uint8_t> data,
                          const UploadOptions& options = {});
    std::future<UploadResponse> upload_async(std::vector<uint8_t> data,
                                              UploadOptions options = {});
    UploadResponse upload_directory(const std::vector<FileEntry>& files,
                                     const std::optional<std::string>& dir_name = std::nullopt);

    // Download operations
    std::vector<uint8_t> download(const std::string& cid);
    std::future<std::vector<uint8_t>> download_async(const std::string& cid);
    void download_stream(const std::string& cid,
                         std::function<void(std::span<const uint8_t>)> callback);

    /// Get gateway URL for content
    std::string get_gateway_url(const std::string& cid,
                                 const std::optional<std::string>& path = std::nullopt);

    // Pinning operations
    Pin pin(const PinRequest& request);
    void unpin(const std::string& cid);
    Pin get_pin_status(const std::string& cid);
    std::vector<Pin> list_pins(std::optional<PinStatus> status = std::nullopt,
                               int32_t limit = 50, int32_t offset = 0);

    // CAR file operations
    CarFile create_car(const std::vector<FileEntry>& entries);
    std::vector<std::string> import_car(std::span<const uint8_t> car_data);
    std::vector<uint8_t> export_car(const std::string& cid);

    // Directory operations
    std::vector<DirectoryEntry> list_directory(const std::string& cid);

    // Statistics
    StorageStats get_stats();

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

} // namespace storage
} // namespace synor
