///|
/// Redaction options for reports.
pub(all) struct RedactionPolicy {
  redact_query : Bool
  redact_tokens : Bool
  redact_cookies : Bool
  redact_domains : Bool
  replacement : String
} derive(Eq, Debug)

///|
pub fn RedactionPolicy::default() -> RedactionPolicy {
  RedactionPolicy::{
    redact_query: true,
    redact_tokens: true,
    redact_cookies: true,
    redact_domains: false,
    replacement: "",
  }
}

///|
pub fn redact_url(url : String, policy : RedactionPolicy) -> String {
  let mut result = url
  if policy.redact_query {
    result = strip_query(result)
  }
  if policy.redact_tokens {
    result = redact_token_segments(result, policy.replacement)
  }
  if policy.redact_domains {
    result = replace_domain(result, policy.replacement)
  }
  result
}

///|
pub fn strip_query(url : String) -> String {
  match url.split_once("?") {
    Some((left, _)) => left.to_owned()
    None => url
  }
}

///|
pub fn redact_token_segments(url : String, replacement : String) -> String {
  let parts = url.split("/").collect()
  let mut out = ""
  for i = 0; i < parts.length(); i = i + 1 {
    if i > 0 {
      out = out + "/"
    }
    let part = parts[i].to_owned()
    if looks_sensitive_segment(part) {
      out = out + replacement
    } else {
      out = out + part
    }
  }
  out
}

///|
fn looks_sensitive_segment(part : String) -> Bool {
  part.length() >= 16 ||
  part.to_lower().contains("token") ||
  part.to_lower().contains("secret") ||
  part.to_lower().contains("session")
}

///|
pub fn replace_domain(url : String, replacement : String) -> String {
  match url.split_once("://") {
    Some((scheme, rest)) =>
      match rest.to_owned().split_once("/") {
        Some((_, path)) =>
          scheme.to_owned() + "://" + replacement + "/" + path.to_owned()
        None => scheme.to_owned() + "://" + replacement
      }
    None => replacement
  }
}

///|
pub fn redact_pair(pair : HarPair, policy : RedactionPolicy) -> HarPair {
  let lower = pair.name.to_lower()
  if policy.redact_cookies && (lower == "cookie" || lower == "set-cookie") {
    HarPair::{ name: pair.name, value: policy.replacement }
  } else if policy.redact_tokens &&
    (lower.contains("token") || lower.contains("authorization")) {
    HarPair::{ name: pair.name, value: policy.replacement }
  } else {
    pair
  }
}

///|
pub fn redact_pairs(
  pairs : Array[HarPair],
  policy : RedactionPolicy,
) -> Array[HarPair] {
  let out : Array[HarPair] = []
  for i = 0; i < pairs.length(); i = i + 1 {
    out.push(redact_pair(pairs[i], policy))
  }
  out
}

///|
pub fn render_redacted_slow_entries(
  summary : HarSummary,
  policy : RedactionPolicy,
) -> String {
  let mut out = ""
  for i = 0; i < summary.slow_entries.length(); i = i + 1 {
    let entry = summary.slow_entries[i]
    out = out + entry.time.to_string() + "ms "
    out = out + entry.http_method + " "
    out = out + redact_url(entry.url, policy) + "\n"
  }
  out
}