///|
pub struct HarRedactionPolicy {
  sensitive_headers_value : Array[String]
  sensitive_cookies_value : Array[String]
  sensitive_query_value : Array[String]
  replacement_value : String
  redact_post_text_value : Bool
  redact_response_text_value : Bool
} derive(Eq, Debug)

///|
pub fn HarRedactionPolicy::standard() -> HarRedactionPolicy {
  {
    sensitive_headers_value: [
      "authorization", "proxy-authorization", "cookie", "set-cookie", "x-api-key",
    ],
    sensitive_cookies_value: [],
    sensitive_query_value: [
      "access_token", "api_key", "apikey", "password", "secret", "token",
    ],
    replacement_value: "[REDACTED]",
    redact_post_text_value: true,
    redact_response_text_value: false,
  }
}

///|
pub fn HarRedactionPolicy::with_header(
  self : HarRedactionPolicy,
  name : String,
) -> HarRedactionPolicy {
  let values = self.sensitive_headers_value.copy()
  values.push(name.to_lower())
  { ..self, sensitive_headers_value: values }
}

///|
pub fn HarRedactionPolicy::with_cookie(
  self : HarRedactionPolicy,
  name : String,
) -> HarRedactionPolicy {
  let values = self.sensitive_cookies_value.copy()
  values.push(name)
  { ..self, sensitive_cookies_value: values }
}

///|
pub fn HarRedactionPolicy::with_query(
  self : HarRedactionPolicy,
  name : String,
) -> HarRedactionPolicy {
  let values = self.sensitive_query_value.copy()
  values.push(name.to_lower())
  { ..self, sensitive_query_value: values }
}

///|
pub fn HarRedactionPolicy::with_replacement(
  self : HarRedactionPolicy,
  replacement : String,
) -> HarRedactionPolicy {
  { ..self, replacement_value: replacement }
}

///|
pub fn HarRedactionPolicy::keep_post_text(
  self : HarRedactionPolicy,
) -> HarRedactionPolicy {
  { ..self, redact_post_text_value: false }
}

///|
pub fn HarRedactionPolicy::redact_response_text(
  self : HarRedactionPolicy,
) -> HarRedactionPolicy {
  { ..self, redact_response_text_value: true }
}

///|
fn lower_array_contains(values : Array[String], expected : String) -> Bool {
  let lower = expected.to_lower()
  for value in values {
    if value.to_lower() == lower {
      return true
    }
  }
  false
}

///|
fn redact_name_values(
  values : Array[HarNameValue],
  names : Array[String],
  replacement : String,
) -> Array[HarNameValue] {
  let result : Array[HarNameValue] = []
  for value in values {
    result.push({
      name_value: value.name_value,
      value_value: if lower_array_contains(names, value.name_value) {
        replacement
      } else {
        value.value_value
      },
      comment_value: value.comment_value,
    })
  }
  result
}

///|
fn redact_cookies(
  values : Array[HarCookie],
  names : Array[String],
  replacement : String,
) -> Array[HarCookie] {
  let result : Array[HarCookie] = []
  for value in values {
    result.push({
      ..value,
      value_value: if names.is_empty() ||
        lower_array_contains(names, value.name_value) {
        replacement
      } else {
        value.value_value
      },
    })
  }
  result
}

///|
fn redact_post_data(
  post_data : HarPostData,
  replacement : String,
  redact_text : Bool,
) -> HarPostData {
  let params : Array[HarPostParam] = []
  for parameter in post_data.params_value {
    params.push({
      ..parameter,
      value_value: match parameter.value_value {
        Some(_) => Some(replacement)
        None => None
      },
    })
  }
  {
    ..post_data,
    params_value: params,
    text_value: if redact_text && post_data.text_value is Some(_) {
      Some(replacement)
    } else {
      post_data.text_value
    },
  }
}

///|
fn redact_request(
  request : HarRequest,
  policy : HarRedactionPolicy,
) -> HarRequest {
  {
    ..request,
    cookies_value: redact_cookies(
      request.cookies_value,
      policy.sensitive_cookies_value,
      policy.replacement_value,
    ),
    headers_value: redact_name_values(
      request.headers_value,
      policy.sensitive_headers_value,
      policy.replacement_value,
    ),
    query_value: redact_name_values(
      request.query_value,
      policy.sensitive_query_value,
      policy.replacement_value,
    ),
    post_data_value: match request.post_data_value {
      Some(value) =>
        Some(
          redact_post_data(
            value,
            policy.replacement_value,
            policy.redact_post_text_value,
          ),
        )
      None => None
    },
  }
}

///|
fn redact_response(
  response : HarResponse,
  policy : HarRedactionPolicy,
) -> HarResponse {
  {
    ..response,
    cookies_value: redact_cookies(
      response.cookies_value,
      policy.sensitive_cookies_value,
      policy.replacement_value,
    ),
    headers_value: redact_name_values(
      response.headers_value,
      policy.sensitive_headers_value,
      policy.replacement_value,
    ),
    content_value: {
      ..response.content_value,
      text_value: if policy.redact_response_text_value &&
        response.content_value.text_value is Some(_) {
        Some(policy.replacement_value)
      } else {
        response.content_value.text_value
      },
    },
  }
}

///|
fn redact_entry(entry : HarEntry, policy : HarRedactionPolicy) -> HarEntry {
  {
    ..entry,
    request_value: redact_request(entry.request_value, policy),
    response_value: redact_response(entry.response_value, policy),
  }
}

///|
pub fn redact_har(
  document : HarDocument,
  policy? : HarRedactionPolicy = HarRedactionPolicy::standard(),
) -> HarDocument {
  let entries : Array[HarEntry] = []
  for entry in document.entries_value {
    entries.push(redact_entry(entry, policy))
  }
  { ..document, entries_value: entries }
}