///|
pub fn parse_dkim(text : String) -> DkimRecord {
  let raw = clean(text)
  let version = lower(tag_value(raw, "v"))
  let key_type = normalize_dkim_key_type(tag_value(raw, "k"))
  let public_key = tag_value(raw, "p")
  let testing_flags = lower(tag_value(raw, "t"))
  let services = tag_value(raw, "s")
  let hashes = tag_value(raw, "h")
  let notes = tag_value(raw, "n")
  let valid_version = version == "dkim1"
  let key_length_hint = rough_base64_bits(public_key)
  let public_key_length = public_key.length()
  let testing_mode = dkim_testing_mode(testing_flags)
  let service_count = dkim_list_count(services)
  let hash_count = dkim_list_count(hashes)
  let findings = dkim_findings(
    raw,
    valid_version,
    has_tag(raw, "v"),
    key_type,
    public_key,
    key_length_hint,
    testing_mode,
    services,
    hashes,
    notes,
  )
  {
    raw,
    valid_version,
    key_type,
    key_length_hint,
    public_key_length,
    testing_mode,
    service_count,
    hash_count,
    notes,
    findings,
  }
}

///|
fn normalize_dkim_key_type(value : String) -> String {
  let item = lower(value.trim().to_owned())
  if item.is_empty() {
    "rsa"
  } else {
    item
  }
}

///|
fn dkim_testing_mode(flags : String) -> Bool {
  csv_contains_token(flags.replace_all(old=":", new=","), "y")
}

///|
fn dkim_list_count(value : String) -> Int {
  if value.trim().is_empty() {
    0
  } else {
    let normalized = value.replace_all(old=":", new=",")
    csv_count(normalized)
  }
}

