///|
/// A valid bundle feature that this verifier does not support yet.
pub enum Feature {
  /// The content is signed by a key that the bundle names by a hint, not by a
  /// certificate (managed keys).
  PublicKeyMaterial
  /// The only time source is an RFC 3161 timestamp, as in bundles from
  /// private repositories and GitHub immutable releases.
  TimestampAuthority
  /// A Rekor v2 (rekor-tiles) entry.
  RekorV2
  /// A Rekor entry kind and version other than `dsse/0.0.1` and
  /// `hashedrekord/0.0.1`.
  EntryKind(kind~ : String, version~ : String)
  /// A key type, for example `Ed25519`.
  KeyType(String)
  /// A message digest algorithm other than SHA-256.
  DigestAlgorithm(String)
} derive(Eq, Debug)

///|
pub extend Feature with Eq::{equal, not_equal}

///|
pub extend Feature with @debug.Debug::{to_repr}

///|
/// A check of the verification pipeline that failed, with the detail that a
/// user needs.
pub enum Failure {
  /// The bundle is well-formed JSON but has a shape that verification refuses.
  BundleShape(String)
  /// The transparency log entry is from an unknown log, or its signed entry
  /// timestamp does not verify.
  TransparencyLog(String)
  /// The integrated time is outside the log's validity, or later than "now".
  TransparencyLogTime(integrated_time~ : Instant, now~ : Instant)
  /// The log entry body does not match the bundle.
  TransparencyLogBody(String)
  /// The inclusion proof does not give the root hash.
  InclusionProof(String)
  /// The checkpoint does not verify or has another root hash.
  Checkpoint(String)
  /// The leaf certificate cannot be read or used.
  Certificate(CertificateProblem)
  /// The leaf certificate was not valid at the integrated time.
  CertificateValidity(not_before~ : Instant, not_after~ : Instant, time~ : Instant)
  /// No trusted certificate authority issued the leaf at the integrated time.
  CertificateChain(String)
  /// No embedded signed certificate timestamp verifies.
  CertificateTimestamp(String)
  /// The DSSE payload type is not `application/vnd.in-toto+json`.
  PayloadType(String)
  /// The content signature does not verify with the leaf key.
  ContentSignature
  /// The DSSE payload is not an in-toto statement that verification can read.
  Statement(String)
  /// No subject (or message digest) has the artifact digest.
  ArtifactDigestMismatch(ArtifactDigest)
  /// The certificate matches no policy; one reason for each policy.
  Policy(Array[PolicyMismatch])
} derive(Eq, Debug)

///|
pub extend Failure with Eq::{equal, not_equal}

///|
pub extend Failure with @debug.Debug::{to_repr}

///|
/// Why the leaf certificate cannot be used.
pub enum CertificateProblem {
  /// The certificate, its key or a Fulcio extension cannot be read; the text
  /// gives the rule.
  Unreadable(String)
  /// The leaf has no code signing extended key usage.
  NotForCodeSigning
  /// The leaf key usage does not allow digital signatures.
  NoDigitalSignature
  /// The leaf has a critical extension with this OID that the verifier does
  /// not understand.
  UnknownCriticalExtension(String)
  /// The leaf is a certificate authority.
  LeafIsAuthority
} derive(Eq, Debug)

///|
pub extend CertificateProblem with Eq::{equal, not_equal}

///|
pub extend CertificateProblem with @debug.Debug::{to_repr}

///|
/// Text for people that describes the problem.
pub fn CertificateProblem::message(self : CertificateProblem) -> String {
  match self {
    Unreadable(rule) => rule
    NotForCodeSigning => "the leaf is not for code signing"
    NoDigitalSignature => "the leaf key usage does not allow digital signatures"
    UnknownCriticalExtension(oid) => "the leaf has the unknown critical extension \{oid}"
    LeafIsAuthority => "the leaf is a certificate authority"
  }
}

