// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// One representation, five requests: the fresh one, the repeat, the lost
/// update, the resumed download and the one whose file moved on underneath it.
fn main raise {
  let body = b"the body of a small file, twice the length of its own name"
  let modified = @moondate.http("Sun, 06 Nov 1994 08:49:37 GMT")
  let etag = @conditional.hash(body[:], digest=@sha2.Hasher::new())
  println("ETag           " + etag.encode())
  println("Last-Modified  " + modified.http_text())
  println(
    "the cheap one  " +
    @conditional.stamp(length=body.length().to_int64(), modified~).encode(),
  )
  println("")

  // First time through: nothing is asked, everything is sent.
  verdict("a first GET", @conditional.ask, "GET", etag, modified)

  // The client comes back with what it was given.
  verdict(
    "the same GET, a day later",
    @conditional.Ask::new(none_matching=@conditional.These([etag])),
    "GET",
    etag,
    modified,
  )

  // A write that must not clobber somebody else's: the tag it holds is stale.
  verdict(
    "PUT holding a stale tag",
    @conditional.Ask::new(
      matching=@conditional.These([@conditional.Tag::new("older")]),
    ),
    "PUT",
    etag,
    modified,
  )

  // A create that must not overwrite: `*` asks only whether anything is there.
  verdict(
    "PUT that must not overwrite",
    @conditional.Ask::new(none_matching=@conditional.Any),
    "PUT",
    etag,
    modified,
  )
  println("")

  // A resumed download asks for the rest of the file and states what it has.
  let asking = @conditional.Ask::new(ranging=@conditional.Marked(etag))
  let specs = @range.decode("bytes=32-")
  println("Range          bytes=32-")
  println("If-Range       " + etag.encode())
  partial("still the same file", asking, etag, specs)
  partial(
    "the file changed",
    asking,
    @conditional.hash(b"something else"[:], digest=@sha2.Hasher::new()),
    specs,
  )
}

///|
fn verdict(
  what : String,
  ask : @conditional.Ask,
  verb : String,
  etag : @conditional.Tag,
  modified : @moondate.Moment,
) -> Unit {
  let answer = match ask.evaluate(verb~, etag~, modified~) {
    Go => "200, with the body"
    Fresh => "304, without it"
    Failed => "412"
  }
  println(wide(what) + answer)
}

///|
/// What a server does with a `Range` once `If-Range` has had its say.
fn partial(
  what : String,
  ask : @conditional.Ask,
  etag : @conditional.Tag,
  specs : Array[@range.Spec]?,
) -> Unit {
  if !ask.unchanged(etag~) {
    println(wide(what) + "200, the whole thing again")
    return
  }
  guard specs is Some(specs) else { return }
  guard @range.measure(specs[:], length=58L) is Some(parts) else { return }
  let content = @range.Content::new(parts[0], complete=58L)
  println(wide(what) + "206, Content-Range: " + content.encode())
}

///|
fn wide(text : String) -> String {
  let out = StringBuilder()
  out.write_string(text)
  for i = text.length(); i < 28; i = i + 1 {
    out.write_char(' ')
  }
  out.to_string()
}