// DKIM 验签(RFC 6376)。
//
// 流程:解析 DKIM-Signature 标签 → 规范化正文并比对 bh → 按 h= 选择头部并规范化
// → 拼接(含去掉 b= 值的签名头)→ 取 DNS key → RSA 验签。

///|
pub struct DkimSignature {
  domain : String
  selector : String
  algorithm : String
  header_canon : CanonMode
  body_canon : CanonMode
  signed_headers : Array[String]
  body_hash : String
  signature : String
  identity : String
  length_limit : Int?
  expiration : Int?
}

///|
fn is_ws_char(c : Char) -> Bool {
  c == ' ' || c == '\t' || c == '\r' || c == '\n'
}

///|
/// 去掉字符串里的全部空白,用于 base64 与哈希比较。
pub fn strip_ws(text : StringView) -> String {
  let out = StringBuilder()
  for c in text {
    if !is_ws_char(c) {
      out.write_char(c)
    }
  }
  out.to_string()
}

///|
/// 解析 `k=v; k=v` 形式的标签列表。
pub fn parse_tags(text : String) -> Array[(String, String)] {
  let out : Array[(String, String)] = []
  for raw_part in text.split(";") {
    let part = raw_part.to_owned()
    match part.split_once("=") {
      Some((k, v)) => out.push((ascii_lower(trim_wsp(k)), trim_wsp(v)))
      None => ()
    }
  }
  out
}

///|
fn tag_get(tags : Array[(String, String)], name : String) -> String? {
  for tag in tags {
    if tag.0 == name {
      return Some(tag.1)
    }
  }
  None
}

