///|
priv struct AuditCursor {
  resource : String
  token : Int
  revision : Int
  at : Int
}

///|
fn find_cursor(cursors : Array[AuditCursor], resource : String) -> Int {
  for index, cursor in cursors {
    if cursor.resource == resource {
      return index
    }
  }
  -1
}

///|
/// Checks monotonic token, revision, and logical-time invariants.
pub fn audit_events(events : Array[LeaseEvent]) -> AuditReport {
  let cursors : Array[AuditCursor] = []
  let findings : Array[AuditFinding] = []
  for index, event in events {
    if event.token <= 0 {
      findings.push({
        code: "invalid_token",
        resource: event.resource,
        event_index: index,
        message: "fencing token must be positive",
      })
    }
    if event.revision <= 0 {
      findings.push({
        code: "invalid_revision",
        resource: event.resource,
        event_index: index,
        message: "revision must be positive",
      })
    }
    let cursor_index = find_cursor(cursors, event.resource)
    if cursor_index < 0 {
      cursors.push({
        resource: event.resource,
        token: event.token,
        revision: event.revision,
        at: event.at,
      })
    } else {
      let cursor = cursors[cursor_index]
      if event.token < cursor.token {
        findings.push({
          code: "token_regression",
          resource: event.resource,
          event_index: index,
          message: "fencing token moved backwards",
        })
      }
      if event.revision <= cursor.revision {
        findings.push({
          code: "revision_regression",
          resource: event.resource,
          event_index: index,
          message: "revision did not increase",
        })
      }
      if event.at < cursor.at {
        findings.push({
          code: "time_regression",
          resource: event.resource,
          event_index: index,
          message: "logical event time moved backwards",
        })
      }
      cursors[cursor_index] = {
        resource: event.resource,
        token: if event.token > cursor.token {
          event.token
        } else {
          cursor.token
        },
        revision: if event.revision > cursor.revision {
          event.revision
        } else {
          cursor.revision
        },
        at: if event.at > cursor.at {
          event.at
        } else {
          cursor.at
        },
      }
    }
  }
  { valid: findings.length() == 0, checked_events: events.length(), findings }
}