///|
/// Why a bundle was not verified: a valid feature that is not supported, or
/// a check that failed.
pub enum Rejection {
  Unsupported(Feature)
  Invalid(Failure)
} derive(Eq, Debug)

///|
pub extend Rejection with Eq::{equal, not_equal}

///|
pub extend Rejection with @debug.Debug::{to_repr}

///|
/// Text for people that names the feature or the check, with its detail.
pub fn Rejection::message(self : Rejection) -> String {
  match self {
    Unsupported(feature) => {
      let name = match feature {
        PublicKeyMaterial => "public key (managed key) bundles"
        TimestampAuthority => "bundles whose only time source is an RFC 3161 timestamp"
        RekorV2 => "Rekor v2 log entries"
        EntryKind(kind~, version~) => "Rekor entries of kind \{kind} \{version}"
        KeyType(kind) => "\{kind} keys"
        DigestAlgorithm(name) => "message digests with \{name}"
      }
      "Not supported: \{name}"
    }
    Invalid(failure) => {
      let reason = match failure {
        BundleShape(text) => "bundle: \{text}"
        TransparencyLog(text) => "transparency log: \{text}"
        TransparencyLogTime(integrated_time~, now~) =>
          "transparency log time \{integrated_time} is outside the log validity or after now (\{now})"
        TransparencyLogBody(text) => "transparency log body: \{text}"
        InclusionProof(text) => "inclusion proof: \{text}"
        Checkpoint(text) => "checkpoint: \{text}"
        Certificate(problem) => "certificate: \{problem.message()}"
        CertificateValidity(not_before~, not_after~, time~) =>
          "the certificate is valid from \{not_before} to \{not_after}, not at \{time}"
        CertificateChain(text) => "certificate chain: \{text}"
        CertificateTimestamp(text) => "signed certificate timestamp: \{text}"
        PayloadType(text) => "the DSSE payload type is \{text.escape()}"
        ContentSignature => "the content signature does not verify"
        Statement(text) => "statement: \{text}"
        ArtifactDigestMismatch(digest) => "no subject has the digest \{digest}"
        Policy(reasons) =>
          "no policy matches: \{reasons.map(reason => reason.message()).join("; ")}"
      }
      "Invalid: \{reason}"
    }
  }
}

///|
/// One subject of an in-toto statement.
pub struct Subject {
  name : String
  /// The SHA-256 digest, when the subject has one.
  sha256 : ArtifactDigest?
} derive(Eq, Debug)

///|
pub extend Subject with Eq::{equal, not_equal}

///|
pub extend Subject with @debug.Debug::{to_repr}

///|
/// The in-toto statement of a verified DSSE bundle.
pub struct Statement {
  predicate_type : PredicateType
  subjects : Array[Subject]
  /// The exact payload bytes that the signature covers.
  payload : Bytes
} derive(Eq, Debug)

///|
pub extend Statement with Eq::{equal, not_equal}

///|
pub extend Statement with @debug.Debug::{to_repr}

///|
/// The facts of a verified bundle.
pub struct VerifiedBundle {
  /// The statement of a DSSE bundle; `None` for a message signature.
  statement : Statement?
  /// The index of the policy that matched: 0 for the first policy, then the
  /// alternatives in order.
  policy_index : Int
  claims : CertificateClaims
  /// The key ID of the transparency log.
  log_id : Bytes
  log_index : Int64
  integrated_time : Instant
} derive(Eq, Debug)

///|
pub extend VerifiedBundle with Eq::{equal, not_equal}

///|
pub extend VerifiedBundle with @debug.Debug::{to_repr}

///|
/// The result of a verification.
pub enum Verdict {
  Verified(VerifiedBundle)
  Rejected(Rejection)
} derive(Eq, Debug)

///|
pub extend Verdict with Eq::{equal, not_equal}

///|
pub extend Verdict with @debug.Debug::{to_repr}