// RFC 9116 validator tests: required fields, cardinality, per-field value
// constraints and the difference between validate and validate_all.

// The RFC 9116 Appendix A.1 example, reproduced for testing.
// Known errata in the published RFC (noted in THIRD_PARTY_NOTICES.md):
// the `Expires` value uses a lower-case 'z' and the second `Contact` value
// lacks a scheme — this library accepts both leniently.

///|
fn rfc9116_a1_example() -> String {
  "# Our security acknowledgements page\nContact: mailto:security@example.com\nContact: https://example.com/security-contact\nEncryption: https://example.com/pgp-key.txt\nAcknowledgments: https://example.com/hall-of-fame\nExpires: 2021-12-31T18:37:07z\n"
}

///|
test "validator accepts the RFC 9116 A.1 example" {
  let document = unwrap_parse(rfc9116_a1_example())
  assert_true(validate(document) is Ok(_))
  assert_int_eq(validate_all(document).length(), 0)
}

///|
test "validator accepts complete documents and extension fields" {
  let document = unwrap_parse(valid_document())
  assert_true(validate(document) is Ok(_))
  let with_extensions = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nX-Vendor-Info: anything goes\n",
  )
  assert_true(validate(with_extensions) is Ok(_))
}

///|
test "validator requires both Contact and Expires" {
  let no_contact = unwrap_parse("Expires: 2027-01-01T00:00:00Z\n")
  match validate(no_contact) {
    Ok(_) => fail("expected MissingContact")
    Err(err) => assert_err_kind(err, MissingContact)
  }
  let no_expires = unwrap_parse("Contact: mailto:a@example.com\n")
  match validate(no_expires) {
    Ok(_) => fail("expected MissingExpires")
    Err(err) => assert_err_kind(err, MissingExpires)
  }
}

///|
test "validator rejects duplicate Expires and Preferred-Languages fields" {
  let dup_expires = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nExpires: 2028-01-01T00:00:00Z\n",
  )
  match validate(dup_expires) {
    Ok(_) => fail("expected DuplicateExpires")
    Err(err) => assert_err_kind(err, DuplicateExpires)
  }
  let dup_languages = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nPreferred-Languages: en\nPreferred-Languages: de\n",
  )
  match validate(dup_languages) {
    Ok(_) => fail("expected DuplicatePreferredLanguages")
    Err(err) => assert_err_kind(err, DuplicatePreferredLanguages)
  }
}

///|
test "validator accepts repeatable optional fields" {
  let document = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nCanonical: https://a.example.com/.well-known/security.txt\nCanonical: https://b.example.com/.well-known/security.txt\nEncryption: https://a.example.com/key\nEncryption: https://b.example.com/key\nPolicy: https://a.example.com/policy\nPolicy: https://b.example.com/policy\nHiring: https://a.example.com/jobs\nHiring: https://b.example.com/jobs\n",
  )
  assert_true(validate(document) is Ok(_))
  assert_int_eq(document.canonicals().length(), 2)
}

///|
test "validator accepts repeated Contact and Acknowledgments fields" {
  let document = unwrap_parse(
    "Contact: mailto:a@example.com\nContact: mailto:b@example.com\nAcknowledgments: https://example.com/thanks1\nAcknowledgments: https://example.com/thanks2\nExpires: 2027-01-01T00:00:00Z\n",
  )
  assert_true(validate(document) is Ok(_))
  assert_int_eq(document.contacts().length(), 2)
  assert_int_eq(document.acknowledgments().length(), 2)
}

///|
test "validator permits RFC Encryption URI schemes and rejects insecure web URIs" {
  let https_doc = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nEncryption: https://example.com/key.txt\n",
  )
  assert_true(validate(https_doc) is Ok(_))
  let fpr_doc = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nEncryption: openpgp4fpr:5f2de5521c63a801ab59ccb603d0de1b\n",
  )
  assert_true(validate(fpr_doc) is Ok(_))
  let dns_doc = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nEncryption: dns:key.example?type=OPENPGPKEY\n",
  )
  assert_true(validate(dns_doc) is Ok(_))
  let mailto_doc = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nEncryption: mailto:a@example.com\n",
  )
  assert_true(validate(mailto_doc) is Ok(_))
  let http_doc = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nEncryption: http://example.com/key\n",
  )
  match validate(http_doc) {
    Ok(_) => fail("expected InvalidScheme")
    Err(err) => assert_err_kind(err, InvalidScheme)
  }
}

///|
test "validator rejects invalid URIs per field" {
  for
    field_name in [
      "Contact", "Canonical", "Acknowledgments", "Policy", "Hiring",
    ] {
    let input = "\{field_name}: not a uri\nExpires: 2027-01-01T00:00:00Z\n"
    let document = unwrap_parse(input)
    match validate(document) {
      Ok(_) => fail("expected InvalidUri for \{field_name}")
      Err(err) => assert_err_kind(err, InvalidUri)
    }
  }
}

///|
test "validator rejects malformed Expires and language tag values" {
  let bad_expires = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: next Tuesday\n",
  )
  match validate(bad_expires) {
    Ok(_) => fail("expected InvalidDateTime")
    Err(err) => assert_err_kind(err, InvalidDateTime)
  }
  let bad_language = unwrap_parse(
    "Contact: mailto:a@example.com\nExpires: 2027-01-01T00:00:00Z\nPreferred-Languages: e\n",
  )
  match validate(bad_language) {
    Ok(_) => fail("expected InvalidLanguage")
    Err(err) => assert_err_kind(err, InvalidLanguage)
  }
}

///|
test "validate returns the first error while validate_all collects all" {
  let document = unwrap_parse(
    "Expires: nonsense\nExpires: 2027-01-01T00:00:00Z\nCanonical: not a uri\n",
  )
  // Per-field value errors (in document order), then MissingContact,
  // then DuplicateExpires.
  let all = validate_all(document)
  assert_true(all.length() >= 4)
  match validate(document) {
    Ok(_) => fail("expected the first error")
    Err(err) => assert_err_kind(err, InvalidDateTime)
  }
}