///|
/// Cache directives used by both request and response policy. Unknown
/// extensions are intentionally ignored; malformed delta-seconds never become
/// permissive values.
pub(all) struct CacheControl {
  mut no_cache : Bool
  mut no_store : Bool
  mut is_private : Bool
  mut is_public : Bool
  mut must_revalidate : Bool
  mut proxy_revalidate : Bool
  mut immutable : Bool
  mut only_if_cached : Bool
  mut max_age : DeltaSeconds?
  mut shared_max_age : DeltaSeconds?
  mut min_fresh : DeltaSeconds?
  mut max_stale : DeltaSeconds?
  mut max_stale_unbounded : Bool
  mut stale_if_error : DeltaSeconds?
  mut stale_while_revalidate : DeltaSeconds?
  mut malformed_delta : Bool
} derive(Eq, Debug)

///|
pub fn CacheControl::new() -> CacheControl {
  CacheControl::{
    no_cache: false,
    no_store: false,
    is_private: false,
    is_public: false,
    must_revalidate: false,
    proxy_revalidate: false,
    immutable: false,
    only_if_cached: false,
    max_age: None,
    shared_max_age: None,
    min_fresh: None,
    max_stale: None,
    max_stale_unbounded: false,
    stale_if_error: None,
    stale_while_revalidate: None,
    malformed_delta: false,
  }
}

///|
fn unquote_directive_value(value : String) -> String {
  let trimmed = value.trim(chars=" \t").to_owned()
  if trimmed.length() >= 2 &&
    trimmed.has_prefix("\"") &&
    trimmed.has_suffix("\"") {
    trimmed.sub(start=1, end=trimmed.length() - 1).to_owned()
  } else {
    trimmed
  }
}

///|
/// Parse an RFC delta-seconds value. Negative, signed, fractional, and malformed
/// values are rejected. Values that fit Int64 are represented without wrapping.
pub fn parse_delta_seconds(value : String) -> DeltaSeconds? {
  let cleaned = unquote_directive_value(value)
  if cleaned == "" || cleaned.has_prefix("-") || cleaned.has_prefix("+") {
    return None
  }
  for ch in cleaned {
    if ch < '0' || ch > '9' {
      return None
    }
  }
  let parsed : Int64? = Some(@strconv.from_str(cleaned)) catch { _ => None }
  match parsed {
    Some(seconds) => Some(DeltaSeconds::from_seconds(seconds))
    None => None
  }
}

///|
fn minimum_delta(
  current : DeltaSeconds?,
  candidate : DeltaSeconds,
) -> DeltaSeconds {
  match current {
    Some(existing) => existing.min(candidate)
    None => candidate
  }
}

///|
fn parse_directive_delta(
  result : CacheControl,
  raw_value : String?,
) -> DeltaSeconds? {
  match raw_value {
    Some(value) =>
      match parse_delta_seconds(value) {
        Some(delta) => Some(delta)
        None => {
          result.malformed_delta = true
          None
        }
      }
    None => {
      result.malformed_delta = true
      None
    }
  }
}

///|
/// Parse the directive subset needed by MoonCache. Directive names are
/// case-insensitive and repeated numeric directives resolve to the smallest
/// valid value, a conservative deterministic choice.
pub fn parse_cache_control(value : String) -> CacheControl {
  let result = CacheControl::new()
  for part_view in value.split(",") {
    let part = part_view.trim(chars=" \t").to_owned()
    if part == "" {
      continue
    }
    let split = part.split_once("=")
    let name = match split {
      Some((name, _)) => name.trim(chars=" \t").to_lower().to_owned()
      None => part.to_lower()
    }
    let raw_value = match split {
      Some((_, directive_value)) =>
        Some(directive_value.trim(chars=" \t").to_owned())
      None => None
    }
    match name {
      "no-cache" => result.no_cache = true
      "no-store" => result.no_store = true
      "private" => result.is_private = true
      "public" => result.is_public = true
      "must-revalidate" => result.must_revalidate = true
      "proxy-revalidate" => result.proxy_revalidate = true
      "immutable" => result.immutable = true
      "only-if-cached" => result.only_if_cached = true
      "max-age" =>
        match parse_directive_delta(result, raw_value) {
          Some(delta) =>
            result.max_age = Some(minimum_delta(result.max_age, delta))
          None => ()
        }
      "s-maxage" =>
        match parse_directive_delta(result, raw_value) {
          Some(delta) =>
            result.shared_max_age = Some(
              minimum_delta(result.shared_max_age, delta),
            )
          None => ()
        }
      "min-fresh" =>
        match parse_directive_delta(result, raw_value) {
          Some(delta) =>
            result.min_fresh = Some(minimum_delta(result.min_fresh, delta))
          None => ()
        }
      "max-stale" =>
        match raw_value {
          None => result.max_stale_unbounded = true
          Some(_) =>
            match parse_directive_delta(result, raw_value) {
              Some(delta) =>
                result.max_stale = Some(minimum_delta(result.max_stale, delta))
              None => ()
            }
        }
      "stale-if-error" =>
        match parse_directive_delta(result, raw_value) {
          Some(delta) =>
            result.stale_if_error = Some(
              minimum_delta(result.stale_if_error, delta),
            )
          None => ()
        }
      "stale-while-revalidate" =>
        match parse_directive_delta(result, raw_value) {
          Some(delta) =>
            result.stale_while_revalidate = Some(
              minimum_delta(result.stale_while_revalidate, delta),
            )
          None => ()
        }
      _ => ()
    }
  }
  result
}

///|
pub fn cache_control_from_headers(headers : HeaderMap) -> CacheControl {
  parse_cache_control(headers.get("cache-control").unwrap_or(""))
}

///|
/// RFC 9111 retains the older `Pragma: no-cache` request behavior when
/// Cache-Control is absent. This helper keeps that compatibility isolated.
pub fn request_has_legacy_no_cache(headers : HeaderMap) -> Bool {
  if headers.contains("cache-control") {
    return false
  }
  match headers.get("pragma") {
    Some(value) =>
      value
      .split(",")
      .any(fn(part) { part.trim(chars=" \t").to_lower().equal("no-cache") })
    None => false
  }
}