///|
/// 取字段冒号之后的值文本,并把折叠换行替换成空格。
fn header_value_text(field : HeaderField) -> String {
  let raw = field.raw
  let n = raw.length()
  let mut colon = 0
  while colon < n && raw[colon] != b':' {
    colon = colon + 1
  }
  let start = if colon < n { colon + 1 } else { n }
  let out : Array[Byte] = []
  for i in start.. DkimSignature? {
  let tags = parse_tags(header_value_text(field))
  let version = tag_get(tags, "v")
  let algorithm = tag_get(tags, "a")
  let domain = tag_get(tags, "d")
  let selector = tag_get(tags, "s")
  let signed = tag_get(tags, "h")
  let body_hash = tag_get(tags, "bh")
  let signature = tag_get(tags, "b")
  let (v, a, d, s, h, bh, b) = match
    (version, algorithm, domain, selector, signed, body_hash, signature) {
    (Some(v), Some(a), Some(d), Some(s), Some(h), Some(bh), Some(b)) =>
      (v, a, d, s, h, bh, b)
    _ => return None
  }
  if trim_wsp(v) != "1" {
    return None
  }
  let canon = tag_get(tags, "c")
  let (header_canon, body_canon) = parse_canon(canon)
  let signed_headers : Array[String] = []
  for name in h.split(":") {
    let trimmed = trim_wsp(name)
    if trimmed.length() > 0 {
      signed_headers.push(ascii_lower(trimmed))
    }
  }
  Some({
    domain: normalize_name(d),
    selector: normalize_name(s),
    algorithm: ascii_lower(a),
    header_canon,
    body_canon,
    signed_headers,
    body_hash: strip_ws(bh),
    signature: strip_ws(b),
    identity: tag_get(tags, "i").unwrap_or(""),
    length_limit: parse_int_tag(tag_get(tags, "l")),
    expiration: parse_int_tag(tag_get(tags, "x")),
  })
}

///|
fn parse_int_tag(value : String?) -> Int? {
  match value {
    None => None
    Some(text) => {
      let trimmed = trim_wsp(text)
      if trimmed.length() == 0 {
        return None
      }
      let mut result = 0
      for c in trimmed {
        if c < '0' || c > '9' {
          return None
        }
        result = result * 10 + (c.to_int() - '0'.to_int())
      }
      Some(result)
    }
  }
}

///|
fn parse_canon(value : String?) -> (CanonMode, CanonMode) {
  match value {
    None => (Simple, Simple)
    Some(text) => {
      let cleaned = strip_ws(text)
      let parts : Array[String] = []
      for part in cleaned.split("/") {
        parts.push(ascii_lower(part))
      }
      let header = if parts.length() > 0 && parts[0] == "relaxed" {
        Relaxed
      } else {
        Simple
      }
      let body = if parts.length() > 1 && parts[1] == "relaxed" {
        Relaxed
      } else {
        Simple
      }
      (header, body)
    }
  }
}

///|
/// 把签名头里 `b=` 的值删掉(保留标签名与等号),用于计算头部哈希。
fn strip_b_value(raw : Bytes) -> Bytes {
  let out : Array[Byte] = []
  let n = raw.length()
  let mut i = 0
  while i < n {
    let at_boundary = i == 0 ||
      raw[i - 1] == b';' ||
      raw[i - 1] == b' ' ||
      raw[i - 1] == b'\t' ||
      raw[i - 1] == b'\r' ||
      raw[i - 1] == b'\n'
    if at_boundary && (raw[i] == b'b' || raw[i] == b'B') {
      let mut j = i + 1
      while j < n && is_wsp_byte(raw[j]) {
        j = j + 1
      }
      if j < n && raw[j] == b'=' {
        let tag_end = j + 1
        for k in i.. Bytes {
  let out : Array[Byte] = []
  for part in parts {
    for b in part {
      out.push(b)
    }
  }
  Bytes::from_array(out)
}

///|
fn fail_result(
  signature : DkimSignature,
  status : AuthStatus,
  reason : String,
  evidence : Array[Evidence],
) -> DkimResult {
  {
    domain: signature.domain,
    selector: signature.selector,
    algorithm: signature.algorithm,
    status,
    reason,
    evidence,
  }
}

///|
/// 验证一封邮件里的某一条 DKIM-Signature。
pub fn[R : Resolver] verify_dkim(
  resolver : R,
  message : Message,
  signature_field : HeaderField,
  now? : Int,
) -> DkimResult {
  let parsed = parse_dkim_signature(signature_field)
  let signature = match parsed {
    Some(sig) => sig
    None =>
      return {
        domain: "",
        selector: "",
        algorithm: "",
        status: PermError,
        reason: "malformed DKIM-Signature: missing required tags",
        evidence: [],
      }
  }
  let evidence : Array[Evidence] = []
  if signature.algorithm != "rsa-sha256" {
    return fail_result(
      signature,
      PermError,
      "unsupported algorithm: \{signature.algorithm}",
      evidence,
    )
  }
  match signature.expiration {
    Some(x) =>
      match now {
        Some(current) =>
          if current > x {
            return fail_result(signature, Fail, "signature expired", evidence)
          }
        None => ()
      }
    None => ()
  }
  // From 必须被签名覆盖
  let mut signs_from = false
  for name in signature.signed_headers {
    if name == "from" {
      signs_from = true
    }
  }
  if !signs_from {
    evidence.push({
      kind: "dkim",
      label: "required-header",
      detail: "h= does not cover From",
    })
    return fail_result(signature, PermError, "h= does not cover From", evidence)
  }
  // 正文哈希
  let canonical_body = canonicalize_body(message.body, signature.body_canon)
  let mut signed_body = canonical_body
  match signature.length_limit {
    Some(limit) =>
      if limit >= 0 && limit < canonical_body.length() {
        let truncated : Array[Byte] = []
        for i in 0.. ()
  }
  let computed_bh = sha256(signed_body)
  let declared_bh = @base64.decode_lossy(
    signature.body_hash,
    ignore_whitespace=true,
  )
  let computed_bh_text = @base64.encode(computed_bh)
  evidence.push({
    kind: "dkim",
    label: "body-hash",
    detail: "canonical body \{signed_body.length()} bytes, bh=\{computed_bh_text}",
  })
  if !bytes_equal(computed_bh, declared_bh) {
    return fail_result(signature, Fail, "body hash mismatch", evidence)
  }
  // 头部选择:每条 h= 从下往上取尚未使用的最靠近底部的一条
  let headers = message.headers
  let used : Array[Bool] = Array::make(headers.length(), false)
  let pieces : Array[Bytes] = []
  for name in signature.signed_headers {
    let mut found = -1
    let mut i = headers.length() - 1
    while i >= 0 {
      if !used[i] && ascii_lower(headers[i].name) == name {
        found = i
        break
      }
      i = i - 1
    }
    if found < 0 {
      evidence.push({
        kind: "dkim",
        label: "signed-header",
        detail: "missing signed header: \{name}",
      })
      return fail_result(
        signature,
        Fail,
        "signed header missing: \{name}",
        evidence,
      )
    }
    used[found] = true
    pieces.push(canonicalize_header(headers[found], signature.header_canon))
  }
  let signature_without_b : HeaderField = {
    name: signature_field.name,
    raw: strip_b_value(signature_field.raw),
  }
  pieces.push(canonicalize_header(signature_without_b, signature.header_canon))
  let header_input = concat(pieces)
  evidence.push({
    kind: "dkim",
    label: "header-hash",
    detail: "signed headers: \{signature.signed_headers.length()}, input \{header_input.length()} bytes",
  })
  // 取公钥
  let key_name = "\{signature.selector}._domainkey.\{signature.domain}"
  let records = resolver.txt(key_name)
  if records.length() == 0 {
    return fail_result(
      signature,
      PermError,
      "no key record at \{key_name}",
      evidence,
    )
  }
  let key_tags = parse_tags(strip_txt_quotes(records[0]))
  let key_type = tag_get(key_tags, "k").unwrap_or("rsa")
  if ascii_lower(key_type) != "rsa" {
    return fail_result(
      signature,
      PermError,
      "unsupported key type: \{key_type}",
      evidence,
    )
  }
  let public_key = tag_get(key_tags, "p").unwrap_or("")
  if strip_ws(public_key).length() == 0 {
    return fail_result(
      signature,
      PermError,
      "public key revoked (p= empty)",
      evidence,
    )
  }
  match tag_get(key_tags, "v") {
    Some(v) =>
      if trim_wsp(v) != "DKIM1" {
        return fail_result(
          signature,
          PermError,
          "unsupported key record version",
          evidence,
        )
      }
    None => ()
  }
  let signature_bytes = @base64.decode_lossy(
    signature.signature,
    ignore_whitespace=true,
  )
  let decoded_key = @base64.decode_lossy(
    strip_ws(public_key),
    ignore_whitespace=true,
  )
  let key = match rsa_public_key_from_der(decoded_key) {
    Some(k) => k
    None => {
      evidence.push({
        kind: "dkim",
        label: "key",
        detail: "cannot parse public key DER at \{key_name}",
      })
      return fail_result(signature, PermError, "malformed public key", evidence)
    }
  }
  evidence.push({
    kind: "dkim",
    label: "key",
    detail: "\{key_name}: rsa \{key.byte_len * 8} bits",
  })
  if rsa_verify_sha256(key, header_input, signature_bytes) {
    fail_result(signature, Pass, "signature verified", evidence)
  } else {
    fail_result(signature, Fail, "signature mismatch", evidence)
  }
}

///|
/// 去掉 TXT 记录外层的引号并还原常见转义。
fn strip_txt_quotes(text : String) -> String {
  let trimmed = trim_wsp(text)
  if trimmed.length() >= 2 &&
    trimmed.has_prefix("\"") &&
    trimmed.has_suffix("\"") {
    let inner = trimmed[1:trimmed.length() - 1].to_owned()
    inner.replace_all(old="\\\"", new="\"").replace_all(old="\\\\", new="\\")
  } else {
    trimmed
  }
}

///|
/// 验证邮件里的所有 DKIM-Signature,按出现顺序返回。
pub fn[R : Resolver] verify_dkim_all(
  resolver : R,
  message : Message,
  now? : Int,
) -> Array[DkimResult] {
  let results : Array[DkimResult] = []
  for field in message.headers {
    if ascii_lower(field.name) == "dkim-signature" {
      results.push(verify_dkim(resolver, message, field, now?))
    }
  }
  results
}