///|
pub fn parse_spf(text : String) -> SpfRecord {
  let raw = clean(text)
  let tokens = raw.split(" ").to_array()
  let mut valid_version = false
  let mechanisms = Array::new(capacity=tokens.length())
  let mut redirect = ""
  let mut explanation = ""
  let mut dns_lookup_count = 0
  let mut all_qualifier = ""

  for i in 0.. SpfMechanism {
  let qualifier = spf_qualifier(token)
  let body = strip_spf_qualifier(token)
  let colon_parts = body.split(":").to_array()
  let left = if colon_parts.length() > 0 {
    colon_parts[0].to_owned()
  } else {
    body
  }
  let slash_parts = left.split("/").to_array()
  let name = lower(
    if slash_parts.length() > 0 {
      slash_parts[0].to_owned()
    } else {
      left
    },
  )
  let value = if colon_parts.length() >= 2 {
    colon_parts[1].split("/").to_array()[0].to_owned()
  } else {
    ""
  }
  {
    raw: token,
    qualifier,
    name,
    value,
    dns_lookup: spf_dns_lookup(name),
    terminal: name == "all",
  }
}

///|
fn spf_qualifier(token : String) -> String {
  if token.has_prefix("+") ||
    token.has_prefix("-") ||
    token.has_prefix("~") ||
    token.has_prefix("?") {
    token[0:1].to_owned()
  } else {
    "+"
  }
}

///|
fn strip_spf_qualifier(token : String) -> String {
  if token.has_prefix("+") ||
    token.has_prefix("-") ||
    token.has_prefix("~") ||
    token.has_prefix("?") {
    token[1:token.length()].to_owned()
  } else {
    token
  }
}

///|
fn spf_dns_lookup(name : String) -> Bool {
  name == "include" ||
  name == "a" ||
  name == "mx" ||
  name == "ptr" ||
  name == "exists"
}

///|
fn spf_findings(
  valid_version : Bool,
  mechanisms : Array[SpfMechanism],
  redirect : String,
  dns_lookup_count : Int,
  all_qualifier : String,
) -> Array[PolicyFinding] {
  let findings = Array::new(capacity=8)
  if valid_version {
    findings.push(pass("spf.version", "SPF version is present", "v=spf1"))
  } else {
    findings.push(
      fail(
        "spf.version", "SPF version is missing", "A valid SPF record must start with v=spf1.",
        "", "Publish a TXT record that starts with v=spf1.",
      ),
    )
  }
  if dns_lookup_count > 10 {
    findings.push(
      fail(
        "spf.lookup-limit",
        "SPF DNS lookup limit is exceeded",
        "SPF evaluation should stay within 10 DNS-query mechanisms.",
        dns_lookup_count.to_string(),
        "Remove unnecessary include, a, mx, ptr, exists or redirect mechanisms.",
      ),
    )
  } else if dns_lookup_count >= 8 {
    findings.push(
      warn(
        "spf.lookup-headroom",
        "SPF DNS lookup headroom is low",
        "The record is close to the 10-lookup limit.",
        dns_lookup_count.to_string(),
        "Keep at least a little lookup budget for future mail providers.",
      ),
    )
  } else {
    findings.push(
      pass(
        "spf.lookup-limit",
        "SPF DNS lookup count is within limit",
        dns_lookup_count.to_string(),
      ),
    )
  }
  if all_qualifier == "-" {
    findings.push(pass("spf.all", "SPF ends with hard fail", "-all"))
  } else if all_qualifier == "~" {
    findings.push(
      warn(
        "spf.softfail", "SPF uses soft fail", "Soft fail allows receivers to treat unauthorized senders leniently.",
        "~all", "Use -all after confirming all legitimate senders are listed.",
      ),
    )
  } else if all_qualifier == "+" {
    findings.push(
      fail(
        "spf.open-all", "SPF allows every sender", "+all permits any host to send mail for the domain.",
        "+all", "Replace +all with -all or a carefully scoped policy.",
      ),
    )
  } else if all_qualifier == "?" {
    findings.push(
      warn(
        "spf.neutral-all", "SPF ends with neutral policy", "?all gives weak receiver guidance.",
        "?all", "Use -all or ~all according to rollout readiness.",
      ),
    )
  } else if !redirect.is_empty() {
    findings.push(
      pass("spf.redirect", "SPF delegates policy with redirect", redirect),
    )
  } else {
    findings.push(
      warn(
        "spf.no-all", "SPF has no terminal all mechanism", "Without all or redirect, unmatched senders are harder to classify.",
        "", "Add -all, ~all or redirect after all legitimate senders are listed.",
      ),
    )
  }
  for mechanism in mechanisms {
    if mechanism.name == "ptr" {
      findings.push(
        warn(
          "spf.ptr",
          "SPF uses ptr mechanism",
          "ptr is slow and fragile for receiver-side policy evaluation.",
          mechanism.raw,
          "Replace ptr with include, ip4, ip6, a or mx mechanisms.",
        ),
      )
    }
  }
  findings
}