// RFC 9116 semantic validation. The parser owns syntax; this module owns the
// RFC field constraints: per-field value formats, required fields, singleton cardinality.

///|
/// Validate a document; fails with the first error found.
pub fn validate(document : SecurityTxt) -> Result[Unit, SecurityTxtError] {
  let errors = validate_all(document)
  if errors.length() == 0 {
    Ok(())
  } else {
    Err(errors[0])
  }
}

///|
/// Collect every RFC violation in a document; empty when valid. Deterministic order.
pub fn validate_all(document : SecurityTxt) -> Array[SecurityTxtError] {
  let errors : Array[SecurityTxtError] = []
  let contact_lines : Array[Int] = []
  let expires_lines : Array[Int] = []
  let pl_lines : Array[Int] = []
  for entry in document.entries() {
    match entry.field {
      Contact(_) => contact_lines.push(entry.line)
      Expires(_) => expires_lines.push(entry.line)
      PreferredLanguages(_) => pl_lines.push(entry.line)
      _ => ()
    }
    match check_field_value(entry.field, entry.line, entry.byte_offset) {
      Ok(_) => ()
      Err(err) => errors.push(err)
    }
  }
  if contact_lines.is_empty() {
    errors.push(missing_error(MissingContact, "document has no Contact field"))
  }
  if expires_lines.is_empty() {
    errors.push(missing_error(MissingExpires, "document has no Expires field"))
  }
  if expires_lines.length() > 1 {
    errors.push(dup_error(DuplicateExpires, "Expires", expires_lines[0]))
  }
  if pl_lines.length() > 1 {
    errors.push(
      dup_error(DuplicatePreferredLanguages, "Preferred-Languages", pl_lines[0]),
    )
  }
  errors
}

///|
/// Build a required-field error with unknown position.
fn missing_error(
  kind : SecurityTxtErrorKind,
  message : String,
) -> SecurityTxtError {
  security_txt_error(Validation, kind, 0, 0, -1, message)
}

///|
/// Build a duplicate-field error pointing at the first occurrence.
fn dup_error(
  kind : SecurityTxtErrorKind,
  name : String,
  first_line : Int,
) -> SecurityTxtError {
  security_txt_error(
    Validation,
    kind,
    first_line,
    1,
    -1,
    "\{name} MUST NOT appear more than once (first occurrence at line \{first_line})",
  )
}