#ifndef W3CS_WEBP_CACHE_H
#define W3CS_WEBP_CACHE_H

#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <vector>

namespace w3cs {

using WebpBytes = std::vector<uint8_t>;

struct WebpSource {
  uint32_t width = 0;
  uint32_t height = 0;
  uint32_t pitch = 0;
  uint32_t format = 0;
  WebpBytes pixels;
};

struct WebpPolicy {
  uint8_t quality = 95;
  uint8_t method = 0;
};

struct WebpRequest {
  std::array<uint8_t, 32> digest{};
  std::string key;
  uint32_t encoder_version = 0;
  WebpPolicy policy;
  uint32_t width = 0;
  uint32_t height = 0;
  uint32_t pitch = 0;
  uint32_t format = 0;
  uint32_t decoded_size = 0;
};

/*
 * Hashing the source pixels dominates cache-key work for large textures.
 * Prepare this identity once, then derive every quality-specific request from
 * it without reading the source pixels again.
 */
struct WebpPreparedSource {
  std::array<uint8_t, 32> source_digest{};
  uint32_t encoder_version = 0;
  uint32_t width = 0;
  uint32_t height = 0;
  uint32_t pitch = 0;
  uint32_t format = 0;
  uint32_t decoded_size = 0;
};

struct WebpCacheStats {
  uint64_t hits = 0;
  uint64_t misses = 0;
  uint64_t denied = 0;
  uint64_t deny_queued = 0;
  uint64_t deny_queue_drops = 0;
  uint64_t deny_write_errors = 0;
  uint64_t deny_pending = 0;
  uint64_t deny_queue_limit = 0;
  uint64_t queued = 0;
  uint64_t queue_drops = 0;
  uint64_t pending_jobs = 0;
  uint64_t pending_bytes = 0;
  uint64_t queue_limit_bytes = 0;
  uint64_t encoded = 0;
  uint64_t encode_errors = 0;
  uint64_t corrupt_entries = 0;
  uint64_t source_bytes = 0;
  uint64_t encoded_bytes = 0;
  uint64_t encode_microseconds = 0;
  uint64_t key_bytes = 0;
  uint64_t key_microseconds = 0;
  uint64_t disk_loads = 0;
  uint64_t disk_loaded_bytes = 0;
  uint64_t fallback_disk_loads = 0;
  uint64_t fallback_disk_loaded_bytes = 0;
  uint64_t disk_scans = 0;
  uint64_t memory_entries = 0;
  uint64_t memory_bytes = 0;
  uint64_t memory_limit_bytes = 0;
  uint64_t memory_busy_misses = 0;
  uint64_t memory_evictions = 0;
  uint64_t memory_evicted_bytes = 0;
  uint64_t webp_entries = 0;
  uint64_t webp_bytes = 0;
  uint64_t deny_entries = 0;
  uint64_t deny_bytes = 0;
  uint64_t disk_limit_bytes = 0;
  uint64_t disk_entry_limit = 0;
  uint64_t disk_evictions = 0;
  uint64_t disk_evicted_bytes = 0;
  uint64_t temp_entries = 0;
  uint64_t temp_bytes = 0;
  uint64_t temp_entry_limit = 0;
  uint64_t temp_byte_limit = 0;
  uint64_t temp_evictions = 0;
  uint64_t temp_evicted_bytes = 0;
  uint64_t stale_temp_removals = 0;
  uint64_t stale_temp_bytes = 0;
};

/*
 * One instance belongs to one relay process, but encoder work is bounded
 * across every seat on the host by advisory slot locks inside cache_dir.
 * Cache lookup only checks a bounded in-memory front and never performs disk
 * I/O or waits for an encoder. A miss stays on the exact raw stream. The
 * background worker loads a disk entry or encodes one for a later session.
 */
class WebpCache {
 public:
  WebpCache(std::string cache_dir, unsigned host_slots,
            size_t queue_byte_limit, size_t cache_byte_limit);
  ~WebpCache();

  WebpCache(const WebpCache &) = delete;
  WebpCache &operator=(const WebpCache &) = delete;

  bool enabled() const;
  WebpPreparedSource prepare_source(const WebpSource &source) const;
  WebpRequest prepare(const WebpPreparedSource &source,
                      const WebpPolicy &policy) const;
  WebpRequest prepare(const WebpSource &source,
                      const WebpPolicy &policy) const;
  std::optional<WebpBytes> lookup(const WebpRequest &request);
  /* Queue the requested variant for a background disk load or encode. When
   * read_fallback is present, the worker can publish that existing disk
   * entry while it warms the requested variant. It never encodes the
   * fallback request. */
  bool submit(const WebpRequest &request, WebpSource source,
              std::optional<WebpRequest> read_fallback = std::nullopt);
  void deny(const WebpRequest &request);
  WebpCacheStats stats() const;

  static bool supported_source(const WebpSource &source);
  static std::optional<WebpBytes> decode_source_rgba(
      const WebpSource &source);
  static std::optional<WebpBytes> encode_rgba(
      const WebpBytes &rgba, uint32_t width, uint32_t height,
      const WebpPolicy &policy);

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

}  // namespace w3cs

#endif