///|
fn dkim_findings(
  raw : String,
  valid_version : Bool,
  version_tag_present : Bool,
  key_type : String,
  public_key : String,
  key_length_hint : Int,
  testing_mode : Bool,
  services : String,
  hashes : String,
  notes : String,
) -> Array[PolicyFinding] {
  let findings = Array::new(capacity=12)
  if raw.is_empty() {
    findings.push(
      fail(
        "dkim.record-missing", "DKIM selector record is missing", "A sending selector needs a DKIM TXT record.",
        "", "Publish a DKIM selector record with v=DKIM1 and p=public-key.",
      ),
    )
    return findings
  }
  if valid_version {
    findings.push(pass("dkim.version", "DKIM version is present", "v=DKIM1"))
  } else if version_tag_present {
    findings.push(
      fail(
        "dkim.version-invalid",
        "DKIM version is invalid",
        "The v tag should be DKIM1 when present.",
        tag_value(raw, "v"),
        "Use v=DKIM1 or omit the tag only if the receiver profile allows it.",
      ),
    )
  } else {
    findings.push(
      warn(
        "dkim.version-missing",
        "DKIM version tag is missing",
        "Many DKIM records include v=DKIM1 for clarity.",
        "",
        "Add v=DKIM1 before the key tags.",
        penalty=3,
      ),
    )
  }
  if key_type == "rsa" || key_type == "ed25519" {
    findings.push(pass("dkim.key-type", "DKIM key type is supported", key_type))
  } else {
    findings.push(
      fail(
        "dkim.key-type-invalid", "DKIM key type is unsupported", "MailShield recognizes rsa and ed25519 selector keys.",
        key_type, "Use k=rsa or k=ed25519.",
      ),
    )
  }
  if public_key.is_empty() {
    findings.push(
      fail(
        "dkim.public-key-missing", "DKIM public key is missing", "The p tag carries the selector public key.",
        "", "Publish a p= value for this selector.",
      ),
    )
  } else if public_key == "" {
    findings.push(
      fail(
        "dkim.public-key-empty", "DKIM public key is empty", "An empty p tag revokes a selector and cannot sign mail.",
        "p=", "Rotate to a selector that contains a valid public key.",
      ),
    )
  } else {
    findings.push(
      pass(
        "dkim.public-key",
        "DKIM public key is present",
        public_key_length_text(public_key),
      ),
    )
  }
  if key_type == "rsa" && key_length_hint > 0 && key_length_hint < 1024 {
    findings.push(
      fail(
        "dkim.rsa-short",
        "DKIM RSA key appears too short",
        "RSA selector keys below 1024 bits are weak.",
        key_length_hint.to_string() + " bit hint",
        "Rotate to a 2048-bit RSA selector or an ed25519 selector.",
      ),
    )
  } else if key_type == "rsa" &&
    key_length_hint >= 1024 &&
    key_length_hint < 2048 {
    findings.push(
      warn(
        "dkim.rsa-legacy",
        "DKIM RSA key appears legacy sized",
        "1024-bit RSA is widely accepted but leaves less future margin.",
        key_length_hint.to_string() + " bit hint",
        "Plan a selector rotation to 2048-bit RSA.",
      ),
    )
  } else if key_length_hint >= 2048 {
    findings.push(
      pass(
        "dkim.key-length",
        "DKIM key length hint is strong",
        key_length_hint.to_string() + " bit hint",
      ),
    )
  }
  if testing_mode {
    findings.push(
      warn(
        "dkim.testing-mode",
        "DKIM selector is in testing mode",
        "t=y tells receivers this selector is being tested.",
        "t=" + tag_value(raw, "t"),
        "Remove t=y after the selector is serving production mail.",
      ),
    )
  } else {
    findings.push(pass("dkim.testing-mode", "DKIM testing flag is not set", ""))
  }
  if services.is_empty() {
    findings.push(
      pass("dkim.service", "DKIM service scope uses the default", "email"),
    )
  } else if services == "*" || list_contains_token(services, "*") {
    findings.push(
      warn(
        "dkim.service-wildcard",
        "DKIM service scope is wildcarded",
        "A wildcard service scope is broad and can hide configuration drift.",
        services,
        "Prefer s=email when the selector is used for email signing.",
        penalty=3,
      ),
    )
  } else if list_contains_token(services, "email") {
    findings.push(
      pass("dkim.service", "DKIM service scope includes email", services),
    )
  } else {
    findings.push(
      warn(
        "dkim.service-no-email", "DKIM service scope does not include email", "A selector without email scope may not be useful for mail signing.",
        services, "Set s=email or remove the s tag.",
      ),
    )
  }
  if hashes.is_empty() {
    findings.push(
      pass("dkim.hash", "DKIM hash algorithms use receiver defaults", ""),
    )
  } else if list_contains_token(hashes, "sha1") &&
    !list_contains_token(hashes, "sha256") {
    findings.push(
      fail(
        "dkim.hash-sha1-only", "DKIM hash list allows only SHA-1", "SHA-1 is obsolete for modern mail authentication.",
        hashes, "Allow sha256 and remove sha1-only policy.",
      ),
    )
  } else if list_contains_token(hashes, "sha1") {
    findings.push(
      warn(
        "dkim.hash-sha1", "DKIM hash list still includes SHA-1", "Keeping SHA-1 broadens legacy behavior.",
        hashes, "Prefer sha256-only when all senders support it.",
      ),
    )
  } else if list_contains_token(hashes, "sha256") {
    findings.push(pass("dkim.hash", "DKIM hash list includes SHA-256", hashes))
  } else {
    findings.push(
      warn(
        "dkim.hash-unknown", "DKIM hash list contains unknown algorithms", "Receivers may ignore unsupported hash names.",
        hashes, "Use sha256 unless a specific receiver profile requires otherwise.",
      ),
    )
  }
  if notes.length() > 120 {
    findings.push(
      warn(
        "dkim.notes-long",
        "DKIM notes tag is unusually long",
        "Long n tags make TXT records harder to operate and may increase DNS fragmentation risk.",
        notes.length().to_string() + " chars",
        "Move operational notes to documentation and keep DNS compact.",
        penalty=2,
      ),
    )
  }
  findings
}

///|
fn public_key_length_text(public_key : String) -> String {
  public_key.length().to_string() + " chars"
}

///|
pub fn DkimRecord::is_configured(self : DkimRecord) -> Bool {
  !self.raw.is_empty() && self.public_key_length > 0
}

///|
pub fn DkimRecord::uses_rsa(self : DkimRecord) -> Bool {
  self.key_type == "rsa"
}

///|
pub fn DkimRecord::uses_ed25519(self : DkimRecord) -> Bool {
  self.key_type == "ed25519"
}

///|
pub fn DkimRecord::is_testing(self : DkimRecord) -> Bool {
  self.testing_mode
}

///|
pub fn DkimRecord::key_strength_label(self : DkimRecord) -> String {
  if self.key_type == "ed25519" && self.public_key_length > 0 {
    "modern"
  } else if self.key_length_hint >= 2048 {
    "strong"
  } else if self.key_length_hint >= 1024 {
    "legacy"
  } else if self.public_key_length > 0 {
    "weak"
  } else {
    "missing"
  }
}