#pragma once

#include <cstdint>
#include <map>
#include <optional>
#include <stdexcept>
#include <stop_token>
#include <string>
#include <vector>

namespace torlinkc {

inline constexpr const char* kUserAgent = "torlinkc (+https://git.kristoffersson.info/repos/Torlinkc)";

struct HttpResponse {
  int status = 0;
  std::string body;
  std::map<std::string, std::string> headers;  // lowercase keys

  bool ok() const { return status >= 200 && status < 300; }
  std::optional<std::string> header(const std::string& lowercaseName) const {
    auto it = headers.find(lowercaseName);
    if (it == headers.end()) return std::nullopt;
    return it->second;
  }
};

class HttpError : public std::runtime_error {
 public:
  HttpError(int status, const std::string& message) : std::runtime_error(message), status(status) {}
  int status;
};

struct FetchOptions {
  int retries = 5;
  int baseMs = 500;
  int capMs = 20000;
  // Extra "Name: value" request headers (User-Agent is always sent and
  // shouldn't be repeated here).
  std::vector<std::string> headers;
  // Default-constructed stop_token is never stop_requested(), so passing
  // nothing here behaves exactly as it did before cancellation existed.
  // Checked before each attempt, wired into libcurl's transfer-progress
  // callback to abort mid-request, and used to cut a backoff sleep short --
  // this is what makes a Phase 3 SearchAggregator able to actually cancel a
  // stale search instead of just abandoning it and blocking on join() until
  // curl's own timeout elapses.
  std::stop_token stopToken;
};

// Parses a Retry-After header value (either delay-seconds or an HTTP-date)
// into a millisecond delay. Ported from util/net.ts::parseRetryAfter.
std::optional<std::int64_t> parseRetryAfter(const std::string& value);

// Exponential backoff with full jitter, honoring an explicit Retry-After
// floor when present. Ported from util/net.ts::backoffDelay.
std::int64_t backoffDelay(int attempt, int baseMs, int capMs, std::optional<std::int64_t> retryAfterMs = std::nullopt);

// A GET request with retry/backoff for transient failures, matching
// util/net.ts::fetchResilient's policy: retries on 408/425/429/500/502/503/504,
// honors Retry-After, and treats a 503 from ddos-guard/cloudflare as
// immediately fatal (retrying never helps against those). Phase 1 has no
// cancellation model yet, so there is no AbortSignal equivalent.
HttpResponse fetchResilient(const std::string& url, const FetchOptions& opts = {});

}  // namespace torlinkc
